重构:模块化拆分 + 重试去重 + P0/P1 行为修复#2
Open
seven7763 wants to merge 14 commits into
Open
Conversation
新增运营口径上报命中率系数(热可配 + 持久化 + Admin API):对带 cache_control 且达最小阈值的请求,把对外上报 cache_read 抬到完整前缀 × ratio(基数为未经 85% 封顶的完整前缀),creation 相应减少,三字段保持 互斥。真实模拟受 MAX_CACHE_RATIO=0.85 物理约束,运营口径上限 0.95;内部 hit/miss 统计仍基于真实命中,与对外口径分离。一次应用覆盖流式/非流式/ buffered 三路径。 实测修复真实性 bug:桶非空时低于 min_tokens 阈值的短 prompt 被运营系数 虚抬,真端点对低于 minimumTokensPerCacheCheckpoint 的请求报 cache=0; compute 在 full_prefix < min_tokens 时返回全零 usage,hit/first 路径统一门控。 本提交一并收入第三轮 cctest 对齐遗留改动(SSE 字段对齐 preserve_order、 token 双计修复、max_tokens 输出预算、原生 reasoningContentEvent 解析、 metering/contextUsage 探针等)。生产实测 152.53.242.77 上报命中率稳定 92.0%(流式/非流式/并发一致),全量 374 测试通过。 Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
将职责混杂的大文件按职责拆分为聚焦模块,行为字节级不变(全程 build + test 399 通过 + clippy 干净)。 anthropic 层(handlers.rs 2448 → 1181 行): - models.rs — /v1/models 列表数据(上游映射 + 静态回退) - error_map.rs — 上游错误 → Anthropic 错误协议 + Retry-After - cache_accounting.rs — per-request prompt cache 计费策略(决定/上报口径) - preprocess.rs — system prompt 注入剥离 + thinking 配置规范化 - saturating_to_i32 归入 token_count.rs - stream/ 子模块(context/sse_state/thinking/tokens/signature/buffered) 的单元测试随实现下沉,stream/mod.rs 回归纯 re-export kiro 层(token_manager/mod.rs 非测试逻辑 ~1000 → ~553 行): - failure_kind.rs — TransientFailureKind + extract_suspicious_directory_key 独立,解除 provider.rs 对 token_manager 内部错误分类的耦合 - 无状态刷新 HTTP 函数合并进 refresh.rs,与调度层同处 - converter/ 也按 content/history/model_map/session/tools 拆分 附:docs/kiro-proxy-reference-audit.md 记录与多个公开 kiro 代理项目的 对照审计(P0/P1 行为问题留作后续独立任务,本次仅做结构整理)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
行为不变(build + test 400 通过 + clippy 零警告):
- 删除死代码:
- token_manager/selection.rs::select_next_credential(已被
select_and_acquire_slot 取代,仅文档残留引用)
- 空壳模块 kiro/model/common(仅注释,无类型),移除 model/mod.rs 的声明
- sha256_hex 去重:machine_id.rs 与 token_manager/refresh.rs 两份实现
统一到 common/hash.rs;refresh.rs 保留同名薄封装以维持现有 re-export 路径
- cache_creation 明细 JSON 收敛:message_start / message_delta / 非流式 /
websearch 共 5 处手写的 {ephemeral_5m,1h} 结构统一走
cache_accounting::cache_creation_breakdown,消除字段漂移风险
- 修复 prompt_cache.rs 既有 clippy let_and_return 警告
注:token 估算三套口径(token_count / stream::tokens / 内联裸算)算法各异,
合并会改变客户端可见 token 数,属行为变更,本次结构整理不动。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
provider 此前 call_api_with_retry 与 call_mcp_with_retry 是两份近乎逐行重复
的 ~210 行重试循环,7 段错误分类各内联一份,易漂移且零测试覆盖。
- 抽纯函数 classify_failure(status, body, endpoint) -> FailureAction,
集中 402-monthly / 402-overage / 400 / 401-403 / 408-429-5xx / 其他4xx /
兜底 七类判别;新增 8 个单测覆盖(provider 重试逻辑首次有单测)
- 两条循环合并为单个 call_with_retry(kind: CallKind, ..),差异(acquire 过滤
参数、URL/body/装饰、成功后 conversation 绑定、错误前缀)由 CallKind 封装
- 三个公开入口 call_api / call_api_stream / call_mcp 改用统一引擎
行为不变:错误信息前缀逐字复刻("流式/非流式 API"、"MCP"),控制流/report_*/
sleep/bail/conversation 绑定与原循环一致;仅 MCP 路径新增与 API 对齐的若干
warn 日志(纯可观测,不影响行为)。build + test 408 通过 + clippy 零警告。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
补齐 baabb92 中已落地的后端能力对应的前端: - 凭据分组(credentialGroups):credential-card 可设置/清除分组,调用 POST /credentials/:id/group;新增 setCredentialGroup API + useSetCredentialGroup hook - 代理来源展示:proxySource(credential/group/global/none 等)在卡片标注有效代理出处 - suspicious_activity 冷却原因 + directoryKey 展示(health-banner / credential-card) - 感知缓存口径:prompt-cache-dialog 展示 perceivedCacheHitRatio / reportedHitRate1m / reportedSavedInputTokens5m - types/api.ts 同步后端 camelCase DTO 字段 - config.example.json 增加 credentialGroups 配置示例 前端 tsc -b 通过;后端路由/handler/DTO 已在 baabb92 提交,前后端字段对齐。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
scheduled_tasks.lock 是工具会话状态,误入库于 88c9bde。删除并将 .claude/ 加入 .gitignore,避免再次被跟踪。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
审计 P1:build_profile_from_request 的指纹此前只 hash 内容块,不含请求级 身份字段。后果:同一段 system+history 在 opus / sonnet 下共用同一指纹, 落到同一 account 缓存桶—— - cache_read 虚高(把不同模型的 prefix 误判为命中) - 可能复用错误模型留下的 conversation_id,污染 sticky 路由 修复:在内容块之前先把 prelude(model + tool_choice 规范化串)混入 hasher, 不计入断点 token/ttl,仅改变后续所有断点指纹值。 行为影响:指纹值变化,部署后首轮该 prefix 视为 cache miss(一次性), 随后恢复正常命中——这是预期且正确的。新增 3 个回归测试: - fingerprint_differs_by_model / by_tool_choice - no_cross_model_cache_hit(端到端:opus 写入后 sonnet 不命中、不复用 conv_id) test 411 通过 + clippy 零警告。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
审计 P0:permit 此前存在共享 CredentialEntry.concurrency_permit,两个请求选中 同一凭据时第二个会覆盖并 drop 掉第一个的 permit,导致 max_inflight_per_credential 在并发下被绕过;priority 模式的 current_id 快路径更是只 +inflight、完全不取 permit。 修复(全部修复方案): - permit 改为 per-request 所有权:select 时取得 OwnedSemaphorePermit 移交 CallContext, 请求结束 drop ctx 时自动归还 semaphore(移除 entry 上的 concurrency_permit 字段及 release_inflight/report_* 里的手动清理) - 快路径(current_id 命中)也 acquire-or-degrade 取 permit,堵住绕过口子 - live 候选池满则跳过选下一个真正有空位的号(skip-if-full),仅当所有 live 候选都满 才退化为不限并发借用调度键最优者;sticky / 全员 cooldown fallback 保持单号 acquire-or-degrade(不跨号跳过,维持路由意图) - 刷新失败路径显式 drop permit,避免单号池下一轮 select 误判满载 新增 2 个 tokio 测试: - permit_caps_concurrent_inflight_per_credential(max=2 时同时存活持 permit 上下文 恰为 2,释放后可再取——旧实现会是 3,绕过上限) - no_permit_when_limit_unset(未配置上限时 permit 为 None) test 413 通过 + clippy 零警告 + 0 build warning。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CI 的 cargo fmt --check 此前红:仓库存在历史格式漂移,叠加本 PR 新增/改动 文件未跑 fmt。统一 cargo fmt 一遍,纯格式、无逻辑变更(test 413 仍全过、 clippy 零警告)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
package.json 早前(8ccdc7d)新增 @radix-ui/react-tabs / recharts / tailwindcss-animate 但未同步 lockfile,CI 的 pnpm install --frozen-lockfile 因 ERR_PNPM_OUTDATED_LOCKFILE 失败,连带 clippy/test 拿不到 admin-ui/dist 嵌入产物而全红。 用 pnpm@9 install --lockfile-only 重新解析依赖树补齐 lockfile;本地 --frozen-lockfile 通过、pnpm build 产物正常。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cooldown_until 此前是进程内 Instant,重启即清空——大号池重启后会立刻重新 撞上同一批刚被风控的号,再学一遍 cooldown。 实现:StatsEntry 增加 cooldown_until_rfc3339 + cooldown_reason 两个 serde(default) 字段(向后兼容旧 stats.json): - save_stats:把 cooldown_until(Instant)换算成墙钟到期时间落盘 (剩余时长 = until - now_instant,到期墙钟 = now_wall + 剩余;已过期不落盘) - load_stats:启动时只恢复仍在未来的 cooldown(墙钟剩余换算回 Instant), 过期/解析失败的丢弃 - TransientFailureKind 加 Deserialize 以便 cooldown_reason 往返 落盘时机复用既有 save_stats_debounced + Drop flush,无新增 IO 路径。 新增 2 个测试:跨重启恢复未过期 cooldown、过期 cooldown 不恢复。 test 415 通过 + clippy 零警告 + fmt 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
问题:401/403 等失败走 report_failure,只累计 failure_count、不进 cooldown 也不改 current_id。priority 模式下 current_id 快路径会在同请求内反复重选 同一个刚 401 的号,直到它累计到阈值被禁用——白白浪费 2 次上游调用。 实现: - select_and_acquire_slot / acquire_context 新增 exclude 集合参数, acquire_context 保持原签名做薄封装(零改动 20+ 测试/调用点), 真实逻辑在 acquire_context_excluding - 软排除语义:排除后仍 ≥1 候选才生效,否则忽略(所有号都试过=自动重置, 不会因排除而饿死);快路径命中 exclude 也跳过走 select - call_with_retry 维护 per-request tried 集合,每次 acquire 后记入; force-refresh 成功重试同号时从 tried 移除(force-refresh 本意是换新 token 重试同号,不能被排除) 新增测试 acquire_excludes_tried_credentials:排除 #1→选 #2、排除 #2→选 #1、 全排除→软重置仍返回。test 416 通过 + clippy 零警告 + fmt 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
复审 P0 permit 修复(3945791)时发现两个让 max_inflight_per_credential 失效的 bug: 1. priority 快路径 degrade-on-full(本次修复引入):current_id 命中的热号 semaphore 满时,旧代码 permit=None 后仍硬用该号(.map 恒返回 Some)。priority 模式 current_id 长期不变,并发请求全走快路径全降级 → 上限对热号形同虚设。 改为 .and_then:满载时返回 None 落到 select_and_acquire_slot,由其 skip-if-full 切到真有空位的号,与 select 路径语义对齐。 2. semaphore 全局共享(pre-existing):new() 创建**单个** Arc<Semaphore> 克隆进 所有 entry;admin_ops 新增凭据时也复用已有 entry 的 semaphore。导致 max_inflight_per_credential 退化成所有凭据共享的**全局**上限。改为每个凭据 各自 Semaphore::new(n)。 新增测试 priority_fast_path_skips_full_credential(max=1 + 双号 priority: #1 满时第二个请求必须切到 #2 并取得其 permit,而非降级硬用 #1)。 该测试在修复前必失败(命中 #1)。test 417 通过 + clippy/fmt 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
anthropic 层此前在 AppState 字段 + 5 个 handler/websearch 形参里硬依赖具体 Arc<KiroProvider>,无法 mock、无法替换上游实现。 - kiro/provider.rs 新增 UpstreamProvider trait(call_api/call_api_stream/ call_mcp/list_upstream_models),dyn-兼容的装箱 future 形式(BoxFuture 别名), 不引入 async-trait 依赖;impl 转发到 KiroProvider 同名 inherent 方法 - AppState.kiro_provider: Arc<KiroProvider> → Arc<dyn UpstreamProvider> - handle_stream_request / handle_non_stream_request / handle_stream_request_buffered / handle_websearch_request / call_mcp_api 形参改为 dyn UpstreamProvider 构造侧(main → router → with_kiro_provider)保持具体 KiroProvider,在 with_kiro_provider 一处装箱;消费侧全部走 trait。行为不变:test 417 通过 + clippy/fmt 干净。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
概要
一轮结构性重构 + 两个高优先级行为修复,全程
build + test(413) + clippy(0)全绿,结构重构部分行为字节级不变。结构重构(行为不变)
handlers.rs2448→1181 行,抽出models/error_map/cache_accounting/preprocess;stream/测试归位;token_manager抽failure_kind.rs解除 provider 耦合,刷新函数归入refresh.rs;converter/按职责拆分call_api/call_mcp两条近乎逐行重复的 ~210 行循环合并为一个call_with_retry,错误分类抽纯函数classify_failure+ 8 个单测(重试逻辑首次有测试)select_next_credential、空壳model/common)、sha256_hex去重到common/hash.rs、cache_creationJSON 5 处收敛、修既有 clippy 警告行为修复(带回归测试)
CredentialEntry移入 per-requestCallContext,杜绝并发下max_inflight_per_credential被绕过;快路径也取 permit;满则跳过选下一个活号model/tool_choice,杜绝跨模型误命中与 conversation_id 错误复用前端
验证
cargo test413 通过,cargo clippy --all-targets零警告,cargo build零警告admin-uitsc -b通过范围外(留作后续)
🤖 Generated with Claude Code