-
Notifications
You must be signed in to change notification settings - Fork 253
fix(runtime): distinguish usage limits from auth errors #2521
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
me2seeks
wants to merge
15
commits into
apache:main
Choose a base branch
from
me2seeks:fix/2516-provider-usage-limit-errors
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
3ec92a1
fix(runtime): distinguish account limits from auth errors
me2seeks 9f9cb70
fix(desktop): preserve neutral provider failures
me2seeks df1ce75
fix(runtime): bound provider summaries and reuse the shared classifier
me2seeks 31d1b64
fix(runtime-host): preserve provider failure codes in turn snapshots
me2seeks 66ef30a
fix(desktop): render only bounded provider summaries verbatim
me2seeks 22a91ce
fix(runtime): unify provider failure authority
me2seeks f004e06
fix(runtime): preserve provider message provenance
me2seeks 40fe6e4
test(runtime): align provider failure assertions
me2seeks b8c74e1
fix(runtime): preserve structured cause semantics
me2seeks 7c1b509
fix(cli): localize runtime error notices
me2seeks bd0c100
fix(runtime): preserve structured provider failures
me2seeks 66dd41c
fix(runtime): prioritize structured context overflow
me2seeks 1607c65
fix(runtime): keep context overflow out of generic retry
me2seeks 57ab21c
fix(runtime-host): advance provider failure epoch
me2seeks 4021508
fix(runtime): tighten provider failure provenance and taxonomy inputs
me2seeks File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
170 changes: 170 additions & 0 deletions
170
apps/desktop/src/main/__tests__/provider-failure-presentation.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| import assert from 'node:assert/strict'; | ||
| import { describe, test } from 'node:test'; | ||
| import type { SessionEvent } from '@maka/core/events'; | ||
|
|
||
| import { sessionEventErrorMessage } from '../../renderer/model-connection-errors.js'; | ||
| import { describeSessionErrorReason } from '../../renderer/session-error-presentation.js'; | ||
| import { | ||
| deriveFailedTurnRecovery, | ||
| describeTurnErrorClass, | ||
| } from '../../renderer/session-status-presentation.js'; | ||
| import { commandPaletteConnectionTestFailureMessage } from '../../renderer/app-shell-copy.js'; | ||
| import { connectionTestFailureMessage } from '../../renderer/settings/provider-panel-shared.js'; | ||
|
|
||
| describe('provider failure presentation', () => { | ||
| test('keeps provider account and access failures distinct in both locales', () => { | ||
| assert.equal(describeSessionErrorReason('usage_limit'), '模型使用额度已用完'); | ||
| assert.equal(describeSessionErrorReason('provider_permission'), '模型服务拒绝访问'); | ||
| assert.equal(describeSessionErrorReason('usage_limit', 'en'), 'Model usage limit reached'); | ||
| assert.equal(describeSessionErrorReason('provider_permission', 'en'), 'Provider access denied'); | ||
| }); | ||
|
|
||
| test('does not present a bare 403 as an authentication failure', () => { | ||
| assert.equal(describeTurnErrorClass('403'), '未知错误'); | ||
| assert.deepEqual( | ||
| deriveFailedTurnRecovery({ | ||
| errorClass: 'usage_limit', | ||
| partialOutputRetained: false, | ||
| toolActivityCount: 0, | ||
| erroredToolCount: 0, | ||
| }), | ||
| { | ||
| action: 'check_account', | ||
| label: '检查模型服务的额度、套餐或恢复时间', | ||
| }, | ||
| ); | ||
| }); | ||
|
|
||
| test('does not present a provider permission code as a local permission wait', () => { | ||
| assert.equal(describeTurnErrorClass('permission_required'), '等待权限确认'); | ||
| assert.equal(describeTurnErrorClass('permission_error'), '未知错误'); | ||
| }); | ||
|
|
||
| test('preserves the bounded provider summary for a neutral Kimi plan-limit event', () => { | ||
| const message = | ||
| "You've reached your usage limit for this billing cycle. Your quota will be refreshed in the next cycle. " + | ||
| 'To continue now, purchase extra usage or upgrade your plan: https://www.kimi.com/code/#pricing ' + | ||
| '(code=permission_error, status=403)'; | ||
| const event: Extract<SessionEvent, { type: 'error' }> = { | ||
| type: 'error', | ||
| id: 'event-kimi-plan-limit', | ||
| turnId: 'turn-kimi-plan-limit', | ||
| ts: 1, | ||
| recoverable: false, | ||
| code: 'permission_error', | ||
| boundedProviderMessage: true, | ||
| message, | ||
| }; | ||
|
|
||
| assert.equal(sessionEventErrorMessage(event), message); | ||
| assert.equal(sessionEventErrorMessage(event, 'en'), message); | ||
| }); | ||
|
|
||
| test('preserves a bounded provider summary without a provider code', () => { | ||
| const event: Extract<SessionEvent, { type: 'error' }> = { | ||
| type: 'error', | ||
| id: 'event-provider-summary', | ||
| turnId: 'turn-provider-summary', | ||
| ts: 1, | ||
| recoverable: false, | ||
| boundedProviderMessage: true, | ||
| message: 'Provider request failed safely.', | ||
| }; | ||
|
|
||
| assert.equal(sessionEventErrorMessage(event), event.message); | ||
| assert.equal(sessionEventErrorMessage(event, 'en'), event.message); | ||
| }); | ||
|
|
||
| test('does not render a coded message verbatim without the bounded-provider marker', () => { | ||
| const event: Extract<SessionEvent, { type: 'error' }> = { | ||
| type: 'error', | ||
| id: 'event-ecodes', | ||
| turnId: 'turn-ecodes', | ||
| ts: 1, | ||
| recoverable: false, | ||
| code: 'ECONNRESET', | ||
| message: 'socket hang up at internal-connect.ts:42 (raw internal text)', | ||
| }; | ||
|
|
||
| assert.equal(sessionEventErrorMessage(event), '任务运行失败,请稍后重试。'); | ||
| assert.equal(sessionEventErrorMessage(event, 'en'), 'The task run failed. Try again later.'); | ||
| }); | ||
|
|
||
| test('uses generic copy when an error has neither a known reason nor provider evidence', () => { | ||
| const event: Extract<SessionEvent, { type: 'error' }> = { | ||
| type: 'error', | ||
| id: 'event-unknown', | ||
| turnId: 'turn-unknown', | ||
| ts: 1, | ||
| recoverable: false, | ||
| message: '403 permission denied', | ||
| }; | ||
|
|
||
| assert.equal(sessionEventErrorMessage(event), '任务运行失败,请稍后重试。'); | ||
| assert.equal(sessionEventErrorMessage(event, 'en'), 'The task run failed. Try again later.'); | ||
| }); | ||
|
|
||
| test('does not reclassify a neutral connection-test 403 as authentication', () => { | ||
| const result = { | ||
| ok: false, | ||
| statusCode: 403, | ||
| errorClass: 'unknown' as const, | ||
| errorMessage: '403 permission_error usage limit', | ||
| }; | ||
|
|
||
| assert.equal( | ||
| connectionTestFailureMessage(result, { | ||
| auth: 'AUTH SHOULD NOT WIN', | ||
| recheck: 'RECHECK', | ||
| }, 'en'), | ||
| 'RECHECK', | ||
| ); | ||
| assert.equal( | ||
| commandPaletteConnectionTestFailureMessage(result, 'en'), | ||
| 'The connection test failed. Try again later.', | ||
| ); | ||
| }); | ||
|
|
||
| test('renders only the Runtime-marked connection-test provider summary verbatim', () => { | ||
| const message = 'Plan allowance exhausted. (code=permission_error, status=403)'; | ||
| const result = { | ||
| ok: false, | ||
| statusCode: 403, | ||
| errorClass: 'unknown' as const, | ||
| providerFailure: { | ||
| errorClass: 'RequestRejected' as const, | ||
| httpStatus: 403, | ||
| providerCode: 'permission_error', | ||
| retryable: false, | ||
| message, | ||
| boundedProviderMessage: true as const, | ||
| }, | ||
| }; | ||
|
|
||
| assert.equal( | ||
| connectionTestFailureMessage(result, { auth: 'AUTH', recheck: 'RECHECK' }, 'en'), | ||
| message, | ||
| ); | ||
| assert.equal(commandPaletteConnectionTestFailureMessage(result, 'en'), message); | ||
| }); | ||
|
|
||
| test('preserves structured account meaning without provider message text', () => { | ||
| const result = { | ||
| ok: false, | ||
| statusCode: 429, | ||
| errorClass: 'provider_unavailable' as const, | ||
| providerFailure: { | ||
| errorClass: 'UsageLimit' as const, | ||
| httpStatus: 429, | ||
| providerCode: 'usage_limit_reached', | ||
| retryable: false, | ||
| }, | ||
| }; | ||
|
|
||
| assert.equal( | ||
| connectionTestFailureMessage(result, { auth: 'AUTH', recheck: 'RECHECK' }, 'en'), | ||
| 'Model usage limit reached', | ||
| ); | ||
| assert.equal(commandPaletteConnectionTestFailureMessage(result, 'en'), 'Model usage limit reached'); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.