-
Notifications
You must be signed in to change notification settings - Fork 7
fix(tui): 修复 /help 单页可选执行 + UTF-8 + 覆盖率修复 #208
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
Changes from all commits
560a716
05ad184
b574036
c31d7e9
6f9dd4c
0e4af34
55c08a4
8ca48f0
64fb3b1
6b5a253
29b9702
14ecb63
494499b
c7dc253
825f225
654adbe
8c1f663
b381864
45294f5
a4a2749
1f7eea9
8b44672
5736244
f947e2f
6c92d04
e9b490d
760fa46
90391e1
aa1e6d1
144e499
3fe66f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import ( | |
| "errors" | ||
| "fmt" | ||
| "net/http" | ||
| "strings" | ||
| ) | ||
|
|
||
| // 通用领域错误。 | ||
|
|
@@ -20,17 +21,41 @@ var ( | |
| type ProviderErrorCode string | ||
|
|
||
| const ( | ||
| ErrorCodeAuthFailed ProviderErrorCode = "auth_failed" // 认证失败(401) | ||
| ErrorCodeForbidden ProviderErrorCode = "forbidden" // 权限不足(403) | ||
| ErrorCodeNotFound ProviderErrorCode = "not_found" // 资源不存在(404) | ||
| ErrorCodeClient ProviderErrorCode = "client_error" // 客户端请求错误(4xx,排除上述分类) | ||
| ErrorCodeRateLimit ProviderErrorCode = "rate_limited" // 限流(429) | ||
| ErrorCodeServer ProviderErrorCode = "server_error" // 服务端错误(5xx) | ||
| ErrorCodeTimeout ProviderErrorCode = "timeout" // 超时 | ||
| ErrorCodeNetwork ProviderErrorCode = "network_error" // 网络错误(连接拒绝、DNS 失败等) | ||
| ErrorCodeUnknown ProviderErrorCode = "unknown" // 未知错误 | ||
| ErrorCodeAuthFailed ProviderErrorCode = "auth_failed" // 认证失败(401) | ||
| ErrorCodeForbidden ProviderErrorCode = "forbidden" // 权限不足(403) | ||
| ErrorCodeNotFound ProviderErrorCode = "not_found" // 资源不存在(404) | ||
| ErrorCodeClient ProviderErrorCode = "client_error" // 客户端请求错误(4xx,排除上述分类) | ||
| ErrorCodeRateLimit ProviderErrorCode = "rate_limited" // 限流(429) | ||
| ErrorCodeServer ProviderErrorCode = "server_error" // 服务端错误(5xx) | ||
| ErrorCodeTimeout ProviderErrorCode = "timeout" // 超时 | ||
| ErrorCodeNetwork ProviderErrorCode = "network_error" // 网络错误(连接拒绝、DNS 失败等) | ||
| ErrorCodeContextTooLong ProviderErrorCode = "context_too_long" // 上下文超出模型窗口 | ||
| ErrorCodeUnknown ProviderErrorCode = "unknown" // 未知错误 | ||
| ) | ||
|
|
||
| var contextTooLongFragments = []string{ | ||
| "context length", | ||
| "context_length_exceeded", | ||
| "context window", | ||
| "maximum context length", | ||
| "maximum prompt length", | ||
| "prompt is too long", | ||
| } | ||
|
|
||
| var contextTokenFragments = []string{ | ||
| "requested too many tokens", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The new |
||
| "too many tokens", | ||
| } | ||
|
|
||
| var contextTokenHints = []string{ | ||
| "context", | ||
| "prompt", | ||
| "message", | ||
| "input", | ||
| "history", | ||
| "window", | ||
| } | ||
|
|
||
| // ProviderError 是 provider 层的领域错误类型。 | ||
| type ProviderError struct { | ||
| StatusCode int // HTTP 状态码,0 表示非 HTTP 错误(如网络超时) | ||
|
|
@@ -78,6 +103,13 @@ func classifyStatus(statusCode int) ProviderErrorCode { | |
| // NewProviderErrorFromStatus 根据 HTTP 状态码和消息构造 ProviderError。 | ||
| func NewProviderErrorFromStatus(statusCode int, message string) *ProviderError { | ||
| code := classifyStatus(statusCode) | ||
| // Only elevate to context_too_long for generic client errors (e.g. 400/413). | ||
| // Do not override specific classifications such as rate_limited (429) even if | ||
| // the message contains token-count fragments, which would mis-route throttling | ||
| // errors into the reactive-compact path. | ||
| if code == ErrorCodeClient && matchesContextTooLong(message) { | ||
| code = ErrorCodeContextTooLong | ||
| } | ||
| return &ProviderError{ | ||
| StatusCode: statusCode, | ||
| Code: code, | ||
|
|
@@ -105,3 +137,54 @@ func NewTimeoutProviderError(message string) *ProviderError { | |
| Retryable: true, // 超时默认可重试 | ||
| } | ||
| } | ||
|
|
||
| // IsContextTooLong 判断 provider 错误是否表示请求上下文超出模型窗口。 | ||
| // 优先识别 typed error,必要时再回退到消息文本匹配,兼容不同厂商或额外包装层。 | ||
| // 已被归类为 rate_limited (429) 的错误不会因文本片段而被误判为 context_too_long。 | ||
| func IsContextTooLong(err error) bool { | ||
| if err == nil { | ||
| return false | ||
| } | ||
|
|
||
| var pErr *ProviderError | ||
| if errors.As(err, &pErr) { | ||
| if pErr.Code == ErrorCodeContextTooLong { | ||
| return true | ||
| } | ||
| // Skip text fallback for errors that are already classified as a specific | ||
| // non-context error (e.g. rate_limited). Token-count fragments in 429 | ||
| // messages must not route the runtime into the reactive-compact path. | ||
| if pErr.Code == ErrorCodeRateLimit { | ||
| return false | ||
| } | ||
| if matchesContextTooLong(pErr.Message) { | ||
| return true | ||
| } | ||
| } | ||
|
|
||
| return matchesContextTooLong(err.Error()) | ||
| } | ||
|
|
||
| // matchesContextTooLong 统一收敛常见“上下文过长”报错片段,减少 runtime 对厂商文案的感知。 | ||
| func matchesContextTooLong(message string) bool { | ||
| normalized := strings.ToLower(strings.TrimSpace(message)) | ||
| if normalized == "" { | ||
| return false | ||
| } | ||
| for _, fragment := range contextTooLongFragments { | ||
| if strings.Contains(normalized, fragment) { | ||
| return true | ||
| } | ||
| } | ||
| for _, fragment := range contextTokenFragments { | ||
| if !strings.Contains(normalized, fragment) { | ||
| continue | ||
| } | ||
| for _, hint := range contextTokenHints { | ||
| if strings.Contains(normalized, hint) { | ||
| return true | ||
| } | ||
| } | ||
| } | ||
| return false | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"requested too many tokens"/"too many tokens"are too broad forcontext_too_longdetection. Providers also use those phrases for bad-request cases like oversizedmax_tokens/ requested output, which compaction cannot fix. In those cases this PR will route the error into the new reactive-compact retry path instead of surfacing the real configuration problem.