@jsonstudio/rcc 0.89.1189 → 0.89.1348

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/README.md +17 -0
  2. package/configsamples/config.json +426 -0
  3. package/configsamples/config.reference.json +58 -0
  4. package/configsamples/provider/crs/config.v1.json +46 -0
  5. package/configsamples/provider/glm/config.v1.json +81 -0
  6. package/configsamples/provider/glm-anthropic/config.v1.json +45 -0
  7. package/configsamples/provider/iflow/config.v1.json +74 -0
  8. package/configsamples/provider/kimi/config.v1.json +41 -0
  9. package/configsamples/provider/lmstudio/config.v1.json +101 -0
  10. package/configsamples/provider/mimo/config.v1.json +35 -0
  11. package/configsamples/provider/modelscope/config.v1.json +96 -0
  12. package/configsamples/provider/qwen/config.v1.json +38 -0
  13. package/configsamples/provider/tab/config.v1.json +50 -0
  14. package/configsamples/provider/tabglm/config.v1.json +49 -0
  15. package/dist/build-info.js +2 -2
  16. package/dist/cli/commands/code.js +12 -6
  17. package/dist/cli/commands/code.js.map +1 -1
  18. package/dist/cli/commands/config.d.ts +2 -1
  19. package/dist/cli/commands/config.js +74 -103
  20. package/dist/cli/commands/config.js.map +1 -1
  21. package/dist/cli/commands/examples.js +6 -6
  22. package/dist/cli/commands/examples.js.map +1 -1
  23. package/dist/cli/commands/init.d.ts +28 -0
  24. package/dist/cli/commands/init.js +91 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/cli/commands/port.js +10 -2
  27. package/dist/cli/commands/port.js.map +1 -1
  28. package/dist/cli/commands/restart.js +5 -2
  29. package/dist/cli/commands/restart.js.map +1 -1
  30. package/dist/cli/commands/start.js +25 -22
  31. package/dist/cli/commands/start.js.map +1 -1
  32. package/dist/cli/commands/status.js +1 -0
  33. package/dist/cli/commands/status.js.map +1 -1
  34. package/dist/cli/commands/stop.js +1 -0
  35. package/dist/cli/commands/stop.js.map +1 -1
  36. package/dist/cli/config/bundled-docs.d.ts +20 -0
  37. package/dist/cli/config/bundled-docs.js +91 -0
  38. package/dist/cli/config/bundled-docs.js.map +1 -0
  39. package/dist/cli/config/init-config.d.ts +36 -0
  40. package/dist/cli/config/init-config.js +180 -0
  41. package/dist/cli/config/init-config.js.map +1 -0
  42. package/dist/cli/config/init-provider-catalog.d.ts +8 -0
  43. package/dist/cli/config/init-provider-catalog.js +187 -0
  44. package/dist/cli/config/init-provider-catalog.js.map +1 -0
  45. package/dist/cli/register/init-command.d.ts +3 -0
  46. package/dist/cli/register/init-command.js +5 -0
  47. package/dist/cli/register/init-command.js.map +1 -0
  48. package/dist/cli.js +28 -3
  49. package/dist/cli.js.map +1 -1
  50. package/dist/client/gemini-cli/gemini-cli-protocol-client.js +1 -1
  51. package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
  52. package/dist/config/risk-control-config.d.ts +94 -0
  53. package/dist/config/risk-control-config.js +196 -0
  54. package/dist/config/risk-control-config.js.map +1 -0
  55. package/dist/constants/index.d.ts +6 -0
  56. package/dist/constants/index.js +13 -0
  57. package/dist/constants/index.js.map +1 -1
  58. package/dist/docs/daemon-admin-ui.html +2113 -190
  59. package/dist/index.js +0 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/manager/modules/health/index.d.ts +1 -1
  62. package/dist/manager/modules/quota/antigravity-quota-manager.d.ts +70 -0
  63. package/dist/manager/modules/quota/antigravity-quota-manager.js +442 -0
  64. package/dist/manager/modules/quota/antigravity-quota-manager.js.map +1 -0
  65. package/dist/manager/modules/quota/index.d.ts +3 -127
  66. package/dist/manager/modules/quota/index.js +2 -1093
  67. package/dist/manager/modules/quota/index.js.map +1 -1
  68. package/dist/manager/modules/quota/provider-key-normalization.d.ts +3 -0
  69. package/dist/manager/modules/quota/provider-key-normalization.js +155 -0
  70. package/dist/manager/modules/quota/provider-key-normalization.js.map +1 -0
  71. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.d.ts +9 -0
  72. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js +115 -0
  73. package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js.map +1 -0
  74. package/dist/manager/modules/quota/provider-quota-daemon.d.ts +77 -0
  75. package/dist/manager/modules/quota/provider-quota-daemon.events.d.ts +12 -0
  76. package/dist/manager/modules/quota/provider-quota-daemon.events.js +237 -0
  77. package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -0
  78. package/dist/manager/modules/quota/provider-quota-daemon.js +404 -0
  79. package/dist/manager/modules/quota/provider-quota-daemon.js.map +1 -0
  80. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.d.ts +11 -0
  81. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +189 -0
  82. package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -0
  83. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.d.ts +8 -0
  84. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js +96 -0
  85. package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js.map +1 -0
  86. package/dist/manager/modules/quota/provider-quota-daemon.view.d.ts +19 -0
  87. package/dist/manager/modules/quota/provider-quota-daemon.view.js +37 -0
  88. package/dist/manager/modules/quota/provider-quota-daemon.view.js.map +1 -0
  89. package/dist/manager/modules/routing/index.d.ts +1 -0
  90. package/dist/manager/modules/routing/index.js +11 -25
  91. package/dist/manager/modules/routing/index.js.map +1 -1
  92. package/dist/manager/quota/provider-quota-center.d.ts +2 -0
  93. package/dist/manager/quota/provider-quota-center.js +80 -82
  94. package/dist/manager/quota/provider-quota-center.js.map +1 -1
  95. package/dist/modules/llmswitch/bridge.d.ts +16 -18
  96. package/dist/modules/llmswitch/bridge.js +314 -71
  97. package/dist/modules/llmswitch/bridge.js.map +1 -1
  98. package/dist/modules/llmswitch/core-loader.d.ts +4 -2
  99. package/dist/modules/llmswitch/core-loader.js +32 -20
  100. package/dist/modules/llmswitch/core-loader.js.map +1 -1
  101. package/dist/modules/pipeline/utils/colored-logger.js +3 -2
  102. package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
  103. package/dist/modules/pipeline/utils/debug-logger.js +1 -1
  104. package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
  105. package/dist/providers/auth/iflow-cookie-auth.js +0 -2
  106. package/dist/providers/auth/iflow-cookie-auth.js.map +1 -1
  107. package/dist/providers/auth/oauth-lifecycle.js +2 -23
  108. package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
  109. package/dist/providers/core/config/camoufox-launcher.js +35 -4
  110. package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
  111. package/dist/providers/core/runtime/antigravity-quota-client.js +6 -3
  112. package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -1
  113. package/dist/providers/core/runtime/base-provider.d.ts +2 -2
  114. package/dist/providers/core/runtime/base-provider.js +74 -69
  115. package/dist/providers/core/runtime/base-provider.js.map +1 -1
  116. package/dist/providers/core/runtime/gemini-cli-http-provider.js +6 -4
  117. package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
  118. package/dist/providers/core/runtime/http-request-executor.js +2 -2
  119. package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
  120. package/dist/providers/core/runtime/http-transport-provider.d.ts +14 -0
  121. package/dist/providers/core/runtime/http-transport-provider.js +111 -5
  122. package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
  123. package/dist/providers/core/runtime/provider-error-classifier.js +10 -0
  124. package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
  125. package/dist/providers/core/runtime/provider-factory.js +7 -5
  126. package/dist/providers/core/runtime/provider-factory.js.map +1 -1
  127. package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +6 -0
  128. package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
  129. package/dist/providers/core/runtime/responses-provider.d.ts +1 -7
  130. package/dist/providers/core/runtime/responses-provider.js +12 -93
  131. package/dist/providers/core/runtime/responses-provider.js.map +1 -1
  132. package/dist/providers/core/strategies/oauth-auth-code-flow.js +12 -8
  133. package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
  134. package/dist/providers/core/utils/http-client.js +16 -3
  135. package/dist/providers/core/utils/http-client.js.map +1 -1
  136. package/dist/providers/core/utils/provider-error-logger.d.ts +1 -1
  137. package/dist/providers/core/utils/provider-error-reporter.d.ts +3 -1
  138. package/dist/providers/core/utils/provider-error-reporter.js +3 -0
  139. package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
  140. package/dist/providers/core/utils/snapshot-writer.js +1 -4
  141. package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
  142. package/dist/providers/mock/mock-provider-runtime.js +57 -27
  143. package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
  144. package/dist/scripts/camoufox/launch-auth.mjs +193 -58
  145. package/dist/server/handlers/handler-utils.js +3 -2
  146. package/dist/server/handlers/handler-utils.js.map +1 -1
  147. package/dist/server/runtime/http-server/daemon-admin/auth-handler.d.ts +2 -0
  148. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +103 -0
  149. package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -0
  150. package/dist/server/runtime/http-server/daemon-admin/auth-session.d.ts +5 -0
  151. package/dist/server/runtime/http-server/daemon-admin/auth-session.js +77 -0
  152. package/dist/server/runtime/http-server/daemon-admin/auth-session.js.map +1 -0
  153. package/dist/server/runtime/http-server/daemon-admin/auth-store.d.ts +18 -0
  154. package/dist/server/runtime/http-server/daemon-admin/auth-store.js +89 -0
  155. package/dist/server/runtime/http-server/daemon-admin/auth-store.js.map +1 -0
  156. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +1 -2
  157. package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
  158. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +226 -24
  159. package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
  160. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +47 -8
  161. package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
  162. package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +1 -1
  163. package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -1
  164. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +1 -1
  165. package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
  166. package/dist/server/runtime/http-server/daemon-admin/status-handler.js +68 -4
  167. package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
  168. package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +3 -4
  169. package/dist/server/runtime/http-server/daemon-admin-routes.js +9 -14
  170. package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
  171. package/dist/server/runtime/http-server/executor-metadata.js +1 -1
  172. package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
  173. package/dist/server/runtime/http-server/executor-response.js +0 -16
  174. package/dist/server/runtime/http-server/executor-response.js.map +1 -1
  175. package/dist/server/runtime/http-server/hub-shadow-compare.d.ts +18 -0
  176. package/dist/server/runtime/http-server/hub-shadow-compare.js +256 -0
  177. package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -0
  178. package/dist/server/runtime/http-server/index.d.ts +7 -2
  179. package/dist/server/runtime/http-server/index.js +287 -49
  180. package/dist/server/runtime/http-server/index.js.map +1 -1
  181. package/dist/server/runtime/http-server/middleware.js +19 -1
  182. package/dist/server/runtime/http-server/middleware.js.map +1 -1
  183. package/dist/server/runtime/http-server/request-executor.js +10 -19
  184. package/dist/server/runtime/http-server/request-executor.js.map +1 -1
  185. package/dist/server/runtime/http-server/routes.js +8 -2
  186. package/dist/server/runtime/http-server/routes.js.map +1 -1
  187. package/dist/server/runtime/http-server/session-dir.d.ts +2 -0
  188. package/dist/server/runtime/http-server/session-dir.js +59 -0
  189. package/dist/server/runtime/http-server/session-dir.js.map +1 -0
  190. package/dist/server/runtime/http-server/types.d.ts +0 -4
  191. package/dist/server/utils/utf8-chunk-buffer.js +6 -3
  192. package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
  193. package/dist/server/utils/warmup-storm-tracker.js +1 -1
  194. package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
  195. package/dist/server-factory.d.ts +6 -28
  196. package/dist/server-factory.js +8 -93
  197. package/dist/server-factory.js.map +1 -1
  198. package/dist/token-daemon/index.js +2 -2
  199. package/dist/token-daemon/index.js.map +1 -1
  200. package/dist/token-daemon/provider-registry.js +0 -1
  201. package/dist/token-daemon/provider-registry.js.map +1 -1
  202. package/dist/token-daemon/server-utils.js +8 -9
  203. package/dist/token-daemon/server-utils.js.map +1 -1
  204. package/dist/token-daemon/token-utils.js +1 -1
  205. package/dist/token-daemon/token-utils.js.map +1 -1
  206. package/dist/tools/semantic-replay.js +2 -2
  207. package/dist/tools/semantic-replay.js.map +1 -1
  208. package/dist/tools/stats-request-events.d.ts +1 -1
  209. package/dist/tools/stats-usage.js +6 -3
  210. package/dist/tools/stats-usage.js.map +1 -1
  211. package/dist/utils/errorsamples.d.ts +5 -0
  212. package/dist/utils/errorsamples.js +27 -0
  213. package/dist/utils/errorsamples.js.map +1 -0
  214. package/dist/utils/llms-engine-shadow.d.ts +19 -0
  215. package/dist/utils/llms-engine-shadow.js +209 -0
  216. package/dist/utils/llms-engine-shadow.js.map +1 -0
  217. package/dist/utils/runtime-versions.d.ts +1 -0
  218. package/dist/utils/runtime-versions.js +39 -0
  219. package/dist/utils/runtime-versions.js.map +1 -0
  220. package/docs/ARCHITECTURE.md +402 -0
  221. package/docs/CODEX_AND_CLAUDE_CODE.md +69 -0
  222. package/docs/CONFIG_ARCHITECTURE.md +517 -0
  223. package/docs/ERROR_HANDLING_AUDIT.md +0 -0
  224. package/docs/GCLI2API_PARITY_GAPS.md +98 -0
  225. package/docs/INSTALLATION_AND_QUICKSTART.md +74 -0
  226. package/docs/INSTRUCTION_MARKUP.md +89 -0
  227. package/docs/MODULE_ENHANCEMENT_SYSTEM.md +666 -0
  228. package/docs/PORTS.md +36 -0
  229. package/docs/PROVIDERS_BUILTIN.md +111 -0
  230. package/docs/PROVIDER_TYPES.md +55 -0
  231. package/docs/SERVERTOOL_CLOCK_DESIGN.md +233 -0
  232. package/docs/USAGE_HANDLING_ANALYSIS.md +335 -0
  233. package/docs/USER_CONFIG_PARSER_CHANGES.md +175 -0
  234. package/docs/V3_INBOUND_OUTBOUND_DESIGN.md +86 -0
  235. package/docs/VIRTUAL_ROUTER_PRIORITY_AND_HEALTH.md +125 -0
  236. package/docs/anthropic-request-golden-samples.md +50 -0
  237. package/docs/ccr-alignment-enhancetool.md +105 -0
  238. package/docs/chat-glm-500-analysis.md +79 -0
  239. package/docs/chat-request-golden-samples.md +42 -0
  240. package/docs/chat-semantic-expansion-plan.md +82 -0
  241. package/docs/cli-command-inventory.md +76 -0
  242. package/docs/codex-samples-replay.md +50 -0
  243. package/docs/daemon-admin-api-design.md +350 -0
  244. package/docs/daemon-admin-module-structure.md +169 -0
  245. package/docs/daemon-admin-ui.html +3394 -0
  246. package/docs/debug-system-design.md +734 -0
  247. package/docs/debugging/gemini-sse-root-cause.md +52 -0
  248. package/docs/debugging/sse_encoding_failure_analysis.md +53 -0
  249. package/docs/dry-run/README.md +721 -0
  250. package/docs/error-handling-v2.md +92 -0
  251. package/docs/exec-command-guard-policy.example.v1.json +42 -0
  252. package/docs/fixes/gemini-protocol-mapping.md +57 -0
  253. package/docs/fixes/oauth-portal-timing-fix.md +202 -0
  254. package/docs/fixes/web-search-hop3-fix.md +265 -0
  255. package/docs/glm-api-reference.md +390 -0
  256. package/docs/glm-chat-completions.md +1779 -0
  257. package/docs/glm-history-inline-images.md +44 -0
  258. package/docs/golden-ci-library.md +66 -0
  259. package/docs/lmstudio-dry-run-summary.md +203 -0
  260. package/docs/lmstudio-tool-calling.md +214 -0
  261. package/docs/mapping-tables/anthropic-to-openai.json +290 -0
  262. package/docs/mapping-tables/iflow-to-openai.json +215 -0
  263. package/docs/mapping-tables/openai-passthrough.json +190 -0
  264. package/docs/mapping-tables/openai-to-iflow.json +227 -0
  265. package/docs/monitoring/Design.md +61 -0
  266. package/docs/multi-token-auth-guide.md +66 -0
  267. package/docs/oauth-authentication-guide.md +168 -0
  268. package/docs/oauth-iflow-implementation.md +153 -0
  269. package/docs/pipeline-routing-report.md +209 -0
  270. package/docs/plans/manager-daemon/PLAN.md +86 -0
  271. package/docs/plans/provider-config-v2-plan.md +176 -0
  272. package/docs/plans/provider-runtime-manager-plan.md +209 -0
  273. package/docs/plans/transparent-429-failover.md +89 -0
  274. package/docs/plans/unified-hub-framework-v1.md +245 -0
  275. package/docs/provider-config-v2-ui-design.md +181 -0
  276. package/docs/provider-quota-design.md +129 -0
  277. package/docs/providers/gemini-provider.md +62 -0
  278. package/docs/providers/lmstudio-v2-migration-report.md +102 -0
  279. package/docs/providers/provider-composite-design.md +142 -0
  280. package/docs/providers/provider-composite-testing.md +98 -0
  281. package/docs/providers/provider-type-only-migration.md +111 -0
  282. package/docs/rccx-wasm-migration.md +74 -0
  283. package/docs/refactoring/architecture-comparison-diagram.md +140 -0
  284. package/docs/refactoring/compatibility-v2-architecture-design.md +738 -0
  285. package/docs/refactoring/workflow-compatibility-refactoring-design.md +361 -0
  286. package/docs/reports/routing-classification-report.json +24 -0
  287. package/docs/reports/routing-classification-report.md +18 -0
  288. package/docs/reports/thinking-keywords-report.json +19 -0
  289. package/docs/responses/README.md +156 -0
  290. package/docs/responses-generic-provider.md +86 -0
  291. package/docs/responses-passthrough-provider-design.md +202 -0
  292. package/docs/routing-awrr-health-weighted-round-robin.md +179 -0
  293. package/docs/routing-instructions.md +393 -0
  294. package/docs/stop-message-auto.md +225 -0
  295. package/docs/streaming-flow.html +30 -0
  296. package/docs/streaming-flow.md +182 -0
  297. package/docs/token-daemon-preview.html +490 -0
  298. package/docs/token-refresh-daemon-plan.md +269 -0
  299. package/docs/transformation-tables/Gemini-FinishReason/345/256/214/346/225/264/350/275/254/346/215/242/350/241/250.json +233 -0
  300. package/docs/transformation-tables/README.md +225 -0
  301. package/docs/transformation-tables/claude-code-router-anthropic-to-gemini.json +283 -0
  302. package/docs/transformation-tables/claude-code-router-anthropic-to-openai.json +208 -0
  303. package/docs/transformation-tables/claude-code-router-openai-to-anthropic.json +261 -0
  304. package/docs/transformation-tables/claude-code-router-openai-to-gemini.json +208 -0
  305. package/docs/transformation-tables/claude-code-router-openai-to-lmstudio.json +182 -0
  306. package/docs/transformation-tables/claude-code-router-openai-to-ollama.json +250 -0
  307. package/docs/transformation-tables/claude-code-router-openai-to-textgenwebui.json +295 -0
  308. package/docs/transformation-tables/claude-code-router-provider-conversions.json +193 -0
  309. package/docs/transformation-tables//345/256/214/346/225/264/347/232/204/345/267/245/345/205/267/346/211/247/350/241/214/346/265/201/347/250/213/350/275/254/346/215/242/350/241/250.json +299 -0
  310. package/docs/transformation-tables//345/257/271/350/257/235/345/216/206/345/217/262/347/273/264/346/212/244/345/210/206/346/236/220.md +134 -0
  311. package/docs/transformation-tables//345/267/245/345/205/267/350/260/203/347/224/250/346/250/241/345/274/217/345/210/206/346/236/220.md +158 -0
  312. package/docs/transformation-tables//347/212/266/346/200/201/347/256/241/347/220/206/351/234/200/346/261/202/345/210/206/346/236/220.md +175 -0
  313. package/docs/transformation-tables//351/235/231/346/200/201/350/241/250vs/345/212/250/346/200/201/345/210/206/346/236/220.md +189 -0
  314. package/docs/transformation-tables//351/235/231/346/200/201/350/241/250/345/207/206/347/241/256/346/200/247/350/257/204/344/274/260.md +179 -0
  315. package/docs/transformation-tables//351/235/236/346/265/201/345/274/217/345/234/272/346/231/257/345/210/206/346/236/220.md +189 -0
  316. package/docs/v2-architecture/IMPLEMENTATION-ROADMAP.md +367 -0
  317. package/docs/v2-architecture/OPTIMIZED-DESIGN.md +827 -0
  318. package/docs/v2-architecture/PRERUN-CONNECTION-DESIGN.md +716 -0
  319. package/docs/v2-architecture/README.md +551 -0
  320. package/docs/verification/modelscope-verify.md +59 -0
  321. package/docs/web-search-service-design.md +322 -0
  322. package/package.json +12 -7
  323. package/scripts/camoufox/launch-auth.mjs +193 -58
  324. package/scripts/monitor-diff.mjs +126 -0
  325. package/scripts/pack-mode.mjs +19 -1
  326. package/scripts/pack-rcc.mjs +63 -0
  327. package/scripts/unified-hub-shadow-compare.mjs +33 -13
  328. package/scripts/verify-e2e-toolcall.mjs +115 -26
  329. package/dist/modules/llmswitch/pipeline-registry.d.ts +0 -57
  330. package/dist/modules/llmswitch/pipeline-registry.js +0 -229
  331. package/dist/modules/llmswitch/pipeline-registry.js.map +0 -1
  332. package/dist/server/RouteCodexServer.d.ts +0 -13
  333. package/dist/server/RouteCodexServer.js +0 -25
  334. package/dist/server/RouteCodexServer.js.map +0 -1
  335. package/dist/v2/conversion/hub/snapshot-recorder.d.ts +0 -12
  336. package/dist/v2/conversion/hub/snapshot-recorder.js +0 -22
  337. package/dist/v2/conversion/hub/snapshot-recorder.js.map +0 -1
