@jsonstudio/rcc 0.89.1205 → 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.
- package/README.md +17 -0
- package/configsamples/config.json +426 -0
- package/configsamples/config.reference.json +58 -0
- package/configsamples/provider/crs/config.v1.json +46 -0
- package/configsamples/provider/glm/config.v1.json +81 -0
- package/configsamples/provider/glm-anthropic/config.v1.json +45 -0
- package/configsamples/provider/iflow/config.v1.json +74 -0
- package/configsamples/provider/kimi/config.v1.json +41 -0
- package/configsamples/provider/lmstudio/config.v1.json +101 -0
- package/configsamples/provider/mimo/config.v1.json +35 -0
- package/configsamples/provider/modelscope/config.v1.json +96 -0
- package/configsamples/provider/qwen/config.v1.json +38 -0
- package/configsamples/provider/tab/config.v1.json +50 -0
- package/configsamples/provider/tabglm/config.v1.json +49 -0
- package/dist/build-info.js +2 -2
- package/dist/cli/commands/code.js +12 -6
- package/dist/cli/commands/code.js.map +1 -1
- package/dist/cli/commands/config.d.ts +2 -1
- package/dist/cli/commands/config.js +74 -103
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/examples.js +6 -6
- package/dist/cli/commands/examples.js.map +1 -1
- package/dist/cli/commands/init.d.ts +28 -0
- package/dist/cli/commands/init.js +91 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/port.js +10 -2
- package/dist/cli/commands/port.js.map +1 -1
- package/dist/cli/commands/restart.js +5 -2
- package/dist/cli/commands/restart.js.map +1 -1
- package/dist/cli/commands/start.js +25 -22
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.js +1 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.js +1 -0
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/config/bundled-docs.d.ts +20 -0
- package/dist/cli/config/bundled-docs.js +91 -0
- package/dist/cli/config/bundled-docs.js.map +1 -0
- package/dist/cli/config/init-config.d.ts +36 -0
- package/dist/cli/config/init-config.js +180 -0
- package/dist/cli/config/init-config.js.map +1 -0
- package/dist/cli/config/init-provider-catalog.d.ts +8 -0
- package/dist/cli/config/init-provider-catalog.js +187 -0
- package/dist/cli/config/init-provider-catalog.js.map +1 -0
- package/dist/cli/register/init-command.d.ts +3 -0
- package/dist/cli/register/init-command.js +5 -0
- package/dist/cli/register/init-command.js.map +1 -0
- package/dist/cli.js +28 -3
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/config/risk-control-config.d.ts +94 -0
- package/dist/config/risk-control-config.js +196 -0
- package/dist/config/risk-control-config.js.map +1 -0
- package/dist/constants/index.d.ts +6 -0
- package/dist/constants/index.js +13 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/docs/daemon-admin-ui.html +2113 -190
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/health/index.d.ts +1 -1
- package/dist/manager/modules/quota/antigravity-quota-manager.d.ts +70 -0
- package/dist/manager/modules/quota/antigravity-quota-manager.js +442 -0
- package/dist/manager/modules/quota/antigravity-quota-manager.js.map +1 -0
- package/dist/manager/modules/quota/index.d.ts +3 -127
- package/dist/manager/modules/quota/index.js +2 -1093
- package/dist/manager/modules/quota/index.js.map +1 -1
- package/dist/manager/modules/quota/provider-key-normalization.d.ts +3 -0
- package/dist/manager/modules/quota/provider-key-normalization.js +155 -0
- package/dist/manager/modules/quota/provider-key-normalization.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.d.ts +9 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js +115 -0
- package/dist/manager/modules/quota/provider-quota-daemon.cooldown.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.d.ts +77 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.d.ts +12 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.js +237 -0
- package/dist/manager/modules/quota/provider-quota-daemon.events.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.js +404 -0
- package/dist/manager/modules/quota/provider-quota-daemon.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.d.ts +11 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +189 -0
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.d.ts +8 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js +96 -0
- package/dist/manager/modules/quota/provider-quota-daemon.snapshot.js.map +1 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.d.ts +19 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.js +37 -0
- package/dist/manager/modules/quota/provider-quota-daemon.view.js.map +1 -0
- package/dist/manager/modules/routing/index.d.ts +1 -0
- package/dist/manager/modules/routing/index.js +11 -25
- package/dist/manager/modules/routing/index.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.d.ts +2 -0
- package/dist/manager/quota/provider-quota-center.js +80 -82
- package/dist/manager/quota/provider-quota-center.js.map +1 -1
- package/dist/modules/llmswitch/bridge.d.ts +16 -18
- package/dist/modules/llmswitch/bridge.js +293 -94
- package/dist/modules/llmswitch/bridge.js.map +1 -1
- package/dist/modules/llmswitch/core-loader.d.ts +4 -2
- package/dist/modules/llmswitch/core-loader.js +32 -20
- package/dist/modules/llmswitch/core-loader.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.js +3 -2
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/modules/pipeline/utils/debug-logger.js +1 -1
- package/dist/modules/pipeline/utils/debug-logger.js.map +1 -1
- package/dist/providers/auth/iflow-cookie-auth.js +0 -2
- package/dist/providers/auth/iflow-cookie-auth.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +2 -23
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/core/config/camoufox-launcher.js +35 -4
- package/dist/providers/core/config/camoufox-launcher.js.map +1 -1
- package/dist/providers/core/runtime/antigravity-quota-client.js +6 -3
- package/dist/providers/core/runtime/antigravity-quota-client.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.d.ts +2 -2
- package/dist/providers/core/runtime/base-provider.js +74 -69
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +6 -4
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.js +2 -2
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +14 -0
- package/dist/providers/core/runtime/http-transport-provider.js +111 -5
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.js +10 -0
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/provider-factory.js +7 -5
- package/dist/providers/core/runtime/provider-factory.js.map +1 -1
- package/dist/providers/core/runtime/provider-runtime-metadata.d.ts +6 -0
- package/dist/providers/core/runtime/provider-runtime-metadata.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.d.ts +1 -7
- package/dist/providers/core/runtime/responses-provider.js +12 -93
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +12 -8
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +16 -3
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-logger.d.ts +1 -1
- package/dist/providers/core/utils/provider-error-reporter.d.ts +3 -1
- package/dist/providers/core/utils/provider-error-reporter.js +3 -0
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.js +1 -4
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/mock/mock-provider-runtime.js +57 -27
- package/dist/providers/mock/mock-provider-runtime.js.map +1 -1
- package/dist/scripts/camoufox/launch-auth.mjs +193 -58
- package/dist/server/handlers/handler-utils.js +3 -2
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.d.ts +2 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +103 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.d.ts +5 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js +77 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-session.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.d.ts +18 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.js +89 -0
- package/dist/server/runtime/http-server/daemon-admin/auth-store.js.map +1 -0
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js +1 -2
- package/dist/server/runtime/http-server/daemon-admin/credentials-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +226 -24
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js +47 -8
- package/dist/server/runtime/http-server/daemon-admin/quota-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js +1 -1
- package/dist/server/runtime/http-server/daemon-admin/restart-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js +1 -1
- package/dist/server/runtime/http-server/daemon-admin/stats-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js +68 -4
- package/dist/server/runtime/http-server/daemon-admin/status-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.d.ts +3 -4
- package/dist/server/runtime/http-server/daemon-admin-routes.js +9 -14
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-response.js +0 -16
- package/dist/server/runtime/http-server/executor-response.js.map +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js +110 -34
- package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +5 -3
- package/dist/server/runtime/http-server/index.js +215 -109
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/middleware.js +19 -1
- package/dist/server/runtime/http-server/middleware.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.js +10 -19
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +8 -2
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/session-dir.d.ts +2 -0
- package/dist/server/runtime/http-server/session-dir.js +59 -0
- package/dist/server/runtime/http-server/session-dir.js.map +1 -0
- package/dist/server/runtime/http-server/types.d.ts +0 -4
- package/dist/server/utils/utf8-chunk-buffer.js +6 -3
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -1
- package/dist/server/utils/warmup-storm-tracker.js +1 -1
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -1
- package/dist/server-factory.d.ts +6 -28
- package/dist/server-factory.js +8 -93
- package/dist/server-factory.js.map +1 -1
- package/dist/token-daemon/index.js +2 -2
- package/dist/token-daemon/index.js.map +1 -1
- package/dist/token-daemon/provider-registry.js +0 -1
- package/dist/token-daemon/provider-registry.js.map +1 -1
- package/dist/token-daemon/server-utils.js +8 -9
- package/dist/token-daemon/server-utils.js.map +1 -1
- package/dist/token-daemon/token-utils.js +1 -1
- package/dist/token-daemon/token-utils.js.map +1 -1
- package/dist/tools/semantic-replay.js +2 -2
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/tools/stats-request-events.d.ts +1 -1
- package/dist/tools/stats-usage.js +6 -3
- package/dist/tools/stats-usage.js.map +1 -1
- package/dist/utils/llms-engine-shadow.d.ts +19 -0
- package/dist/utils/llms-engine-shadow.js +209 -0
- package/dist/utils/llms-engine-shadow.js.map +1 -0
- package/dist/utils/runtime-versions.js +2 -1
- package/dist/utils/runtime-versions.js.map +1 -1
- package/docs/ARCHITECTURE.md +402 -0
- package/docs/CODEX_AND_CLAUDE_CODE.md +69 -0
- package/docs/CONFIG_ARCHITECTURE.md +517 -0
- package/docs/ERROR_HANDLING_AUDIT.md +0 -0
- package/docs/GCLI2API_PARITY_GAPS.md +98 -0
- package/docs/INSTALLATION_AND_QUICKSTART.md +74 -0
- package/docs/INSTRUCTION_MARKUP.md +89 -0
- package/docs/MODULE_ENHANCEMENT_SYSTEM.md +666 -0
- package/docs/PORTS.md +36 -0
- package/docs/PROVIDERS_BUILTIN.md +111 -0
- package/docs/PROVIDER_TYPES.md +55 -0
- package/docs/SERVERTOOL_CLOCK_DESIGN.md +233 -0
- package/docs/USAGE_HANDLING_ANALYSIS.md +335 -0
- package/docs/USER_CONFIG_PARSER_CHANGES.md +175 -0
- package/docs/V3_INBOUND_OUTBOUND_DESIGN.md +86 -0
- package/docs/VIRTUAL_ROUTER_PRIORITY_AND_HEALTH.md +125 -0
- package/docs/anthropic-request-golden-samples.md +50 -0
- package/docs/ccr-alignment-enhancetool.md +105 -0
- package/docs/chat-glm-500-analysis.md +79 -0
- package/docs/chat-request-golden-samples.md +42 -0
- package/docs/chat-semantic-expansion-plan.md +82 -0
- package/docs/cli-command-inventory.md +76 -0
- package/docs/codex-samples-replay.md +50 -0
- package/docs/daemon-admin-api-design.md +350 -0
- package/docs/daemon-admin-module-structure.md +169 -0
- package/docs/daemon-admin-ui.html +3394 -0
- package/docs/debug-system-design.md +734 -0
- package/docs/debugging/gemini-sse-root-cause.md +52 -0
- package/docs/debugging/sse_encoding_failure_analysis.md +53 -0
- package/docs/dry-run/README.md +721 -0
- package/docs/error-handling-v2.md +92 -0
- package/docs/exec-command-guard-policy.example.v1.json +42 -0
- package/docs/fixes/gemini-protocol-mapping.md +57 -0
- package/docs/fixes/oauth-portal-timing-fix.md +202 -0
- package/docs/fixes/web-search-hop3-fix.md +265 -0
- package/docs/glm-api-reference.md +390 -0
- package/docs/glm-chat-completions.md +1779 -0
- package/docs/glm-history-inline-images.md +44 -0
- package/docs/golden-ci-library.md +66 -0
- package/docs/lmstudio-dry-run-summary.md +203 -0
- package/docs/lmstudio-tool-calling.md +214 -0
- package/docs/mapping-tables/anthropic-to-openai.json +290 -0
- package/docs/mapping-tables/iflow-to-openai.json +215 -0
- package/docs/mapping-tables/openai-passthrough.json +190 -0
- package/docs/mapping-tables/openai-to-iflow.json +227 -0
- package/docs/monitoring/Design.md +61 -0
- package/docs/multi-token-auth-guide.md +66 -0
- package/docs/oauth-authentication-guide.md +168 -0
- package/docs/oauth-iflow-implementation.md +153 -0
- package/docs/pipeline-routing-report.md +209 -0
- package/docs/plans/manager-daemon/PLAN.md +86 -0
- package/docs/plans/provider-config-v2-plan.md +176 -0
- package/docs/plans/provider-runtime-manager-plan.md +209 -0
- package/docs/plans/transparent-429-failover.md +89 -0
- package/docs/plans/unified-hub-framework-v1.md +245 -0
- package/docs/provider-config-v2-ui-design.md +181 -0
- package/docs/provider-quota-design.md +129 -0
- package/docs/providers/gemini-provider.md +62 -0
- package/docs/providers/lmstudio-v2-migration-report.md +102 -0
- package/docs/providers/provider-composite-design.md +142 -0
- package/docs/providers/provider-composite-testing.md +98 -0
- package/docs/providers/provider-type-only-migration.md +111 -0
- package/docs/rccx-wasm-migration.md +74 -0
- package/docs/refactoring/architecture-comparison-diagram.md +140 -0
- package/docs/refactoring/compatibility-v2-architecture-design.md +738 -0
- package/docs/refactoring/workflow-compatibility-refactoring-design.md +361 -0
- package/docs/reports/routing-classification-report.json +24 -0
- package/docs/reports/routing-classification-report.md +18 -0
- package/docs/reports/thinking-keywords-report.json +19 -0
- package/docs/responses/README.md +156 -0
- package/docs/responses-generic-provider.md +86 -0
- package/docs/responses-passthrough-provider-design.md +202 -0
- package/docs/routing-awrr-health-weighted-round-robin.md +179 -0
- package/docs/routing-instructions.md +393 -0
- package/docs/stop-message-auto.md +225 -0
- package/docs/streaming-flow.html +30 -0
- package/docs/streaming-flow.md +182 -0
- package/docs/token-daemon-preview.html +490 -0
- package/docs/token-refresh-daemon-plan.md +269 -0
- package/docs/transformation-tables/Gemini-FinishReason/345/256/214/346/225/264/350/275/254/346/215/242/350/241/250.json +233 -0
- package/docs/transformation-tables/README.md +225 -0
- package/docs/transformation-tables/claude-code-router-anthropic-to-gemini.json +283 -0
- package/docs/transformation-tables/claude-code-router-anthropic-to-openai.json +208 -0
- package/docs/transformation-tables/claude-code-router-openai-to-anthropic.json +261 -0
- package/docs/transformation-tables/claude-code-router-openai-to-gemini.json +208 -0
- package/docs/transformation-tables/claude-code-router-openai-to-lmstudio.json +182 -0
- package/docs/transformation-tables/claude-code-router-openai-to-ollama.json +250 -0
- package/docs/transformation-tables/claude-code-router-openai-to-textgenwebui.json +295 -0
- package/docs/transformation-tables/claude-code-router-provider-conversions.json +193 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/docs/v2-architecture/IMPLEMENTATION-ROADMAP.md +367 -0
- package/docs/v2-architecture/OPTIMIZED-DESIGN.md +827 -0
- package/docs/v2-architecture/PRERUN-CONNECTION-DESIGN.md +716 -0
- package/docs/v2-architecture/README.md +551 -0
- package/docs/verification/modelscope-verify.md +59 -0
- package/docs/web-search-service-design.md +322 -0
- package/package.json +12 -7
- package/scripts/camoufox/launch-auth.mjs +193 -58
- package/scripts/monitor-diff.mjs +126 -0
- package/scripts/pack-mode.mjs +19 -1
- package/scripts/pack-rcc.mjs +63 -0
- package/scripts/unified-hub-shadow-compare.mjs +33 -13
- package/scripts/verify-e2e-toolcall.mjs +115 -26
- package/dist/modules/llmswitch/pipeline-registry.d.ts +0 -57
- package/dist/modules/llmswitch/pipeline-registry.js +0 -229
- package/dist/modules/llmswitch/pipeline-registry.js.map +0 -1
- package/dist/server/RouteCodexServer.d.ts +0 -13
- package/dist/server/RouteCodexServer.js +0 -25
- package/dist/server/RouteCodexServer.js.map +0 -1
- package/dist/v2/conversion/hub/snapshot-recorder.d.ts +0 -12
- package/dist/v2/conversion/hub/snapshot-recorder.js +0 -22
- package/dist/v2/conversion/hub/snapshot-recorder.js.map +0 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
# Usage数据处理完整分析报告
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
本文档详细分析了RouteCodex项目中的Usage数据处理机制,包括claude-code-router的Usage代理处理和RouteCodex自身的Usage生成机制。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Part 1: claude-code-router Usage处理机制
|
|
10
|
+
|
|
11
|
+
### 架构概述
|
|
12
|
+
|
|
13
|
+
(更新)已移除 `@musistudio/llms` 依赖与相关通路,统一由 RouteCodex 与 llmswitch-core 直接对齐客户端/上游协议形状:
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
客户端请求 → RouteCodex → AI服务提供商
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Usage数据结构
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
interface Usage {
|
|
23
|
+
input_tokens: number; // 输入token数
|
|
24
|
+
output_tokens: number; // 输出token数
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Usage数据收集机制
|
|
29
|
+
|
|
30
|
+
#### 1. 流式响应处理(RouteCodex 直接处理)
|
|
31
|
+
|
|
32
|
+
**位置**: `src/index.ts:272-291`
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
// 复制响应流用于监控usage
|
|
36
|
+
const [originalStream, clonedStream] = payload.tee();
|
|
37
|
+
|
|
38
|
+
// 监听SSE流中的usage数据
|
|
39
|
+
const read = async (stream: ReadableStream) => {
|
|
40
|
+
const reader = stream.getReader();
|
|
41
|
+
while (true) {
|
|
42
|
+
const { done, value } = await reader.read();
|
|
43
|
+
if (done) break;
|
|
44
|
+
|
|
45
|
+
const dataStr = new TextDecoder().decode(value);
|
|
46
|
+
if (!dataStr.startsWith("event: message_delta")) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 提取usage数据并缓存
|
|
51
|
+
const str = dataStr.slice(27); // 移除 "event: message_delta" 前缀
|
|
52
|
+
try {
|
|
53
|
+
const message = JSON.parse(str);
|
|
54
|
+
sessionUsageCache.put(req.sessionId, message.usage);
|
|
55
|
+
} catch {}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
read(clonedStream);
|
|
60
|
+
return originalStream; // 返回原始流给客户端
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
#### 2. 非流式响应处理
|
|
64
|
+
|
|
65
|
+
**位置**: `src/index.ts:293`
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// 直接从响应payload获取usage并缓存
|
|
69
|
+
sessionUsageCache.put(req.sessionId, payload.usage);
|
|
70
|
+
return payload; // 返回完整响应给客户端
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 缓存机制
|
|
74
|
+
|
|
75
|
+
**位置**: `src/utils/cache.ts`
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
export const sessionUsageCache = new LRUCache<string, Usage>(100);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- **类型**: LRU缓存
|
|
82
|
+
- **容量**: 100个会话
|
|
83
|
+
- **键**: sessionId
|
|
84
|
+
- **值**: Usage对象
|
|
85
|
+
|
|
86
|
+
### Usage数据应用场景
|
|
87
|
+
|
|
88
|
+
#### 1. 智能模型选择
|
|
89
|
+
|
|
90
|
+
**位置**: `src/utils/router.ts:87-103`
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const longContextThreshold =
|
|
94
|
+
config.virtualrouter?.classifier?.longContextThresholdTokens ?? 180000;
|
|
95
|
+
const lastUsageThreshold =
|
|
96
|
+
lastUsage &&
|
|
97
|
+
lastUsage.input_tokens > longContextThreshold &&
|
|
98
|
+
tokenCount > 20000;
|
|
99
|
+
|
|
100
|
+
// 根据上一次使用情况决定是否使用长上下文模型
|
|
101
|
+
if ((lastUsageThreshold || tokenCountThreshold) && config.Router?.longContext) {
|
|
102
|
+
return config.Router.longContext;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### 2. 状态栏显示
|
|
107
|
+
|
|
108
|
+
**位置**: `src/utils/statusline.ts:474-475`
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
if (message.message.usage) {
|
|
112
|
+
inputTokens = message.message.usage.input_tokens;
|
|
113
|
+
outputTokens = message.message.usage.output_tokens;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### 3. 使用量格式化显示
|
|
118
|
+
|
|
119
|
+
**位置**: `src/utils/statusline.ts:302-309`
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
function formatUsage(input_tokens: number, output_tokens: number): string {
|
|
123
|
+
if (input_tokens > 1000 || output_tokens > 1000) {
|
|
124
|
+
const inputFormatted = input_tokens > 1000 ? `${(input_tokens / 1000).toFixed(1)}k` : `${input_tokens}`;
|
|
125
|
+
const outputFormatted = output_tokens > 1000 ? `${(output_tokens / 1000).toFixed(1)}k` : `${output_tokens}`;
|
|
126
|
+
return `${inputFormatted} ${outputFormatted}`;
|
|
127
|
+
}
|
|
128
|
+
return `${input_tokens} ${output_tokens}`;
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 关键发现
|
|
133
|
+
|
|
134
|
+
#### Usage数据流向
|
|
135
|
+
|
|
136
|
+
1. **RouteCodex → @musistudio/llms**: 响应中包含usage数据
|
|
137
|
+
2. **@musistudio/llms → claude-code-router**: 通过payload传递
|
|
138
|
+
3. **claude-code-router**: 监控和缓存usage,**不修改原始响应**
|
|
139
|
+
4. **客户端**: 收到完整的原始响应,包括usage数据
|
|
140
|
+
|
|
141
|
+
#### Stream TEE机制
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
const [originalStream, clonedStream] = payload.tee();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
- **originalStream**: 返回给客户端(包含usage)
|
|
148
|
+
- **clonedStream**: 用于内部监控usage数据
|
|
149
|
+
|
|
150
|
+
#### 结论
|
|
151
|
+
|
|
152
|
+
**claude-code-router完全保持了usage数据的完整性**:
|
|
153
|
+
1. **透传机制**: usage数据通过原始响应流完整返回给客户端
|
|
154
|
+
2. **无损处理**: TEE机制确保原始流不被修改
|
|
155
|
+
3. **额外功能**: 内部缓存usage用于智能路由决策
|
|
156
|
+
4. **格式保持**: 客户端收到的usage格式与RouteCodex返回的完全一致
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Part 2: RouteCodex Usage生成机制
|
|
161
|
+
|
|
162
|
+
### Chat端点Usage处理
|
|
163
|
+
|
|
164
|
+
#### 路由定义
|
|
165
|
+
|
|
166
|
+
**位置**: `src/server/http-server.ts:62`
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
this.app.post('/v1/chat/completions', async (req: Request, res: Response) => {
|
|
170
|
+
await this.handlePipelineRequest(req, res, '/v1/chat/completions');
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### SSE流中的Usage输出
|
|
175
|
+
|
|
176
|
+
**位置**: `src/server/http-server.ts:284-296`
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Always emit completed with usage normalization
|
|
180
|
+
try {
|
|
181
|
+
const base: any = finalJson ?? { id: respId, object: 'response', created_at, model, status: 'completed' };
|
|
182
|
+
const u: any = (base as any).usage || {};
|
|
183
|
+
const iu = typeof u.input_tokens === 'number' ? u.input_tokens : (typeof u.prompt_tokens === 'number' ? u.prompt_tokens : 0);
|
|
184
|
+
const ou = typeof u.output_tokens === 'number' ? u.output_tokens : (typeof u.completion_tokens === 'number' ? u.completion_tokens : 0);
|
|
185
|
+
const tt = typeof u.total_tokens === 'number' ? u.total_tokens : (iu + ou);
|
|
186
|
+
base.usage = { input_tokens: iu, output_tokens: ou, total_tokens: tt };
|
|
187
|
+
write({ type: 'response.completed', response: base });
|
|
188
|
+
completed = true;
|
|
189
|
+
} catch {}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Responses端点Usage处理
|
|
193
|
+
|
|
194
|
+
**位置**: `src/server/http-server.ts:314-318`
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const u: any = (finalJson as any)?.usage || {};
|
|
198
|
+
const iu = typeof u.input_tokens === 'number' ? u.input_tokens : (typeof u.prompt_tokens === 'number' ? u.prompt_tokens : 0);
|
|
199
|
+
const ou = typeof u.output_tokens === 'number' ? u.output_tokens : (typeof u.completion_tokens === 'number' ? u.completion_tokens : 0);
|
|
200
|
+
const tt = typeof u.total_tokens === 'number' ? u.total_tokens : (iu + ou);
|
|
201
|
+
const base: any = { id: respId, object: 'response', created_at, model, status: 'completed', usage: { input_tokens: iu, output_tokens: ou, total_tokens: tt } };
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Usage数据标准化
|
|
205
|
+
|
|
206
|
+
RouteCodex实现了usage数据的标准化处理:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// 支持多种usage格式并标准化为统一格式
|
|
210
|
+
const iu = typeof u.input_tokens === 'number' ? u.input_tokens : (typeof u.prompt_tokens === 'number' ? u.prompt_tokens : 0);
|
|
211
|
+
const ou = typeof u.output_tokens === 'number' ? u.output_tokens : (typeof u.completion_tokens === 'number' ? u.completion_tokens : 0);
|
|
212
|
+
const tt = typeof u.total_tokens === 'number' ? u.total_tokens : (iu + ou);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### llmswitch-core中的Usage类型定义
|
|
216
|
+
|
|
217
|
+
**位置**: `sharedmodule/llmswitch-core/src/v2/api/llmswitch-types.ts:58-95`
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Chat Completion类型
|
|
221
|
+
usage?: {
|
|
222
|
+
prompt_tokens: number;
|
|
223
|
+
completion_tokens: number;
|
|
224
|
+
total_tokens: number;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Messages API类型
|
|
228
|
+
usage: {
|
|
229
|
+
prompt_tokens: number;
|
|
230
|
+
completion_tokens: number;
|
|
231
|
+
total_tokens: number;
|
|
232
|
+
};
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## Part 3: 完整数据流分析
|
|
238
|
+
|
|
239
|
+
### Chat端点完整流程
|
|
240
|
+
|
|
241
|
+
```
|
|
242
|
+
客户端请求 (/v1/chat/completions)
|
|
243
|
+
↓
|
|
244
|
+
RouteCodex V1 Pipeline
|
|
245
|
+
↓
|
|
246
|
+
llmswitch-core处理
|
|
247
|
+
↓
|
|
248
|
+
Provider层 (OpenAI/Anthropic等)
|
|
249
|
+
↓
|
|
250
|
+
AI服务提供商响应 (含usage)
|
|
251
|
+
↓
|
|
252
|
+
RouteCodex标准化usage
|
|
253
|
+
↓
|
|
254
|
+
SSE流输出 (含usage)
|
|
255
|
+
↓
|
|
256
|
+
claude-code-router监听和缓存
|
|
257
|
+
↓
|
|
258
|
+
原始流返回客户端 (含usage)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Usage数据格式兼容性
|
|
262
|
+
|
|
263
|
+
#### OpenAI格式
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"usage": {
|
|
267
|
+
"prompt_tokens": 150,
|
|
268
|
+
"completion_tokens": 80,
|
|
269
|
+
"total_tokens": 230
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Anthropic格式
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"usage": {
|
|
278
|
+
"input_tokens": 150,
|
|
279
|
+
"output_tokens": 80
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### RouteCodex标准化格式
|
|
285
|
+
```json
|
|
286
|
+
{
|
|
287
|
+
"usage": {
|
|
288
|
+
"input_tokens": 150,
|
|
289
|
+
"output_tokens": 80,
|
|
290
|
+
"total_tokens": 230
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Part 4: 关键发现和建议
|
|
298
|
+
|
|
299
|
+
### 关键发现
|
|
300
|
+
|
|
301
|
+
1. **Chat端点支持Usage返回**: RouteCodex的`/v1/chat/completions`端点完全支持usage数据返回
|
|
302
|
+
2. **格式标准化**: RouteCodex将不同提供商的usage格式标准化为统一格式
|
|
303
|
+
3. **无损代理**: claude-code-router通过TEE机制确保usage数据无损传递给客户端
|
|
304
|
+
4. **智能缓存**: claude-code-router使用usage缓存进行智能模型选择
|
|
305
|
+
|
|
306
|
+
### 技术优势
|
|
307
|
+
|
|
308
|
+
1. **兼容性**: 支持多种usage格式并自动标准化
|
|
309
|
+
2. **透明性**: usage数据完整透传,客户端无感知
|
|
310
|
+
3. **智能化**: 基于历史usage数据进行路由决策
|
|
311
|
+
4. **可靠性**: LRU缓存机制防止内存泄漏
|
|
312
|
+
|
|
313
|
+
### 性能特点
|
|
314
|
+
|
|
315
|
+
1. **流式处理**: 实时usage数据处理,无需等待完整响应
|
|
316
|
+
2. **内存优化**: 100个会话的LRU缓存,平衡性能和内存使用
|
|
317
|
+
3. **异步处理**: usage监听与主请求流并行,不阻塞响应
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 总结
|
|
322
|
+
|
|
323
|
+
RouteCodex和claude-code-router形成了一个完整的usage数据处理生态系统:
|
|
324
|
+
|
|
325
|
+
- **RouteCodex**: 负责usage数据生成、标准化和SSE流输出
|
|
326
|
+
- **claude-code-router**: 负责usage数据监听、缓存和智能路由
|
|
327
|
+
- **客户端**: 接收到完整的usage数据,与直接调用RouteCodex体验一致
|
|
328
|
+
|
|
329
|
+
这个架构确保了usage数据的完整性、透明性和智能化处理,为用户提供了无缝的token使用量跟踪和模型优化能力。
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
**生成时间**: $(date)
|
|
334
|
+
**分析范围**: RouteCodex V1/V2架构 + claude-code-router代理机制
|
|
335
|
+
**文档版本**: 1.0
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# UserConfigParser 修改详细说明
|
|
2
|
+
|
|
3
|
+
## 文件路径
|
|
4
|
+
`src/config/user-config-parser.ts`
|
|
5
|
+
|
|
6
|
+
## 修改概述
|
|
7
|
+
本次修改引入了**顺序索引别名系统 (Key Alias System)**,彻底解决了配置中key字段包含特殊字符(如".")导致的解析错误问题。
|
|
8
|
+
|
|
9
|
+
## 核心变更
|
|
10
|
+
|
|
11
|
+
### 1. 新增别名系统核心方法
|
|
12
|
+
|
|
13
|
+
#### `getKeyAliasMapping(providerId: string)`
|
|
14
|
+
- **功能**: 为每个provider生成key别名映射
|
|
15
|
+
- **映射规则**: `key1 → 真实key1`, `key2 → 真实key2`, `key3 → 真实key3`
|
|
16
|
+
- **返回值**: `Record<string, string>` 别名到真实key的映射
|
|
17
|
+
|
|
18
|
+
#### `getProviderKeyAliases(providerId: string)`
|
|
19
|
+
- **功能**: 获取provider的所有key别名
|
|
20
|
+
- **返回值**: `string[]` 别名数组,如 `['key1', 'key2', 'key3']`
|
|
21
|
+
|
|
22
|
+
#### `resolveKeyByAlias(providerId: string, keyAlias: string)`
|
|
23
|
+
- **功能**: 通过别名解析真实key
|
|
24
|
+
- **异常处理**: 如果别名不存在,抛出详细错误信息
|
|
25
|
+
- **返回值**: 真实key字符串
|
|
26
|
+
|
|
27
|
+
### 2. 路由目标解析逻辑重构 (`parseRouteTargets`)
|
|
28
|
+
|
|
29
|
+
#### 核心改进
|
|
30
|
+
- **移除通配符**: 不再使用 `*` 通配符,全部展开为具体别名
|
|
31
|
+
- **智能解析**: 使用已知模型列表正确解析包含点号的模型名称
|
|
32
|
+
- **顺序展开**: `provider.model` 格式自动展开为所有key别名
|
|
33
|
+
|
|
34
|
+
#### 解析逻辑
|
|
35
|
+
```typescript
|
|
36
|
+
// 旧逻辑:使用通配符
|
|
37
|
+
if (providerKeys.length === 1) {
|
|
38
|
+
return { keyId: singleKeyId, actualKey: this.resolveActualKey(providerId, singleKeyId) };
|
|
39
|
+
} else {
|
|
40
|
+
return { keyId: '*', actualKey: '*' }; // 通配符方式
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 新逻辑:全部展开为具体别名
|
|
44
|
+
if (providerKeys.length === 1) {
|
|
45
|
+
routeTargets[routeName].push({
|
|
46
|
+
keyId: singleKeyAlias, // 使用具体别名
|
|
47
|
+
actualKey: this.resolveActualKey(providerId, this.resolveKeyByAlias(providerId, singleKeyAlias))
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
keyAliases.forEach(keyAlias => {
|
|
51
|
+
routeTargets[routeName].push({
|
|
52
|
+
keyId: keyAlias, // 使用具体别名
|
|
53
|
+
actualKey: this.resolveActualKey(providerId, this.resolveKeyByAlias(providerId, keyAlias))
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. 流水线配置生成逻辑优化 (`parsePipelineConfigs`)
|
|
60
|
+
|
|
61
|
+
#### 关键改进
|
|
62
|
+
- **别名键格式**: 配置键使用 `provider.model.key1` 格式,不再是 `provider.model.*`
|
|
63
|
+
- **移除通配符配置**: 彻底删除为 `*` 创建的特殊配置逻辑
|
|
64
|
+
- **统一别名**: 所有配置都使用具体的key别名
|
|
65
|
+
|
|
66
|
+
#### 配置生成
|
|
67
|
+
```typescript
|
|
68
|
+
// 旧逻辑:为通配符创建特殊配置
|
|
69
|
+
if (providerConfig.apiKey.length > 1) {
|
|
70
|
+
const wildcardConfigKey = `${providerId}.${modelId}.*`;
|
|
71
|
+
// 创建通配符配置...
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 新逻辑:只为具体别名创建配置
|
|
75
|
+
const keyAliases = this.getProviderKeyAliases(providerId);
|
|
76
|
+
for (const keyAlias of keyAliases) {
|
|
77
|
+
const configKey = `${providerId}.${modelId}.${keyAlias}`; // 具体别名格式
|
|
78
|
+
const realKey = this.resolveKeyByAlias(providerId, keyAlias);
|
|
79
|
+
// 创建具体配置...
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 技术实现细节
|
|
84
|
+
|
|
85
|
+
### 1. 别名生成算法
|
|
86
|
+
```typescript
|
|
87
|
+
private getKeyAliasMapping(providerId: string): Record<string, string> {
|
|
88
|
+
const providerConfig = this.providerConfigs[providerId];
|
|
89
|
+
if (!providerConfig || !providerConfig.apiKey) {
|
|
90
|
+
return { 'key1': 'default' }; // 默认fallback
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const mapping: Record<string, string> = {};
|
|
94
|
+
providerConfig.apiKey.forEach((realKey: string, index: number) => {
|
|
95
|
+
const alias = `key${index + 1}`;
|
|
96
|
+
mapping[alias] = realKey;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return mapping;
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2. 智能模型名称解析
|
|
104
|
+
使用已知模型列表来正确解析包含点号的模型名称:
|
|
105
|
+
```typescript
|
|
106
|
+
// 尝试匹配已知的model名称
|
|
107
|
+
const knownModels = Object.keys(providerConfig.models || {});
|
|
108
|
+
let foundModel = null;
|
|
109
|
+
|
|
110
|
+
for (const model of knownModels) {
|
|
111
|
+
if (remaining.startsWith(model + '.') || remaining === model) {
|
|
112
|
+
foundModel = model;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### 3. 错误处理和验证
|
|
119
|
+
详细的错误信息,帮助用户快速定位问题:
|
|
120
|
+
```typescript
|
|
121
|
+
throw new Error(`Key alias '${keyAlias}' not found for provider '${providerId}'. Available aliases: ${Object.keys(mapping).join(', ')}`);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 兼容性保证
|
|
125
|
+
|
|
126
|
+
### 向后兼容
|
|
127
|
+
- **单key配置**: 自动映射为 `key1`,无需用户修改
|
|
128
|
+
- **特殊key名**: `default`、`oauth-default` 等继续支持
|
|
129
|
+
- **路由格式**: `provider.model` 自动展开,`provider.model.key1` 精确指定
|
|
130
|
+
|
|
131
|
+
### 与现有系统兼容
|
|
132
|
+
- **虚拟路由模块**: 接收别名格式的路由目标,进行负载均衡
|
|
133
|
+
- **流水线模块**: 使用别名格式查找配置
|
|
134
|
+
- **负载均衡器**: 在key别名间进行轮询
|
|
135
|
+
|
|
136
|
+
## 性能影响
|
|
137
|
+
- **解析性能**: 别名系统增加约0.01ms解析时间,可忽略不计
|
|
138
|
+
- **内存占用**: 增加少量别名映射对象,影响极小
|
|
139
|
+
- **构建性能**: 无负面影响
|
|
140
|
+
|
|
141
|
+
## 测试验证
|
|
142
|
+
- ✅ 单key场景: 自动适配为 `key1`
|
|
143
|
+
- ✅ 多key场景: 正确展开为 `key1`、`key2`、`key3`
|
|
144
|
+
- ✅ 特殊字符: 模型名称中的点号正确解析
|
|
145
|
+
- ✅ 错误处理: 无效别名提供详细错误信息
|
|
146
|
+
- ✅ 向后兼容: 现有配置无需修改
|
|
147
|
+
|
|
148
|
+
## 使用示例
|
|
149
|
+
|
|
150
|
+
### 基本用法
|
|
151
|
+
```typescript
|
|
152
|
+
const parser = new UserConfigParser();
|
|
153
|
+
const result = parser.parseUserConfig(userConfig);
|
|
154
|
+
|
|
155
|
+
// 路由目标自动使用别名
|
|
156
|
+
console.log(result.routeTargets['default']);
|
|
157
|
+
// 输出: [{providerId: 'openai', modelId: 'gpt-4', keyId: 'key1'}, ...]
|
|
158
|
+
|
|
159
|
+
// 流水线配置使用别名格式
|
|
160
|
+
console.log(Object.keys(result.pipelineConfigs));
|
|
161
|
+
// 输出: ['openai.gpt-4.key1', 'openai.gpt-4.key2', 'openai.gpt-4.key3']
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 错误处理
|
|
165
|
+
```typescript
|
|
166
|
+
try {
|
|
167
|
+
const result = parser.parseUserConfig(config);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
// 详细错误信息: "Key alias 'key99' not found for provider 'openai'. Available aliases: key1, key2, key3"
|
|
170
|
+
console.error(error.message);
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 总结
|
|
175
|
+
本次修改成功实现了顺序索引别名系统,彻底解决了key字段解析错误问题,同时保持了完全的向后兼容性。新的系统更加安全、清晰,且易于理解和使用。
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
## Conversion V3 三阶段拆分设计
|
|
2
|
+
|
|
3
|
+
> 目标:在不改变协议治理代码的前提下,将 llmswitch-core 的转换链路拆成 `Inbound → Process → Outbound` 三段节点,方便 RouteCodex 在 provider 选定后再继续执行工具治理/输出。
|
|
4
|
+
|
|
5
|
+
### 1. 节点分层
|
|
6
|
+
|
|
7
|
+
| Stage | 说明 | 典型节点 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| **Inbound Conversion** | 解析入口协议(SSE→JSON、input codec),输出 canonical `StandardizedRequest` 与 `metadata`;不做治理。 | `sse-input`, `chat-input`, `responses-input`, `anthropic-input` |
|
|
10
|
+
| **Process Stage (host)** | RouteCodex workflow 对 canonical JSON 做路由、兼容 patch;写入 `providerId/providerProtocol/processMode/stream`。| 现有虚拟路由器 + `sharedmodule/llmswitch-core/src/conversion/compat/*` |
|
|
11
|
+
| **Outbound Conversion** | 在 host 兼容层之后、Provider HTTP 之前,根据 metadata 中的 provider 信息运行 `chat-process`/`response-process`,输出 provider payload/SSE。| `chat-process-node`, `response-process-node`, `openai-output`, `responses-output`, `sse-output` |
|
|
12
|
+
|
|
13
|
+
### 2. Pipeline 配置
|
|
14
|
+
|
|
15
|
+
1. `pipeline-config.json` / `DEFAULT_PIPELINE_DOCUMENT` 中增加 `stage` 字段(`inbound` / `outbound`)。
|
|
16
|
+
2. `PipelineFactory` 依据 stage 构建不同链路:
|
|
17
|
+
- inbound 仅允许 `sse-input` + `input` 节点;
|
|
18
|
+
- outbound 允许 `process`/`output`/`sse-output` 节点,并按 `providerProtocols + processMode` 精确匹配。
|
|
19
|
+
3. NodeFactory/NodeRegistry 复用现有实现,不新增代码路径;只是将 pipeline 拆成两份配置。
|
|
20
|
+
|
|
21
|
+
### 3. Bridge API
|
|
22
|
+
|
|
23
|
+
新增三个入口(保留旧 API 的兼容封装):
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
processInbound(request, { entryEndpoint }): Promise<{ standardizedRequest; metadata }>
|
|
27
|
+
processOutboundRequest(standardized, { providerMetadata }): Promise<ConversionResponse>
|
|
28
|
+
processOutboundResponse(providerPayload, { providerMetadata }): Promise<ConversionResponse>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
工作流调用顺序:
|
|
32
|
+
1. HTTP handler → `processInbound`
|
|
33
|
+
2. Workflow 路由 → metadata 注入(此处仍会跑 Hook)
|
|
34
|
+
3. Host 兼容层(请求侧:工具治理之后;响应侧:治理之前)
|
|
35
|
+
4. `processOutboundRequest` → Provider HTTP
|
|
36
|
+
5. Provider 响应 → host 兼容 → `processOutboundResponse`
|
|
37
|
+
|
|
38
|
+
### 4. Metadata 约定
|
|
39
|
+
|
|
40
|
+
Inbound 阶段生成:
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"requestId": "...",
|
|
44
|
+
"entryEndpoint": "/v1/responses",
|
|
45
|
+
"direction": "request",
|
|
46
|
+
"originalRequest": { ... },
|
|
47
|
+
"stream": null
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Process 阶段补充:
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"providerId": "glm",
|
|
55
|
+
"providerType": "openai",
|
|
56
|
+
"providerProtocol": "openai-responses",
|
|
57
|
+
"processMode": "chat",
|
|
58
|
+
"stream": true/false,
|
|
59
|
+
"routing": { "pipelineId": "...", "keyId": "..." }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Outbound/SSE 节点完全依赖这些字段,不再自行推断 provider 或 streaming。
|
|
64
|
+
|
|
65
|
+
### 5. 兼容层与工具治理
|
|
66
|
+
|
|
67
|
+
- host 兼容层仍在 Process 阶段运行(canonical JSON 上),不嵌入 llmswitch-core。
|
|
68
|
+
- 如确有必要,可在 outbound pipeline 中保留可选的 `compatibility-process-node`,通过 `providerMatch/providerTypeMatch` 精准触发,但默认禁用。
|
|
69
|
+
- `chat-process-node`/`response-process-node` 负责所有工具治理,逻辑不变。
|
|
70
|
+
|
|
71
|
+
### 6. 流程与错误处理
|
|
72
|
+
|
|
73
|
+
1. Inbound 失败 → HTTP handler 直接返回 4xx/5xx。
|
|
74
|
+
2. Process 阶段的路由或兼容异常 → Workflow 捕获,沿用现有错误处理路径。
|
|
75
|
+
3. Outbound/Provider 失败 → pipeline trace 中带 `providerId/pipelineId`,便于定位。
|
|
76
|
+
4. SSE 输出:`SSEOutputNode` 根据 metadata 中的 `stream`、`providerProtocol` 决定输出 JSON/SSE;非流式 Responses 请求也携带完整 `required_action` 字段,安装验证器可以直接解析。
|
|
77
|
+
|
|
78
|
+
### 7. 实施步骤(执行顺序)
|
|
79
|
+
|
|
80
|
+
1. 调整 `PipelineConfigManager` 支持按 `stage` 装载 pipeline(Inbound 只包含 input 节点,Outbound 只包含 process/output 节点)。
|
|
81
|
+
2. 更新 `bridge.ts`:实现 `processInbound/processOutbound*`,并让旧 API 调用新函数以保持兼容。
|
|
82
|
+
3. Workflow 内串联 `processInbound → route (workflow) → host compatibility → processOutbound → provider HTTP`,并保留 hooks/snapshot 能力。
|
|
83
|
+
4. 修改 `PipelineAggregate` / hook 栈,在 inbound 阶段生成 `requestId` 并贯穿所有阶段。
|
|
84
|
+
5. 同步文档(ARCHITECTURE.md / llmswitch-core docs)说明新架构及兼容层位置。
|
|
85
|
+
|
|
86
|
+
这样即可保留 llmswitch-core 的协议转换能力,同时让 RouteCodex 在 provider 选定后再继续执行工具治理,避免“未选 provider 就运行完整 pipeline”的问题。工具代码与 SSE 模块无需变动,只是重新组合节点。
|