@@ -0,0 +1,92 @@
1
+ # RouteCodex 错误中心机制(V2)
2
+
3
+ 本页记录当前 RouteCodex V2 的统一错误处理与熔断策略,覆盖 HTTP Server、Hub Pipeline、Provider 以及 Virtual Router 健康管理。所有细节以此为准,后续规则变动需同步更新此文档并在 `AGENTS.md` 追加引用。
4
+
5
+ ## 1. 统一流程
6
+
7
+ 1. **HTTP Server / CLI / Pipeline** 捕获异常后调用 `reportRouteError(payload)`,由 `RouteErrorHub` 统筹。
8
+ 2. `RouteErrorHub` 负责:
9
+ - 归一化错误元数据(requestId、endpoint、providerKey、model 等)。
10
+ - 根据构建模式自动裁剪堆栈:`release` 构建默认移除 stack(可通过 `ROUTECODEX_RELEASE_ERROR_VERBOSE=1` 恢复详细日志)。
11
+ - 将错误交给 `ErrorHandlerRegistry`,触发挂载的处理 Hook(含 429 回退调度、快照写入等)。
12
+ - 可选返回 HTTP 映射结果,确保客户端仅收到统一格式的错误体。
13
+ 3. Provider 侧在 `emitProviderError` 同时上报 `providerErrorCenter`(供 Virtual Router 熔断)与 `ErrorHandlingCenter`。
14
+ 4. `llmswitch-core` 的 Virtual Router 根据 `ProviderErrorEvent` 执行健康状态变更(回退、降级、拉黑)。
15
+
16
+ ## 2. 错误策略矩阵
17
+
18
+ | 错误来源 | 状态 / 错误码 | Error Center 处理 | Virtual Router / ProviderHealth 策略 | 说明 |
19
+ | --- | --- | --- | --- | --- |
20
+ | Provider 客户端错误 | 4xx(排除 429) | 记录并透传,`affectsHealth=false` | 不触发健康计数 | 用户参数错误,可重试但不熔断 |
21
+ | Provider 429 限流 | HTTP 429 / `retryable=true` | `rate_limit_error` Hook 启动回退:10s → 30s → 60s 共三次 | BaseProvider 内置 RateLimitTracker:**同一 providerKey(通常为 `provider.key` 或 `provider.key::model` 维度)连续 4 次 429** 会以 `affectsHealth=true` 向 Virtual Router 上报,触发熔断;任意一次成功即清零;不区分 route/routePool | 回退期间可切换同模型 pipeline,必要时返回 429 给客户端 |
22
+ | Provider 5xx / 不可恢复 | HTTP ≥ 500、`affectsHealth=true` | 立即触发 `emitProviderError`,带 `fatal=true` | `tripProvider`,按 `fatalCooldownMs` 冷却 | 兼容层错误(stage=compat)同样视为 fatal |
23
+ | Host/Server 内部错误 | pipeline/router 抛出的 500 | `RouteErrorHub` 归档并映射 HTTP 500;原始错误号写入 `code` 字段 | 同步 `providerErrorCenter`(若具备 provider 上下文) | 保证 release 输出简单错误号,dev 模式保留堆栈 |
24
+ | CLI/工具链错误 | CLI command / debug harness | `reportCliError`(同 `RouteErrorHub`) | 仅记录,不影响路由池 | CLI 运行期错误不触发 provider 熔断 |
25
+
26
+ > ⚠️ RateLimitTracker 只针对**相同 providerKey(provider.key / provider.key::model)**的连续 429 生效,中间出现成功或其他错误即会自动清零;冷却结束后会再次尝试,具体 TTL 由 `virtualrouter.health` 配置决定,且与使用的 route/routePool 无关。
27
+
28
+ ## 3. 日志与可观测性
29
+
30
+ - **Release 输出最小化**:`error-center-payload` 会在 release 构建中移除 `stack`、`details.stack`,仅保留 message/code/requestId 等必要字段。若需排查,可在运行时设置 `ROUTECODEX_RELEASE_ERROR_VERBOSE=1`。
31
+ - **OAuth 噪音削减**:所有 `[OAuth] ...` 信息级日志默认通过 `ROUTECODEX_OAUTH_DEBUG=1` 才会打印,错误(`console.error`) 仍保持输出。
32
+ - **SSE 预览日志禁用**:Server 不再将 SSE chunk 内容写入 `stage-logger`,仅保留流开始/结束事件与统计,避免泄露响应片段。
33
+
34
+ ## 4. 回调挂载点
35
+
36
+ - `ErrorHandlerRegistry` 默认挂载以下 Hook,可按需扩展:
37
+ - `rate_limit_error`:提供回退调度(切换 pipeline 或延迟重放)。
38
+ - `provider_error`:可注入通知/报警逻辑。
39
+ - 自定义 Hook 通过 `ErrorHandlerRegistry.registerErrorHandler` 挂载,RouteErrorHub 会自动转发。
40
+
41
+ 如需新增策略(例如特定 provider 的 4xx 也触发冷却),建议在 `docs/error-handling-v2.md` 补充矩阵,并在 `virtualrouter.health` 配置中增加自定义参数。
42
+
43
+ ## 5. 工具协议错误(ProviderProtocolError)
44
+
45
+ ServerTool 与工具调用相关的后端错误统一通过 `ProviderProtocolError` 表达,类型定义位于
46
+ `sharedmodule/llmswitch-core/src/conversion/shared/errors.ts`。
47
+
48
+ - `ProviderProtocolErrorCode` 目前包含:
49
+ - `TOOL_PROTOCOL_ERROR`:工具调用/结果与 upstream 协议不一致,例如:
50
+ - provider 返回 `finishReason=UNEXPECTED_TOOL_CALL` 但响应中并无有效 tool calls;
51
+ - 工具结果缺少与请求中 tool call 对应的 `tool_outputs`。
52
+ - `SSE_DECODE_ERROR`:SSE → JSON 解码阶段发现非法事件序列或结构。
53
+ - `MALFORMED_RESPONSE`:provider 返回的响应在语义或结构上与声明的协议不符。
54
+ - `MALFORMED_REQUEST`:我们构造的请求违反了目标协议的约束(通常视为开发错误,需要修复代码)。
55
+
56
+ 统一约定:
57
+
58
+ - **工具相关错误不得静默吞掉**:一旦判定为工具/协议层错误,必须:
59
+ - 在 provider 侧通过 `emitProviderError({ ..., error })` 上报 `providerErrorCenter`;
60
+ - 让错误沿着 `RouteErrorHub → ErrorHandlerRegistry` 流转,最终映射为 HTTP 错误返回客户端;
61
+ - 禁止“返回 200 + 空回答/占位内容”来掩盖错误。
62
+ - 在 conversion/compat/codec 层,遇到协议违约时应抛出 `new ProviderProtocolError(message, { code, protocol, providerType, details })`,
63
+ 由上游统一捕获与分类,而不是直接 `throw new Error(...)`。
64
+
65
+ 文档规则:若新增一种协议级错误(例如新的 SSE 解析场景或特定 provider 的工具约束),需要同时:
66
+
67
+ 1. 在 `errors.ts` 中补充新的 `ProviderProtocolErrorCode`(或复用已有 code);
68
+ 2. 在此文档补充该错误 code 的含义与触发条件;
69
+ 3. 确保 provider runtime 捕获到该错误时仍通过 `emitProviderError` 上报,并映射为明确的 HTTP 错误响应。
70
+
71
+ ### 5.1 粗粒度错误类别(EXTERNAL / TOOL / INTERNAL)
72
+
73
+ 为方便统计与路由策略,`ProviderProtocolError` 还暴露一个粗粒度错误类别 `category`:
74
+
75
+ - `EXTERNAL_ERROR`:外部载荷/协议错误
76
+ - 典型来源:SSE 解析失败(`SSE_DECODE_ERROR`)、上游返回的响应结构不合法(`MALFORMED_RESPONSE`)、
77
+ 请求体与协议约束不符(`MALFORMED_REQUEST` 且原因在 provider / 用户输入侧)。
78
+ - `TOOL_ERROR`:工具/ServerTool 协议错误
79
+ - 典型来源:工具调用/结果与 contract 不匹配(`TOOL_PROTOCOL_ERROR`)、工具 result 缺失对应 tool_call 等。
80
+ - `INTERNAL_ERROR`:内部实现/配置错误
81
+ - 不直接用 `ProviderProtocolErrorCode` 区分,通常由 Host / Hub 在捕获异常时显式设置,用于标记
82
+ 我们自己的实现/配置 bug(例如错误拼装 ChatEnvelope、必填字段缺失等)。
83
+
84
+ 约定:
85
+
86
+ - `ProviderProtocolError.category` 默认会根据 `code` 自动推导:
87
+ - `TOOL_PROTOCOL_ERROR` → `TOOL_ERROR`;
88
+ - 其它(目前为 `SSE_DECODE_ERROR` / `MALFORMED_RESPONSE` / `MALFORMED_REQUEST`)→ 默认 `EXTERNAL_ERROR`。
89
+ - 当上层明确知道错误是“内部实现错误”时,可以在构造时显式传入 `category: 'INTERNAL_ERROR'` 覆盖默认推导。
90
+ - Host 在将错误映射为 `ProviderErrorEvent` 时,可将 `category` 映射到统一的 `event.code`(例如
91
+ `TOOL_ERROR` / `EXTERNAL_ERROR` / `INTERNAL_ERROR`),而把细粒度 `ProviderProtocolErrorCode` 放入
92
+ `event.details.reason`,从而同时兼顾统计与排障信息。
@@ -0,0 +1,42 @@
1
+ {
2
+ "version": 1,
3
+ "allowedProjectRoots": [
4
+ "~/Documents/github"
5
+ ],
6
+ "defaults": {
7
+ "denyOutsideProjectDestructive": true,
8
+ "gitSingleFileOnly": true,
9
+ "denyMassKill": true
10
+ },
11
+ "rules": [
12
+ {
13
+ "id": "deny-git-reset",
14
+ "type": "regex",
15
+ "pattern": "\\bgit\\s+reset\\b",
16
+ "flags": "i",
17
+ "reason": "git reset is not allowed"
18
+ },
19
+ {
20
+ "id": "deny-rm-rf-anywhere",
21
+ "type": "regex",
22
+ "pattern": "\\brm\\b[^\\n]*\\s(-rf|-fr)\\b",
23
+ "flags": "i",
24
+ "reason": "rm -rf is not allowed"
25
+ },
26
+ {
27
+ "id": "deny-find-delete-anywhere",
28
+ "type": "regex",
29
+ "pattern": "\\bfind\\b[^\\n]*\\s-delete(\\s|$)",
30
+ "flags": "i",
31
+ "reason": "find -delete is not allowed"
32
+ },
33
+ {
34
+ "id": "deny-loop-or-batch-delete",
35
+ "type": "regex",
36
+ "pattern": "\\b(xargs\\s+rm\\b|for\\s+[^;\\n]+;\\s*do\\s+rm\\b|while\\s+[^;\\n]+;\\s*do\\s+rm\\b)",
37
+ "flags": "i",
38
+ "reason": "Loop/batch deletion is not allowed"
39
+ }
40
+ ]
41
+ }
42
+
@@ -0,0 +1,57 @@
1
+ # Gemini CLI Protocol Mapping Implementation
2
+
3
+ ## Overview
4
+ Implemented proper protocol mapping for Gemini CLI responses to OpenAI-compatible format, ensuring tool calls, thinking/reasoning, and other metadata are preserved and correctly transformed.
5
+
6
+ ## Problem
7
+ Previously, `GeminiSseNormalizer` would silently drop any SSE events that didn't match the expected format:
8
+ - Tool calls (`functionCall`, `functionResponse`) were discarded
9
+ - Thinking/reasoning content was lost
10
+ - Code execution events were ignored
11
+ - Stream would terminate prematurely (showing only 8 events instead of complete response)
12
+
13
+ ## Solution
14
+ Rewrote `emitCandidateParts` method in `src/providers/core/runtime/gemini-cli-http-provider.ts` to map all Gemini part types to OpenAI/Chat-compatible semantics:
15
+
16
+ ### Mapping Rules
17
+
18
+ | Gemini Part Type | Target Format | Description |
19
+ |-----------------|---------------|-------------|
20
+ | `text` | `type: 'text', content: string` | Regular text content → delta text |
21
+ | `functionCall` | `type: 'tool_call', tool_call: {...}` | Tool invocation → OpenAI `tool_calls` |
22
+ | `functionResponse` | `type: 'tool_response', tool_response: {...}` | Tool result → tool message |
23
+ | `thought` | `type: 'reasoning', reasoning_content: string` | Thinking process → reasoning content (o1-style) |
24
+ | `executableCode` | `type: 'code', code_content: {...}` | Code to execute → code_interpreter |
25
+ | `codeExecutionResult` | `type: 'code_result', code_result: {...}` | Execution output → code output |
26
+ | Unknown types | `type: 'metadata', metadata: {...}` | Preserve in metadata for outbound processing |
27
+
28
+ ### Implementation Details
29
+
30
+ 1. **Tool Calls**: Maps `functionCall` to OpenAI `tool_calls` format with generated call IDs
31
+ 2. **Reasoning**: Maps `thought` to `reasoning_content` for models with thinking capability
32
+ 3. **Code Execution**: Preserves code and execution results for code_interpreter tools
33
+ 4. **Metadata Preservation**: Unknown part types are wrapped in metadata for downstream processing
34
+
35
+ ### Outbound Processing
36
+ The response outbound layer (in `llmswitch-core`) will:
37
+ - Transform these typed events to target protocol format (OpenAI/Anthropic/etc)
38
+ - Drop metadata fields not supported by target protocol
39
+ - Preserve chat-compatible fields in final response
40
+
41
+ ## Benefits
42
+ - ✅ Complete streaming responses (no premature termination)
43
+ - ✅ Tool calls work correctly with Gemini providers
44
+ - ✅ Thinking/reasoning preserved for Advanced Reasoning models
45
+ - ✅ Extensible to future Gemini part types
46
+ - ✅ No data loss - all events properly mapped or logged
47
+
48
+ ## Testing
49
+ After restarting the server with v0.89.371+:
50
+ - Tool call requests should show complete responses
51
+ - Thinking models should stream reasoning content
52
+ - No more "8 events then stop" issues
53
+ - Logs will show any unmapped part types (for debugging)
54
+
55
+ ## Version
56
+ - Fixed in: v0.89.371
57
+ - Files modified: `src/providers/core/runtime/gemini-cli-http-provider.ts`
@@ -0,0 +1,202 @@
1
+ # OAuth Portal 启动时序问题修复
2
+
3
+ ## 问题描述
4
+
5
+ 在项目启动时,当需要检查 token 有效性并打开浏览器进行 OAuth 认证时,浏览器无法访问 Portal 页面 (`/token-auth/demo`),导致 404 错误。
6
+
7
+ ### 根本原因
8
+
9
+ **时序问题**: OAuth 认证流程在服务器 HTTP 路由注册之前就已经开始尝试打开浏览器。
10
+
11
+ 具体流程:
12
+ 1. Provider 初始化阶段(在路由注册之前)
13
+ - `cli.ts` → `index.js` → 服务器初始化
14
+ - Provider 初始化时检查 token 有效性
15
+ - `ensureValidOAuthToken()` 被调用
16
+ - 此时尝试打开浏览器访问 Portal URL
17
+
18
+ 2. HTTP 路由注册阶段(在所有 provider 初始化完成之后)
19
+ - `RouteCodexHttpServer.initialize()` 中调用 `registerHttpRoutes()`
20
+ - 这时 `/token-auth/demo` 路由才被注册
21
+
22
+ 3. **问题**: 浏览器打开的时候,`/token-auth/demo` 路由还不存在!
23
+
24
+ ## 解决方案
25
+
26
+ 采用 **方案 1 + 方案 2 组合**:
27
+
28
+ ### 方案 1: 提前注册 OAuth Portal 路由
29
+
30
+ 将 `/token-auth/demo` 路由从 `registerHttpRoutes()` 中分离出来,并在服务器构造函数中提前注册。
31
+
32
+ **修改的文件**:
33
+ - `src/server/runtime/http-server/routes.ts`
34
+ - 新增 `registerOAuthPortalRoute()` 函数
35
+ - 从 `registerHttpRoutes()` 中移除 Portal 路由注册
36
+
37
+ - `src/server/runtime/http-server/index.ts`
38
+ - 在构造函数中调用 `registerDefaultMiddleware()` 和 `registerOAuthPortalRoute()`
39
+ - 确保在任何 provider 初始化之前,这些关键路由已经可用
40
+
41
+ ### 方案 2: 添加智能等待机制
42
+
43
+ 在 OAuth 流程中,当使用 Portal URL 时,先检查服务器是否已就绪,再打开浏览器。
44
+
45
+ **修改的文件**:
46
+ - `src/providers/core/config/oauth-flows.ts`
47
+ - 新增 `waitForPortalReady()` 方法
48
+ - 在 `activateWithBrowser()` 中,如果使用 Portal URL,先调用 `waitForPortalReady()`
49
+ - 最多等待 3 秒(15 次 × 200ms),通过 `/health` 端点检查服务器状态
50
+
51
+ ## 实现细节
52
+
53
+ ### 1. 分离 OAuth Portal 路由 (`routes.ts`)
54
+
55
+ ```typescript
56
+ /**
57
+ * Register OAuth Portal route early to support token authentication flow
58
+ * This route must be available before provider initialization
59
+ */
60
+ export function registerOAuthPortalRoute(app: Application): void {
61
+ app.get('/token-auth/demo', (req: Request, res: Response) => {
62
+ // ... Portal 页面实现 ...
63
+ });
64
+ }
65
+ ```
66
+
67
+ ### 2. 提前注册路由 (`index.ts` 构造函数)
68
+
69
+ ```typescript
70
+ constructor(config: ServerConfigV2) {
71
+ // ... 其他初始化 ...
72
+
73
+ // Register critical routes early (before provider initialization)
74
+ // This ensures OAuth Portal is available when providers check token validity
75
+ registerDefaultMiddleware(this.app);
76
+ registerOAuthPortalRoute(this.app);
77
+ console.log('[RouteCodexHttpServer] OAuth Portal route registered (early initialization)');
78
+ }
79
+ ```
80
+
81
+ ### 3. 智能等待机制 (`oauth-flows.ts`)
82
+
83
+ ```typescript
84
+ protected async activateWithBrowser(...): Promise<void> {
85
+ // ... 生成 Portal URL ...
86
+
87
+ if (options.openBrowser !== false) {
88
+ // If using Portal URL, ensure server is ready before opening browser
89
+ if (portalUrl) {
90
+ await this.waitForPortalReady(portalUrl);
91
+ }
92
+
93
+ // ... 打开浏览器 ...
94
+ }
95
+ }
96
+
97
+ protected async waitForPortalReady(portalUrl: string): Promise<void> {
98
+ const healthUrl = `${baseUrl}/health`;
99
+ const maxAttempts = 15; // 最多尝试 15 次
100
+ const delayMs = 200; // 每次等待 200ms,总计最多 3 秒
101
+
102
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
103
+ // 尝试访问 /health 端点
104
+ if (healthCheckSuccess) {
105
+ console.log('[OAuth] Portal server is ready');
106
+ return;
107
+ }
108
+ await delay(delayMs);
109
+ }
110
+
111
+ // 超时但继续执行(路由可能已经可用)
112
+ console.warn('[OAuth] Portal server health check timed out, continuing anyway...');
113
+ }
114
+ ```
115
+
116
+ ## 效果
117
+
118
+ 修复后的启动流程:
119
+
120
+ 1. ✅ 服务器构造函数执行
121
+ - 注册默认中间件
122
+ - **提前注册 OAuth Portal 路由** ← 关键修复
123
+
124
+ 2. ✅ Provider 初始化
125
+ - 检查 token 有效性
126
+ - 需要 OAuth 认证时:
127
+ - **检查服务器是否就绪** ← 关键修复
128
+ - 打开浏览器访问 Portal URL
129
+ - ✅ Portal 路由已存在,用户看到认证页面
130
+
131
+ 3. ✅ 其他 HTTP 路由注册
132
+ - 注册业务路由(/v1/chat/completions 等)
133
+
134
+ 4. ✅ 服务器监听端口
135
+
136
+ ## 测试方法
137
+
138
+ ### 手动测试
139
+
140
+ 1. 启动服务器:
141
+ ```bash
142
+ routecodex start
143
+ ```
144
+
145
+ 2. 观察日志中是否有:
146
+ ```
147
+ [RouteCodexHttpServer] OAuth Portal route registered (early initialization)
148
+ ```
149
+
150
+ 3. 如果有 token 需要认证,观察日志中是否有:
151
+ ```
152
+ [OAuth] Portal server is ready
153
+ Opening browser for authentication...
154
+ Portal URL: http://127.0.0.1:5555/token-auth/demo?...
155
+ ```
156
+
157
+ 4. 浏览器应该能正常打开 Portal 页面,显示 token 信息
158
+
159
+ ### 自动化测试
160
+
161
+ 创建测试脚本验证路由可用性(需要服务器先启动):
162
+
163
+ ```bash
164
+ # 测试 health 端点
165
+ curl http://127.0.0.1:5555/health
166
+
167
+ # 测试 Portal 端点
168
+ curl "http://127.0.0.1:5555/token-auth/demo?provider=test&alias=test&oauthUrl=https://example.com&sessionId=test-123"
169
+ ```
170
+
171
+ ## 向后兼容性
172
+
173
+ - ✅ 完全向后兼容
174
+ - ✅ 不影响现有的 OAuth 流程
175
+ - ✅ 不影响现有的路由注册
176
+ - ✅ 只是提前了 Portal 路由的注册时机
177
+ - ✅ 增加了可选的服务器就绪检查(失败也不影响后续流程)
178
+
179
+ ## 相关文件
180
+
181
+ ### 修改的文件
182
+ 1. `src/server/runtime/http-server/routes.ts` - 分离 OAuth Portal 路由
183
+ 2. `src/server/runtime/http-server/index.ts` - 提前注册路由
184
+ 3. `src/providers/core/config/oauth-flows.ts` - 添加就绪检查
185
+
186
+ ### 影响的功能
187
+ - OAuth 认证流程
188
+ - 多 token 支持
189
+ - Token Portal 页面
190
+
191
+ ## 注意事项
192
+
193
+ 1. **中间件注册**: `registerDefaultMiddleware()` 也移到了构造函数中,避免重复注册
194
+ 2. **健康检查**: 使用 `/health` 端点作为服务器就绪的判断标准
195
+ 3. **超时处理**: 即使健康检查超时,也会继续打开浏览器(防止误报)
196
+ 4. **日志输出**: 添加了详细的日志,便于调试时序问题
197
+
198
+ ## 未来改进
199
+
200
+ 1. 可以考虑将所有"关键路由"(如 `/health`, `/config`)也提前注册
201
+ 2. 可以添加更精确的服务器启动状态跟踪
202
+ 3. 可以在 Portal 页面上显示"等待服务器就绪"的进度提示
@@ -0,0 +1,265 @@
1
+ # Web Search Server-side Tool 第三跳修复
2
+
3
+ **修复日期:** 2025-12-30
4
+ **影响范围:** `src/server/runtime/http-server/request-executor.ts`
5
+ **问题类型:** Server-side tool 第三跳请求构建错误
6
+
7
+ ---
8
+
9
+ ## 问题描述
10
+
11
+ 在实现 `web_search` server-side tool 时,第三跳(将搜索结果返回给主模型)的请求构建不正确,导致:
12
+ - 消息历史混乱
13
+ - 工具执行结果配对失败
14
+ - 主模型无法正确处理搜索结果
15
+
16
+ ### 正确的三跳流程
17
+
18
+ 1. **第一跳 (主模型调用):**
19
+ - 用户请求 → Chat Process → 主模型
20
+ - 主模型识别需要 web_search → 返回 tool_call
21
+
22
+ 2. **第二跳 (执行 web_search):**
23
+ - 拦截 tool_call → 调用搜索后端(如 GLM)
24
+ - **单独执行**(不带原始请求上下文,走特殊通道)
25
+ - 返回搜索结果
26
+
27
+ 3. **第三跳 (返回主模型):**
28
+ - **在 Chat Process 层面**显式组装:
29
+ - 原始用户请求(来自 `__chat_process_raw`)
30
+ - + Assistant 的 tool_call 消息
31
+ - + Tool 执行结果
32
+ - 重新发送到虚拟路由器 → 主模型生成最终答案
33
+
34
+ ---
35
+
36
+ ## 根本原因
37
+
38
+ ### 1. **原始请求克隆位置错误**
39
+
40
+ **错误实现:**
41
+ ```typescript
42
+ // 在 Chat Process 循环中克隆 input.body
43
+ const originalRequestSnapshot = this.cloneRequestPayload(input.body);
44
+ ```
45
+
46
+ **问题:**
47
+ - `input.body` 已经被预处理过(如注入系统提示词)
48
+ - 不是"干净"的 chat 协议版本请求
49
+ - 导致第三跳重复应用修改
50
+
51
+ ### 2. **缺少 Chat Process Raw 的概念**
52
+
53
+ 需要区分两个不同的"原始请求":
54
+ - `__raw_request_body`: handler 入口的原始协议(chat/responses/messages)
55
+ - **`__chat_process_raw`**: Chat Process 入口已转换为 **chat 协议** 的请求 ← **新增**
56
+
57
+ ### 3. **第三跳消息组装逻辑不完整**
58
+
59
+ **错误实现:**
60
+ ```typescript
61
+ // 直接使用 core 返回的 body,没有显式控制消息历史
62
+ body: reenterOpts.body
63
+ ```
64
+
65
+ **问题:**
66
+ - 依赖 `llmswitch-core` 内部逻辑组装 messages
67
+ - 如果 core 的实现有问题或使用了错误的 base,第三跳就会出错
68
+ - 没有显式保证"原始消息 + tool_call + tool_result"的顺序
69
+
70
+ ---
71
+
72
+ ## 修复方案
73
+
74
+ ### 改动 1: 在 Chat Process 入口克隆 `__chat_process_raw`
75
+
76
+ **位置:** `HubRequestExecutor.execute()` 循环开始处
77
+
78
+ **实现:**
79
+ ```typescript
80
+ // 在第一次迭代时克隆 chat 协议版本的请求
81
+ if (attempt === 0 && !iterationMetadata.__chat_process_raw) {
82
+ const isChatEndpoint =
83
+ input.entryEndpoint.includes('/v1/chat/completions') ||
84
+ input.entryEndpoint.includes('/v1/responses') ||
85
+ input.entryEndpoint.includes('/v1/messages');
86
+
87
+ if (isChatEndpoint) {
88
+ // 克隆当前的 input.body (此时已经是 inbound 转换后的 chat 协议格式)
89
+ iterationMetadata.__chat_process_raw = this.cloneRequestPayload(input.body);
90
+ this.logStage('chat.process.raw.cloned', providerRequestId, {
91
+ endpoint: input.entryEndpoint,
92
+ hasMessages: Boolean(input.body && typeof input.body === 'object' && 'messages' in input.body)
93
+ });
94
+ }
95
+ }
96
+
97
+ // 获取原始请求快照 (优先使用 chat_process_raw)
98
+ const originalRequestSnapshot = iterationMetadata.__chat_process_raw &&
99
+ typeof iterationMetadata.__chat_process_raw === 'object'
100
+ ? (iterationMetadata.__chat_process_raw as Record<string, unknown>)
101
+ : this.cloneRequestPayload(input.body);
102
+ ```
103
+
104
+ **关键点:**
105
+ - ✅ 只在 **第一次迭代** (`attempt === 0`) 克隆
106
+ - ✅ 只对 chat 相关端点生效
107
+ - ✅ 克隆的是 **inbound 转换后的 chat 协议格式**
108
+ - ✅ 通过 `iterationMetadata` 传递,支持多轮循环
109
+
110
+ ### 改动 2: 第三跳显式组装消息历史 (仅 chat 协议)
111
+
112
+ **位置:** `convertProviderResponseIfNeeded()` 内的 `reenterPipeline` 回调
113
+
114
+ **实现:**
115
+ ```typescript
116
+ const reenterPipeline = async (reenterOpts) => {
117
+ const targetEndpoint = reenterOpts.entryEndpoint || options.entryEndpoint || entry;
118
+ const isChatEndpoint = targetEndpoint.includes('/v1/chat/completions');
119
+
120
+ let finalBody = reenterOpts.body;
121
+
122
+ // Chat 协议的第三跳显式组装逻辑
123
+ if (isChatEndpoint) {
124
+ const chatProcessRaw = metadataBag?.__chat_process_raw;
125
+
126
+ if (chatProcessRaw && typeof chatProcessRaw === 'object') {
127
+ const rawRequest = chatProcessRaw as Record<string, unknown>;
128
+ const incomingBody = reenterOpts.body;
129
+
130
+ // 从 core 返回的 body 中提取 assistant 和 tool 消息
131
+ const incomingMessages = Array.isArray(incomingBody?.messages)
132
+ ? incomingBody.messages : [];
133
+
134
+ const assistantMessages = incomingMessages.filter(
135
+ (msg: any) => msg && msg.role === 'assistant' && msg.tool_calls
136
+ );
137
+ const toolMessages = incomingMessages.filter(
138
+ (msg: any) => msg && msg.role === 'tool'
139
+ );
140
+
141
+ const originalMessages = Array.isArray(rawRequest.messages)
142
+ ? rawRequest.messages : [];
143
+
144
+ // 重新组装: 原始消息 + assistant tool_call + tool result
145
+ const reconstructedMessages = [
146
+ ...originalMessages,
147
+ ...assistantMessages,
148
+ ...toolMessages
149
+ ];
150
+
151
+ // 构建第三跳的请求体(基于 chat_process_raw)
152
+ finalBody = {
153
+ ...rawRequest, // 保留原始的 model 等字段
154
+ messages: reconstructedMessages
155
+ };
156
+
157
+ this.logStage('chat.process.hop3.assembled', reenterOpts.requestId, {
158
+ originalMessageCount: originalMessages.length,
159
+ assistantMessageCount: assistantMessages.length,
160
+ toolMessageCount: toolMessages.length,
161
+ finalMessageCount: reconstructedMessages.length
162
+ });
163
+ }
164
+ }
165
+
166
+ // 传递 __chat_process_raw 到下一跳,支持多轮 tool 调用
167
+ const nestedMetadata = {
168
+ ...metadataBag,
169
+ ...reenterOpts.metadata,
170
+ serverToolFollowup: true,
171
+ __chat_process_raw: metadataBag?.__chat_process_raw
172
+ };
173
+
174
+ return await this.execute({
175
+ ...nestedInput,
176
+ body: finalBody // 使用显式组装的 body
177
+ });
178
+ };
179
+ ```
180
+
181
+ **关键点:**
182
+ - ✅ **仅对 `/v1/chat/completions` 生效**(其他协议走标准转换)
183
+ - ✅ 基于 `__chat_process_raw` 重新组装完整消息历史
184
+ - ✅ 明确提取 assistant tool_call 和 tool result
185
+ - ✅ 保留原始请求的其他字段(如 `model`)
186
+ - ✅ 传递 `__chat_process_raw` 到下一跳,支持多轮工具调用
187
+
188
+ ---
189
+
190
+ ## 新增日志
191
+
192
+ ### `chat.process.raw.cloned`
193
+ - **触发时机:** Chat Process 第一次迭代,成功克隆 chat_process_raw
194
+ - **用途:** 确认克隆时机和内容
195
+ - **字段:**
196
+ - `endpoint`: 入口端点
197
+ - `hasMessages`: 是否包含 messages 字段
198
+
199
+ ### `chat.process.hop3.assembled`
200
+ - **触发时机:** 第三跳成功组装消息历史
201
+ - **用途:** 验证消息组装正确性
202
+ - **字段:**
203
+ - `originalMessageCount`: 原始用户消息数量
204
+ - `assistantMessageCount`: assistant tool_call 消息数量
205
+ - `toolMessageCount`: tool result 消息数量
206
+ - `finalMessageCount`: 最终消息总数
207
+
208
+ ### `chat.process.hop3.fallback`
209
+ - **触发时机:** 缺少 `__chat_process_raw`,降级使用 core 提供的 body
210
+ - **用途:** 诊断异常情况
211
+ - **字段:**
212
+ - `reason`: 降级原因(如 `missing_chat_process_raw`)
213
+
214
+ ---
215
+
216
+ ## 验证要点
217
+
218
+ ### 1. 检查日志顺序
219
+ 正常流程应该看到:
220
+ ```
221
+ [chat.process.raw.cloned] endpoint=/v1/chat/completions hasMessages=true
222
+ [chat.process.hop3.assembled] originalMessageCount=1 assistantMessageCount=1 toolMessageCount=1 finalMessageCount=3
223
+ ```
224
+
225
+ ### 2. 验证第三跳请求
226
+ - messages[0]: 原始用户请求(role=user)
227
+ - messages[1]: assistant tool_call(role=assistant, 包含 tool_calls)
228
+ - messages[2]: tool result(role=tool, tool_call_id 匹配)
229
+
230
+ ### 3. 确认协议隔离
231
+ - `/v1/chat/completions`: 走显式组装逻辑
232
+ - `/v1/responses`, `/v1/messages`: 走标准 inbound/outbound 转换
233
+
234
+ ---
235
+
236
+ ## 影响范围
237
+
238
+ ### ✅ 修复的场景
239
+ - `/v1/chat/completions` 端点的 `web_search` 工具调用
240
+ - 多轮工具调用场景(连续的 tool_call)
241
+
242
+ ### ⚠️ 不受影响的场景
243
+ - `/v1/responses` 端点(依赖 llmswitch-core 的标准转换)
244
+ - `/v1/messages` (Anthropic) 端点(依赖标准转换)
245
+ - 客户端侧工具调用(submit_tool_outputs)
246
+
247
+ ---
248
+
249
+ ## 后续优化建议
250
+
251
+ 1. **扩展到其他端点:**
252
+ - 考虑为 `/v1/responses` 实现类似的显式组装逻辑
253
+ - 需要适配 responses 协议的消息历史格式
254
+
255
+ 2. **增强错误处理:**
256
+ - 如果第三跳组装失败,记录详细的诊断信息
257
+ - 提供降级机制,避免整个请求失败
258
+
259
+ 3. **性能优化:**
260
+ - `__chat_process_raw` 的克隆可以惰性执行(只在检测到 tool_call 时克隆)
261
+ - 减少不必要的 JSON 序列化/反序列化
262
+
263
+ 4. **单元测试:**
264
+ - 添加第三跳消息组装的单元测试
265
+ - 覆盖边界情况(如空消息、多个 tool_call 等)