@jsonstudio/rcc 0.89.168 → 0.89.524
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 +18 -15
- package/dist/build-info.js +3 -3
- package/dist/build-info.js.map +1 -1
- package/dist/cli.js +94 -0
- package/dist/cli.js.map +1 -1
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js +28 -5
- package/dist/client/gemini-cli/gemini-cli-protocol-client.js.map +1 -1
- package/dist/commands/token-daemon.d.ts +2 -0
- package/dist/commands/token-daemon.js +183 -0
- package/dist/commands/token-daemon.js.map +1 -0
- package/dist/error-handling/quiet-error-handling-center.d.ts +9 -0
- package/dist/error-handling/quiet-error-handling-center.js +141 -0
- package/dist/error-handling/quiet-error-handling-center.js.map +1 -0
- package/dist/error-handling/route-error-hub.js +8 -2
- package/dist/error-handling/route-error-hub.js.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/modules/llmswitch/bridge.d.ts +1 -1
- package/dist/modules/llmswitch/bridge.js +3 -2
- package/dist/modules/llmswitch/bridge.js.map +1 -1
- package/dist/modules/pipeline/utils/colored-logger.d.ts +2 -0
- package/dist/modules/pipeline/utils/colored-logger.js +22 -3
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- package/dist/providers/auth/antigravity-userinfo-helper.d.ts +10 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js +140 -0
- package/dist/providers/auth/antigravity-userinfo-helper.js.map +1 -0
- package/dist/providers/auth/gemini-cli-userinfo-helper.js +12 -2
- package/dist/providers/auth/gemini-cli-userinfo-helper.js.map +1 -1
- package/dist/providers/auth/oauth-lifecycle.js +395 -24
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/auth/token-scanner/index.d.ts +32 -0
- package/dist/providers/auth/token-scanner/index.js +86 -0
- package/dist/providers/auth/token-scanner/index.js.map +1 -0
- package/dist/providers/auth/tokenfile-auth.d.ts +17 -0
- package/dist/providers/auth/tokenfile-auth.js +27 -5
- package/dist/providers/auth/tokenfile-auth.js.map +1 -1
- package/dist/providers/core/api/provider-types.d.ts +10 -0
- package/dist/providers/core/config/oauth-flows.d.ts +25 -0
- package/dist/providers/core/config/oauth-flows.js +92 -5
- package/dist/providers/core/config/oauth-flows.js.map +1 -1
- package/dist/providers/core/config/provider-oauth-configs.js +93 -2
- package/dist/providers/core/config/provider-oauth-configs.js.map +1 -1
- package/dist/providers/core/config/service-profiles.js +18 -10
- package/dist/providers/core/config/service-profiles.js.map +1 -1
- package/dist/providers/core/runtime/base-provider.d.ts +2 -0
- package/dist/providers/core/runtime/base-provider.js +135 -15
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.d.ts +8 -3
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +332 -67
- package/dist/providers/core/runtime/gemini-cli-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/http-request-executor.d.ts +1 -0
- package/dist/providers/core/runtime/http-request-executor.js +41 -1
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +27 -0
- package/dist/providers/core/runtime/http-transport-provider.js +342 -69
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/provider-error-classifier.d.ts +2 -2
- package/dist/providers/core/runtime/provider-error-classifier.js +14 -4
- package/dist/providers/core/runtime/provider-error-classifier.js.map +1 -1
- package/dist/providers/core/runtime/provider-factory.d.ts +1 -0
- package/dist/providers/core/runtime/provider-factory.js +37 -8
- package/dist/providers/core/runtime/provider-factory.js.map +1 -1
- package/dist/providers/core/runtime/responses-provider.d.ts +3 -3
- package/dist/providers/core/runtime/responses-provider.js +56 -117
- package/dist/providers/core/runtime/responses-provider.js.map +1 -1
- package/dist/providers/core/runtime/vision-debug-utils.d.ts +13 -0
- package/dist/providers/core/runtime/vision-debug-utils.js +114 -0
- package/dist/providers/core/runtime/vision-debug-utils.js.map +1 -0
- package/dist/providers/core/strategies/oauth-auth-code-flow.js +82 -25
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.d.ts +5 -0
- package/dist/providers/core/utils/http-client.js +31 -4
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-reporter.js +8 -2
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.d.ts +1 -1
- package/dist/providers/core/utils/snapshot-writer.js +5 -1
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/profile/provider-profile-loader.js +8 -4
- package/dist/providers/profile/provider-profile-loader.js.map +1 -1
- package/dist/runtime/runtime-flags.d.ts +4 -0
- package/dist/runtime/runtime-flags.js +32 -0
- package/dist/runtime/runtime-flags.js.map +1 -0
- package/dist/server/handlers/handler-utils.js +29 -2
- package/dist/server/handlers/handler-utils.js.map +1 -1
- package/dist/server/handlers/messages-handler.js +27 -26
- package/dist/server/handlers/messages-handler.js.map +1 -1
- package/dist/server/handlers/responses-handler.js +35 -1
- package/dist/server/handlers/responses-handler.js.map +1 -1
- package/dist/server/handlers/sse-dispatcher.js +22 -2
- package/dist/server/handlers/sse-dispatcher.js.map +1 -1
- package/dist/server/runtime/http-server/index.d.ts +10 -0
- package/dist/server/runtime/http-server/index.js +551 -148
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +14 -0
- package/dist/server/runtime/http-server/request-executor.js +638 -149
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.d.ts +5 -0
- package/dist/server/runtime/http-server/routes.js +69 -0
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/runtime-manager.js +18 -0
- package/dist/server/runtime/http-server/runtime-manager.js.map +1 -1
- package/dist/server/utils/sse-request-parser.d.ts +1 -0
- package/dist/server/utils/sse-request-parser.js +17 -6
- package/dist/server/utils/sse-request-parser.js.map +1 -1
- package/dist/server/utils/utf8-chunk-buffer.d.ts +43 -0
- package/dist/server/utils/utf8-chunk-buffer.js +132 -0
- package/dist/server/utils/utf8-chunk-buffer.js.map +1 -0
- package/dist/server/utils/warmup-detector.d.ts +7 -0
- package/dist/server/utils/warmup-detector.js +125 -0
- package/dist/server/utils/warmup-detector.js.map +1 -0
- package/dist/server/utils/warmup-storm-tracker.d.ts +9 -0
- package/dist/server/utils/warmup-storm-tracker.js +61 -0
- package/dist/server/utils/warmup-storm-tracker.js.map +1 -0
- package/dist/token-daemon/index.d.ts +7 -0
- package/dist/token-daemon/index.js +242 -0
- package/dist/token-daemon/index.js.map +1 -0
- package/dist/token-daemon/server-utils.d.ts +33 -0
- package/dist/token-daemon/server-utils.js +155 -0
- package/dist/token-daemon/server-utils.js.map +1 -0
- package/dist/token-daemon/token-daemon.d.ts +20 -0
- package/dist/token-daemon/token-daemon.js +144 -0
- package/dist/token-daemon/token-daemon.js.map +1 -0
- package/dist/token-daemon/token-types.d.ts +44 -0
- package/dist/token-daemon/token-types.js +18 -0
- package/dist/token-daemon/token-types.js.map +1 -0
- package/dist/token-daemon/token-utils.d.ts +17 -0
- package/dist/token-daemon/token-utils.js +153 -0
- package/dist/token-daemon/token-utils.js.map +1 -0
- package/dist/tools/semantic-replay.js +7 -6
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/utils/debug-utils.js +14 -0
- package/dist/utils/debug-utils.js.map +1 -1
- package/dist/utils/error-handler-registry.d.ts +36 -0
- package/dist/utils/error-handler-registry.js +99 -12
- package/dist/utils/error-handler-registry.js.map +1 -1
- package/dist/utils/error-handling-utils.js +4 -3
- package/dist/utils/error-handling-utils.js.map +1 -1
- package/dist/utils/log-helpers.d.ts +6 -0
- package/dist/utils/log-helpers.js +90 -0
- package/dist/utils/log-helpers.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +55 -2
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/snapshot-writer.js +2 -6
- package/dist/utils/snapshot-writer.js.map +1 -1
- package/node_modules/@jsonstudio/llms/README.md +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/codecs/gemini-openai-codec.js +152 -6
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.d.ts +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/auto-thinking.js +25 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.d.ts +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/field-mapping.js +155 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.d.ts +17 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/gemini-web-search.js +68 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-image-content.js +83 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-tool-extraction.js +264 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.d.ts +11 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-vision-prompt.js +177 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/glm-web-search.js +63 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.d.ts +3 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwen-transform.js +209 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.d.ts +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/request-rules.js +63 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.d.ts +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-blacklist.js +85 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.d.ts +5 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-normalize.js +121 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.d.ts +5 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/response-validate.js +76 -0
- package/{dist/providers/compat/utils/snapshot-writer.d.ts → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.d.ts} +2 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/snapshot.js +21 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.d.ts +6 -0
- package/{dist/providers/compat/glm/utils/tool-schema-helpers.js → node_modules/@jsonstudio/llms/dist/conversion/compat/actions/tool-schema.js} +6 -1
- package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.d.ts +17 -22
- package/{dist/providers/compat/filters → node_modules/@jsonstudio/llms/dist/conversion/compat/actions}/universal-shape-filter.js +46 -99
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-gemini.json +17 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-glm.json +196 -13
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +194 -26
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +43 -35
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +20 -16
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/responses-c4m.json +42 -42
- package/node_modules/@jsonstudio/llms/dist/conversion/config/sample-config.json +1 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.d.ts +7 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-engine.js +5 -665
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.d.ts +9 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +869 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +55 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +74 -5
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +9 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process.js +213 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.d.ts +34 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +84 -24
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/response-runtime.js +19 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.d.ts +26 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/server-side-tools.js +383 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/gemini-mapper.js +241 -14
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/semantic-mappers/responses-mapper.js +17 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/standardized-bridge.js +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/types/standardized.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.d.ts +6 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-host-policy.js +14 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +133 -5
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +98 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/bridge-message-utils.js +137 -10
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-output-builder.js +43 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.d.ts +4 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-reasoning-registry.js +62 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/responses-response-utils.js +23 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/snapshot-utils.js +17 -47
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-canonicalizer.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +12 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-mapping.js +25 -2
- package/node_modules/@jsonstudio/llms/dist/index.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/index.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/bootstrap.js +540 -36
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +12 -11
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +19 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +64 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +26 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +450 -54
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +23 -458
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/health-manager.js +2 -7
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.d.ts +7 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +78 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +7 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +14 -3
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-estimator.js +16 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.d.ts +15 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-file-scanner.js +56 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.d.ts +13 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/tool-signals.js +403 -0
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +86 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +3 -1
- package/node_modules/@jsonstudio/llms/dist/servertool/engine.d.ts +27 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +60 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.d.ts +40 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/flow-types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +194 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/web-search.js +638 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.d.ts +33 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/orchestration-types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/registry.d.ts +18 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/registry.js +27 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.d.ts +8 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +208 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +88 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/types.js +1 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.d.ts +2 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/vision-tool.js +185 -0
- package/node_modules/@jsonstudio/llms/dist/sse/json-to-sse/event-generators/responses.js +15 -3
- package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/builders/response-builder.js +6 -3
- package/node_modules/@jsonstudio/llms/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +27 -1
- package/node_modules/@jsonstudio/llms/dist/sse/types/gemini-types.d.ts +20 -1
- package/node_modules/@jsonstudio/llms/dist/sse/types/responses-types.js +1 -1
- package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.d.ts +73 -0
- package/node_modules/@jsonstudio/llms/dist/telemetry/stats-center.js +280 -0
- package/node_modules/@jsonstudio/llms/package.json +2 -2
- package/package.json +11 -10
- package/scripts/README.md +26 -12
- package/scripts/auth-antigravity-token.mjs +64 -0
- package/scripts/auth-gemini-cli-token.mjs +96 -0
- package/scripts/auth-iflow-manual.mjs +81 -0
- package/scripts/auth-iflow-token-direct.mjs +87 -0
- package/scripts/auth-iflow-token.mjs +77 -0
- package/scripts/copy-compat-assets.mjs +3 -15
- package/scripts/install-verify.mjs +1 -0
- package/scripts/pack-mode.mjs +2 -1
- package/scripts/publish-rcc.mjs +20 -4
- package/scripts/replay-codex-sample.mjs +13 -8
- package/scripts/tests/chat-pipeline-blackbox.mjs +1 -1
- package/scripts/tests/virtual-router-health.mjs +141 -6
- package/scripts/tools/capture-provider-goldens.mjs +8 -7
- package/scripts/verify-client-headers.mjs +224 -0
- package/dist/providers/compat/base-compatibility.d.ts +0 -27
- package/dist/providers/compat/base-compatibility.js +0 -143
- package/dist/providers/compat/base-compatibility.js.map +0 -1
- package/dist/providers/compat/compat-directory-loader.d.ts +0 -4
- package/dist/providers/compat/compat-directory-loader.js +0 -85
- package/dist/providers/compat/compat-directory-loader.js.map +0 -1
- package/dist/providers/compat/compatibility-adapter.d.ts +0 -18
- package/dist/providers/compat/compatibility-adapter.js +0 -104
- package/dist/providers/compat/compatibility-adapter.js.map +0 -1
- package/dist/providers/compat/compatibility-factory.d.ts +0 -57
- package/dist/providers/compat/compatibility-factory.js +0 -155
- package/dist/providers/compat/compatibility-factory.js.map +0 -1
- package/dist/providers/compat/compatibility-interface.d.ts +0 -35
- package/dist/providers/compat/compatibility-interface.js +0 -2
- package/dist/providers/compat/compatibility-interface.js.map +0 -1
- package/dist/providers/compat/compatibility-manager.d.ts +0 -85
- package/dist/providers/compat/compatibility-manager.js +0 -368
- package/dist/providers/compat/compatibility-manager.js.map +0 -1
- package/dist/providers/compat/config/config-compatibility.d.ts +0 -28
- package/dist/providers/compat/config/config-compatibility.js +0 -95
- package/dist/providers/compat/config/config-compatibility.js.map +0 -1
- package/dist/providers/compat/field-mapping.d.ts +0 -102
- package/dist/providers/compat/field-mapping.js +0 -447
- package/dist/providers/compat/field-mapping.js.map +0 -1
- package/dist/providers/compat/filters/blacklist-sanitizer.d.ts +0 -45
- package/dist/providers/compat/filters/blacklist-sanitizer.js +0 -133
- package/dist/providers/compat/filters/blacklist-sanitizer.js.map +0 -1
- package/dist/providers/compat/filters/response-blacklist-sanitizer.d.ts +0 -28
- package/dist/providers/compat/filters/response-blacklist-sanitizer.js +0 -138
- package/dist/providers/compat/filters/response-blacklist-sanitizer.js.map +0 -1
- package/dist/providers/compat/filters/universal-shape-filter.js.map +0 -1
- package/dist/providers/compat/glm/config/blacklist-rules.json +0 -22
- package/dist/providers/compat/glm/config/field-mappings.json +0 -92
- package/dist/providers/compat/glm/config/response-blacklist.json +0 -7
- package/dist/providers/compat/glm/config/shape-filters.json +0 -37
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.d.ts +0 -28
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js +0 -306
- package/dist/providers/compat/glm/field-mapping/field-mapping-processor.js.map +0 -1
- package/dist/providers/compat/glm/functions/glm-processor.d.ts +0 -50
- package/dist/providers/compat/glm/functions/glm-processor.js +0 -134
- package/dist/providers/compat/glm/functions/glm-processor.js.map +0 -1
- package/dist/providers/compat/glm/glm-compatibility.d.ts +0 -34
- package/dist/providers/compat/glm/glm-compatibility.js +0 -117
- package/dist/providers/compat/glm/glm-compatibility.js.map +0 -1
- package/dist/providers/compat/glm/hooks/base-hook.d.ts +0 -21
- package/dist/providers/compat/glm/hooks/base-hook.js +0 -53
- package/dist/providers/compat/glm/hooks/base-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.d.ts +0 -24
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js +0 -268
- package/dist/providers/compat/glm/hooks/glm-request-validation-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.d.ts +0 -21
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js +0 -171
- package/dist/providers/compat/glm/hooks/glm-response-normalization-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.d.ts +0 -25
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js +0 -236
- package/dist/providers/compat/glm/hooks/glm-response-validation-hook.js.map +0 -1
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.d.ts +0 -26
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js +0 -186
- package/dist/providers/compat/glm/hooks/glm-tool-cleaning-hook.js.map +0 -1
- package/dist/providers/compat/glm/index.d.ts +0 -24
- package/dist/providers/compat/glm/index.js +0 -29
- package/dist/providers/compat/glm/index.js.map +0 -1
- package/dist/providers/compat/glm/utils/tool-schema-helpers.d.ts +0 -3
- package/dist/providers/compat/glm/utils/tool-schema-helpers.js.map +0 -1
- package/dist/providers/compat/iflow/config/field-mappings.json +0 -92
- package/dist/providers/compat/iflow/config/shape-filters.json +0 -37
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.d.ts +0 -34
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js +0 -386
- package/dist/providers/compat/iflow/field-mapping/iflow-field-mapping-processor.js.map +0 -1
- package/dist/providers/compat/iflow/functions/iflow-processor.d.ts +0 -53
- package/dist/providers/compat/iflow/functions/iflow-processor.js +0 -215
- package/dist/providers/compat/iflow/functions/iflow-processor.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/base-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/base-hook.js +0 -59
- package/dist/providers/compat/iflow/hooks/base-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js +0 -279
- package/dist/providers/compat/iflow/hooks/iflow-request-validation-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.d.ts +0 -20
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js +0 -180
- package/dist/providers/compat/iflow/hooks/iflow-response-normalization-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.d.ts +0 -23
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js +0 -232
- package/dist/providers/compat/iflow/hooks/iflow-response-validation-hook.js.map +0 -1
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.d.ts +0 -25
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js +0 -216
- package/dist/providers/compat/iflow/hooks/iflow-tool-cleaning-hook.js.map +0 -1
- package/dist/providers/compat/iflow/iflow-compatibility.d.ts +0 -24
- package/dist/providers/compat/iflow/iflow-compatibility.js +0 -94
- package/dist/providers/compat/iflow/iflow-compatibility.js.map +0 -1
- package/dist/providers/compat/index.d.ts +0 -59
- package/dist/providers/compat/index.js +0 -83
- package/dist/providers/compat/index.js.map +0 -1
- package/dist/providers/compat/lmstudio-compatibility.d.ts +0 -44
- package/dist/providers/compat/lmstudio-compatibility.js +0 -193
- package/dist/providers/compat/lmstudio-compatibility.js.map +0 -1
- package/dist/providers/compat/passthrough-compatibility.d.ts +0 -29
- package/dist/providers/compat/passthrough-compatibility.js +0 -83
- package/dist/providers/compat/passthrough-compatibility.js.map +0 -1
- package/dist/providers/compat/profiles/chat/glm/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/glm/index.js +0 -6
- package/dist/providers/compat/profiles/chat/glm/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/iflow/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/iflow/index.js +0 -6
- package/dist/providers/compat/profiles/chat/iflow/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/lmstudio/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/lmstudio/index.js +0 -6
- package/dist/providers/compat/profiles/chat/lmstudio/index.js.map +0 -1
- package/dist/providers/compat/profiles/chat/qwen/index.d.ts +0 -6
- package/dist/providers/compat/profiles/chat/qwen/index.js +0 -6
- package/dist/providers/compat/profiles/chat/qwen/index.js.map +0 -1
- package/dist/providers/compat/profiles/compat/passthrough/index.d.ts +0 -6
- package/dist/providers/compat/profiles/compat/passthrough/index.js +0 -6
- package/dist/providers/compat/profiles/compat/passthrough/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/c4m/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/c4m/index.js +0 -6
- package/dist/providers/compat/profiles/responses/c4m/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/default/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/default/index.js +0 -6
- package/dist/providers/compat/profiles/responses/default/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/fai/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/fai/index.js +0 -6
- package/dist/providers/compat/profiles/responses/fai/index.js.map +0 -1
- package/dist/providers/compat/profiles/responses/fc/index.d.ts +0 -6
- package/dist/providers/compat/profiles/responses/fc/index.js +0 -6
- package/dist/providers/compat/profiles/responses/fc/index.js.map +0 -1
- package/dist/providers/compat/qwen/index.d.ts +0 -4
- package/dist/providers/compat/qwen/index.js +0 -6
- package/dist/providers/compat/qwen/index.js.map +0 -1
- package/dist/providers/compat/qwen-compatibility.d.ts +0 -52
- package/dist/providers/compat/qwen-compatibility.js +0 -330
- package/dist/providers/compat/qwen-compatibility.js.map +0 -1
- package/dist/providers/compat/register-compat-module.d.ts +0 -8
- package/dist/providers/compat/register-compat-module.js +0 -53
- package/dist/providers/compat/register-compat-module.js.map +0 -1
- package/dist/providers/compat/responses/c4m-responses-compatibility.d.ts +0 -27
- package/dist/providers/compat/responses/c4m-responses-compatibility.js +0 -197
- package/dist/providers/compat/responses/c4m-responses-compatibility.js.map +0 -1
- package/dist/providers/compat/standard-compatibility-utils.d.ts +0 -1
- package/dist/providers/compat/standard-compatibility-utils.js +0 -77
- package/dist/providers/compat/standard-compatibility-utils.js.map +0 -1
- package/dist/providers/compat/standard-compatibility.d.ts +0 -31
- package/dist/providers/compat/standard-compatibility.js +0 -118
- package/dist/providers/compat/standard-compatibility.js.map +0 -1
- package/dist/providers/compat/utils/snapshot-writer.js +0 -62
- package/dist/providers/compat/utils/snapshot-writer.js.map +0 -1
- package/dist/tools/replay-request.d.ts +0 -0
- package/dist/tools/replay-request.js +0 -2
- package/dist/tools/replay-request.js.map +0 -1
- package/scripts/check-glm-compat.mjs +0 -47
|
@@ -36,6 +36,7 @@ export class HubRequestExecutor {
|
|
|
36
36
|
}
|
|
37
37
|
async execute(input) {
|
|
38
38
|
this.deps.stats.recordRequestStart(input.requestId);
|
|
39
|
+
const requestStartedAt = Date.now();
|
|
39
40
|
let statsRecorded = false;
|
|
40
41
|
const finalizeStats = (options) => {
|
|
41
42
|
if (statsRecorded) {
|
|
@@ -46,12 +47,13 @@ export class HubRequestExecutor {
|
|
|
46
47
|
};
|
|
47
48
|
try {
|
|
48
49
|
const hubPipeline = this.ensureHubPipeline();
|
|
49
|
-
const
|
|
50
|
+
const initialMetadata = this.buildRequestMetadata(input);
|
|
51
|
+
const inboundClientHeaders = this.cloneClientHeaders(initialMetadata?.clientHeaders);
|
|
50
52
|
const providerRequestId = input.requestId;
|
|
51
|
-
const clientRequestId = this.resolveClientRequestId(
|
|
53
|
+
const clientRequestId = this.resolveClientRequestId(initialMetadata, providerRequestId);
|
|
52
54
|
this.logStage('request.received', providerRequestId, {
|
|
53
55
|
endpoint: input.entryEndpoint,
|
|
54
|
-
stream:
|
|
56
|
+
stream: initialMetadata.stream === true
|
|
55
57
|
});
|
|
56
58
|
try {
|
|
57
59
|
const headerUa = (typeof input.headers?.['user-agent'] === 'string' && input.headers['user-agent']) ||
|
|
@@ -64,7 +66,7 @@ export class HubRequestExecutor {
|
|
|
64
66
|
headers: asRecord(input.headers),
|
|
65
67
|
body: input.body,
|
|
66
68
|
metadata: {
|
|
67
|
-
...
|
|
69
|
+
...initialMetadata,
|
|
68
70
|
userAgent: headerUa,
|
|
69
71
|
clientOriginator: headerOriginator
|
|
70
72
|
}
|
|
@@ -74,158 +76,187 @@ export class HubRequestExecutor {
|
|
|
74
76
|
/* snapshot failure should not block request path */
|
|
75
77
|
}
|
|
76
78
|
const pipelineLabel = 'hub';
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
route: pipelineResult.routingDecision?.routeName,
|
|
87
|
-
target: pipelineResult.target?.providerKey
|
|
88
|
-
});
|
|
89
|
-
const providerPayload = pipelineResult.providerPayload;
|
|
90
|
-
const target = pipelineResult.target;
|
|
91
|
-
if (!providerPayload || !target?.providerKey) {
|
|
92
|
-
throw Object.assign(new Error('Virtual router did not produce a provider target'), {
|
|
93
|
-
code: 'ERR_NO_PROVIDER_TARGET',
|
|
94
|
-
requestId: input.requestId
|
|
79
|
+
let iterationMetadata = initialMetadata;
|
|
80
|
+
let aggregatedUsage;
|
|
81
|
+
let attempt = 0;
|
|
82
|
+
let followupTriggered = false;
|
|
83
|
+
while (true) {
|
|
84
|
+
this.logStage(`${pipelineLabel}.start`, providerRequestId, {
|
|
85
|
+
endpoint: input.entryEndpoint,
|
|
86
|
+
stream: iterationMetadata.stream,
|
|
87
|
+
attempt
|
|
95
88
|
});
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
89
|
+
// 获取原始请求快照,用于后续响应转换(不包含任何 server-side 工具语义)。
|
|
90
|
+
const originalRequestSnapshot = this.cloneRequestPayload(input.body);
|
|
91
|
+
const pipelineResult = await this.runHubPipeline(hubPipeline, input, iterationMetadata);
|
|
92
|
+
const pipelineMetadata = pipelineResult.metadata ?? {};
|
|
93
|
+
const mergedMetadata = { ...iterationMetadata, ...pipelineMetadata };
|
|
94
|
+
const mergedClientHeaders = this.cloneClientHeaders(mergedMetadata?.clientHeaders) || inboundClientHeaders;
|
|
95
|
+
if (mergedClientHeaders) {
|
|
96
|
+
mergedMetadata.clientHeaders = mergedClientHeaders;
|
|
97
|
+
}
|
|
98
|
+
mergedMetadata.clientRequestId = clientRequestId;
|
|
99
|
+
this.logStage(`${pipelineLabel}.completed`, providerRequestId, {
|
|
100
|
+
route: pipelineResult.routingDecision?.routeName,
|
|
101
|
+
target: pipelineResult.target?.providerKey,
|
|
102
|
+
attempt
|
|
109
103
|
});
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
104
|
+
const providerPayload = pipelineResult.providerPayload;
|
|
105
|
+
const target = pipelineResult.target;
|
|
106
|
+
if (!providerPayload || !target?.providerKey) {
|
|
107
|
+
throw Object.assign(new Error('Virtual router did not produce a provider target'), {
|
|
108
|
+
code: 'ERR_NO_PROVIDER_TARGET',
|
|
109
|
+
requestId: input.requestId
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const runtimeKey = target.runtimeKey || this.deps.runtimeManager.resolveRuntimeKey(target.providerKey);
|
|
113
|
+
if (!runtimeKey) {
|
|
114
|
+
throw Object.assign(new Error(`Runtime for provider ${target.providerKey} not initialized`), {
|
|
115
|
+
code: 'ERR_RUNTIME_NOT_FOUND',
|
|
116
|
+
requestId: input.requestId
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const handle = this.deps.runtimeManager.getHandleByRuntimeKey(runtimeKey);
|
|
120
|
+
if (!handle) {
|
|
121
|
+
throw Object.assign(new Error(`Provider runtime ${runtimeKey} not found`), {
|
|
122
|
+
code: 'ERR_PROVIDER_NOT_FOUND',
|
|
123
|
+
requestId: input.requestId
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const providerProtocol = target.outboundProfile || handle.providerProtocol;
|
|
127
|
+
const metadataModel = mergedMetadata?.target && typeof mergedMetadata.target === 'object'
|
|
128
|
+
? mergedMetadata.target.clientModelId
|
|
129
|
+
: undefined;
|
|
130
|
+
const rawModel = this.extractProviderModel(providerPayload) ||
|
|
131
|
+
(typeof metadataModel === 'string' ? metadataModel : undefined);
|
|
132
|
+
const providerAlias = typeof target.providerKey === 'string' && target.providerKey.includes('.')
|
|
133
|
+
? target.providerKey.split('.').slice(0, 2).join('.')
|
|
134
|
+
: target.providerKey;
|
|
135
|
+
const providerIdToken = providerAlias || handle.providerId || runtimeKey;
|
|
136
|
+
if (!providerIdToken) {
|
|
137
|
+
throw Object.assign(new Error('Provider identifier missing for request'), {
|
|
138
|
+
code: 'ERR_PROVIDER_ID_MISSING',
|
|
139
|
+
requestId: providerRequestId
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
const enhancedRequestId = enhanceProviderRequestId(providerRequestId, {
|
|
143
|
+
entryEndpoint: input.entryEndpoint,
|
|
144
|
+
providerId: providerIdToken,
|
|
145
|
+
model: rawModel
|
|
125
146
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const providerModel = rawModel;
|
|
137
|
-
const providerLabel = this.buildProviderLabel(target.providerKey, providerModel);
|
|
138
|
-
this.deps.stats.bindProvider(input.requestId, {
|
|
139
|
-
providerKey: target.providerKey,
|
|
140
|
-
providerType: handle.providerType,
|
|
141
|
-
model: providerModel
|
|
142
|
-
});
|
|
143
|
-
this.logStage('provider.prepare', input.requestId, {
|
|
144
|
-
providerKey: target.providerKey,
|
|
145
|
-
runtimeKey,
|
|
146
|
-
protocol: providerProtocol,
|
|
147
|
-
providerType: handle.providerType,
|
|
148
|
-
providerFamily: handle.providerFamily,
|
|
149
|
-
model: providerModel,
|
|
150
|
-
providerLabel
|
|
151
|
-
});
|
|
152
|
-
attachProviderRuntimeMetadata(providerPayload, {
|
|
153
|
-
requestId: input.requestId,
|
|
154
|
-
providerId: handle.providerId,
|
|
155
|
-
providerKey: target.providerKey,
|
|
156
|
-
providerType: handle.providerType,
|
|
157
|
-
providerFamily: handle.providerFamily,
|
|
158
|
-
providerProtocol,
|
|
159
|
-
pipelineId: target.providerKey,
|
|
160
|
-
routeName: pipelineResult.routingDecision?.routeName,
|
|
161
|
-
runtimeKey,
|
|
162
|
-
target,
|
|
163
|
-
metadata: mergedMetadata,
|
|
164
|
-
compatibilityProfile: target.compatibilityProfile
|
|
165
|
-
});
|
|
166
|
-
this.logStage('provider.send.start', input.requestId, {
|
|
167
|
-
providerKey: target.providerKey,
|
|
168
|
-
runtimeKey,
|
|
169
|
-
protocol: providerProtocol,
|
|
170
|
-
providerType: handle.providerType,
|
|
171
|
-
providerFamily: handle.providerFamily,
|
|
172
|
-
model: providerModel,
|
|
173
|
-
providerLabel
|
|
174
|
-
});
|
|
175
|
-
try {
|
|
176
|
-
const providerResponse = await handle.instance.processIncoming(providerPayload);
|
|
177
|
-
const responseStatus = this.extractResponseStatus(providerResponse);
|
|
178
|
-
this.logStage('provider.send.completed', input.requestId, {
|
|
147
|
+
if (enhancedRequestId !== input.requestId) {
|
|
148
|
+
input.requestId = enhancedRequestId;
|
|
149
|
+
}
|
|
150
|
+
mergedMetadata.clientRequestId = clientRequestId;
|
|
151
|
+
const providerModel = rawModel;
|
|
152
|
+
const providerLabel = this.buildProviderLabel(target.providerKey, providerModel);
|
|
153
|
+
if (inboundClientHeaders) {
|
|
154
|
+
this.ensureClientHeadersOnPayload(providerPayload, inboundClientHeaders);
|
|
155
|
+
}
|
|
156
|
+
this.deps.stats.bindProvider(input.requestId, {
|
|
179
157
|
providerKey: target.providerKey,
|
|
180
|
-
status: responseStatus,
|
|
181
158
|
providerType: handle.providerType,
|
|
182
|
-
|
|
183
|
-
model: providerModel,
|
|
184
|
-
providerLabel
|
|
159
|
+
model: providerModel
|
|
185
160
|
});
|
|
186
|
-
|
|
187
|
-
const converted = await this.convertProviderResponseIfNeeded({
|
|
188
|
-
entryEndpoint: input.entryEndpoint,
|
|
189
|
-
providerType: handle.providerType,
|
|
190
|
-
requestId: input.requestId,
|
|
191
|
-
wantsStream: Boolean(input.metadata?.inboundStream ?? input.metadata?.stream),
|
|
192
|
-
originalRequest: originalRequestSnapshot,
|
|
193
|
-
processMode: pipelineResult.processMode,
|
|
194
|
-
response: normalized,
|
|
195
|
-
pipelineMetadata: mergedMetadata
|
|
196
|
-
});
|
|
197
|
-
const usage = this.extractUsageFromResult(converted, mergedMetadata);
|
|
198
|
-
finalizeStats({ usage, error: false });
|
|
199
|
-
return converted;
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
this.logStage('provider.send.error', input.requestId, {
|
|
161
|
+
this.logStage('provider.prepare', input.requestId, {
|
|
203
162
|
providerKey: target.providerKey,
|
|
204
|
-
|
|
163
|
+
runtimeKey,
|
|
164
|
+
protocol: providerProtocol,
|
|
205
165
|
providerType: handle.providerType,
|
|
206
166
|
providerFamily: handle.providerFamily,
|
|
207
167
|
model: providerModel,
|
|
208
|
-
providerLabel
|
|
168
|
+
providerLabel,
|
|
169
|
+
attempt
|
|
209
170
|
});
|
|
210
|
-
|
|
171
|
+
attachProviderRuntimeMetadata(providerPayload, {
|
|
211
172
|
requestId: input.requestId,
|
|
212
|
-
providerKey: target.providerKey,
|
|
213
173
|
providerId: handle.providerId,
|
|
174
|
+
providerKey: target.providerKey,
|
|
214
175
|
providerType: handle.providerType,
|
|
176
|
+
providerFamily: handle.providerFamily,
|
|
215
177
|
providerProtocol,
|
|
216
|
-
routeName: pipelineResult.routingDecision?.routeName,
|
|
217
178
|
pipelineId: target.providerKey,
|
|
179
|
+
routeName: pipelineResult.routingDecision?.routeName,
|
|
218
180
|
runtimeKey,
|
|
219
|
-
target
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
emitProviderError({
|
|
223
|
-
error,
|
|
224
|
-
stage: 'provider.send',
|
|
225
|
-
runtime: runtimeMetadata,
|
|
226
|
-
dependencies: this.deps.getModuleDependencies()
|
|
181
|
+
target,
|
|
182
|
+
metadata: mergedMetadata,
|
|
183
|
+
compatibilityProfile: target.compatibilityProfile
|
|
227
184
|
});
|
|
228
|
-
|
|
185
|
+
this.logStage('provider.send.start', input.requestId, {
|
|
186
|
+
providerKey: target.providerKey,
|
|
187
|
+
runtimeKey,
|
|
188
|
+
protocol: providerProtocol,
|
|
189
|
+
providerType: handle.providerType,
|
|
190
|
+
providerFamily: handle.providerFamily,
|
|
191
|
+
model: providerModel,
|
|
192
|
+
providerLabel,
|
|
193
|
+
attempt
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
const providerResponse = await handle.instance.processIncoming(providerPayload);
|
|
197
|
+
const responseStatus = this.extractResponseStatus(providerResponse);
|
|
198
|
+
this.logStage('provider.send.completed', input.requestId, {
|
|
199
|
+
providerKey: target.providerKey,
|
|
200
|
+
status: responseStatus,
|
|
201
|
+
providerType: handle.providerType,
|
|
202
|
+
providerFamily: handle.providerFamily,
|
|
203
|
+
model: providerModel,
|
|
204
|
+
providerLabel,
|
|
205
|
+
attempt
|
|
206
|
+
});
|
|
207
|
+
const wantsStreamBase = Boolean(input.metadata?.inboundStream ?? input.metadata?.stream);
|
|
208
|
+
const normalized = this.normalizeProviderResponse(providerResponse);
|
|
209
|
+
const converted = await this.convertProviderResponseIfNeeded({
|
|
210
|
+
entryEndpoint: input.entryEndpoint,
|
|
211
|
+
providerType: handle.providerType,
|
|
212
|
+
requestId: input.requestId,
|
|
213
|
+
wantsStream: wantsStreamBase,
|
|
214
|
+
originalRequest: originalRequestSnapshot,
|
|
215
|
+
processMode: pipelineResult.processMode,
|
|
216
|
+
response: normalized,
|
|
217
|
+
pipelineMetadata: mergedMetadata
|
|
218
|
+
});
|
|
219
|
+
const usage = this.extractUsageFromResult(converted, mergedMetadata);
|
|
220
|
+
aggregatedUsage = this.mergeUsageMetrics(aggregatedUsage, usage);
|
|
221
|
+
finalizeStats({ usage: aggregatedUsage, error: false });
|
|
222
|
+
this.logUsageSummary(input.requestId, {
|
|
223
|
+
providerKey: target.providerKey,
|
|
224
|
+
model: providerModel,
|
|
225
|
+
usage: aggregatedUsage,
|
|
226
|
+
latencyMs: Date.now() - requestStartedAt
|
|
227
|
+
});
|
|
228
|
+
return converted;
|
|
229
|
+
}
|
|
230
|
+
catch (error) {
|
|
231
|
+
this.logStage('provider.send.error', input.requestId, {
|
|
232
|
+
providerKey: target.providerKey,
|
|
233
|
+
message: error instanceof Error ? error.message : String(error ?? 'Unknown error'),
|
|
234
|
+
providerType: handle.providerType,
|
|
235
|
+
providerFamily: handle.providerFamily,
|
|
236
|
+
model: providerModel,
|
|
237
|
+
providerLabel,
|
|
238
|
+
attempt
|
|
239
|
+
});
|
|
240
|
+
const runtimeMetadata = {
|
|
241
|
+
requestId: input.requestId,
|
|
242
|
+
providerKey: target.providerKey,
|
|
243
|
+
providerId: handle.providerId,
|
|
244
|
+
providerType: handle.providerType,
|
|
245
|
+
providerProtocol,
|
|
246
|
+
routeName: pipelineResult.routingDecision?.routeName,
|
|
247
|
+
pipelineId: target.providerKey,
|
|
248
|
+
runtimeKey,
|
|
249
|
+
target
|
|
250
|
+
};
|
|
251
|
+
runtimeMetadata.providerFamily = handle.providerFamily;
|
|
252
|
+
emitProviderError({
|
|
253
|
+
error,
|
|
254
|
+
stage: 'provider.send',
|
|
255
|
+
runtime: runtimeMetadata,
|
|
256
|
+
dependencies: this.deps.getModuleDependencies()
|
|
257
|
+
});
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
229
260
|
}
|
|
230
261
|
}
|
|
231
262
|
catch (error) {
|
|
@@ -265,6 +296,31 @@ export class HubRequestExecutor {
|
|
|
265
296
|
metadata: result.metadata ?? {}
|
|
266
297
|
};
|
|
267
298
|
}
|
|
299
|
+
cloneClientHeaders(source) {
|
|
300
|
+
if (!source || typeof source !== 'object') {
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
const normalized = {};
|
|
304
|
+
for (const [key, value] of Object.entries(source)) {
|
|
305
|
+
if (typeof value === 'string' && value.trim()) {
|
|
306
|
+
normalized[key] = value;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return Object.keys(normalized).length ? normalized : undefined;
|
|
310
|
+
}
|
|
311
|
+
ensureClientHeadersOnPayload(payload, headers) {
|
|
312
|
+
if (!payload || typeof payload !== 'object') {
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
const carrier = payload;
|
|
316
|
+
const existing = carrier.metadata && typeof carrier.metadata === 'object'
|
|
317
|
+
? carrier.metadata
|
|
318
|
+
: {};
|
|
319
|
+
carrier.metadata = {
|
|
320
|
+
...existing,
|
|
321
|
+
clientHeaders: existing.clientHeaders ?? headers
|
|
322
|
+
};
|
|
323
|
+
}
|
|
268
324
|
resolveClientRequestId(metadata, fallback) {
|
|
269
325
|
const clientRequestId = typeof metadata.clientRequestId === 'string' && metadata.clientRequestId.trim()
|
|
270
326
|
? metadata.clientRequestId.trim()
|
|
@@ -273,6 +329,15 @@ export class HubRequestExecutor {
|
|
|
273
329
|
}
|
|
274
330
|
buildRequestMetadata(input) {
|
|
275
331
|
const userMeta = asRecord(input.metadata);
|
|
332
|
+
const headers = asRecord(input.headers);
|
|
333
|
+
const inboundUserAgent = this.extractHeaderValue(headers, 'user-agent');
|
|
334
|
+
const inboundOriginator = this.extractHeaderValue(headers, 'originator');
|
|
335
|
+
const resolvedUserAgent = typeof userMeta.userAgent === 'string' && userMeta.userAgent.trim()
|
|
336
|
+
? userMeta.userAgent.trim()
|
|
337
|
+
: inboundUserAgent;
|
|
338
|
+
const resolvedOriginator = typeof userMeta.clientOriginator === 'string' && userMeta.clientOriginator.trim()
|
|
339
|
+
? userMeta.clientOriginator.trim()
|
|
340
|
+
: inboundOriginator;
|
|
276
341
|
const routeHint = this.extractRouteHint(input) ?? userMeta.routeHint;
|
|
277
342
|
const processMode = userMeta.processMode || 'chat';
|
|
278
343
|
return {
|
|
@@ -282,9 +347,30 @@ export class HubRequestExecutor {
|
|
|
282
347
|
direction: 'request',
|
|
283
348
|
stage: 'inbound',
|
|
284
349
|
routeHint,
|
|
285
|
-
stream: userMeta.stream === true
|
|
350
|
+
stream: userMeta.stream === true,
|
|
351
|
+
...(resolvedUserAgent ? { userAgent: resolvedUserAgent } : {}),
|
|
352
|
+
...(resolvedOriginator ? { clientOriginator: resolvedOriginator } : {})
|
|
286
353
|
};
|
|
287
354
|
}
|
|
355
|
+
extractHeaderValue(headers, name) {
|
|
356
|
+
if (!headers) {
|
|
357
|
+
return undefined;
|
|
358
|
+
}
|
|
359
|
+
const target = name.toLowerCase();
|
|
360
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
361
|
+
if (key.toLowerCase() !== target) {
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
if (typeof value === 'string') {
|
|
365
|
+
return value.trim() || undefined;
|
|
366
|
+
}
|
|
367
|
+
if (Array.isArray(value) && value.length) {
|
|
368
|
+
return String(value[0]).trim() || undefined;
|
|
369
|
+
}
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
373
|
+
}
|
|
288
374
|
extractRouteHint(input) {
|
|
289
375
|
const header = input.headers?.['x-route-hint'];
|
|
290
376
|
if (typeof header === 'string' && header.trim()) {
|
|
@@ -342,12 +428,17 @@ export class HubRequestExecutor {
|
|
|
342
428
|
const metadataBag = asRecord(options.pipelineMetadata);
|
|
343
429
|
const aliasMap = extractAnthropicToolAliasMap(metadataBag);
|
|
344
430
|
const originalModelId = this.extractClientModelId(metadataBag, options.originalRequest);
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
entryEndpoint: options.entryEndpoint || entry,
|
|
348
|
-
providerProtocol,
|
|
349
|
-
originalModelId
|
|
431
|
+
const baseContext = {
|
|
432
|
+
...(metadataBag ?? {})
|
|
350
433
|
};
|
|
434
|
+
if (typeof metadataBag?.routeName === 'string') {
|
|
435
|
+
baseContext.routeId = metadataBag.routeName;
|
|
436
|
+
}
|
|
437
|
+
baseContext.requestId = options.requestId;
|
|
438
|
+
baseContext.entryEndpoint = options.entryEndpoint || entry;
|
|
439
|
+
baseContext.providerProtocol = providerProtocol;
|
|
440
|
+
baseContext.originalModelId = originalModelId;
|
|
441
|
+
const adapterContext = baseContext;
|
|
351
442
|
if (aliasMap) {
|
|
352
443
|
adapterContext.anthropicToolNameMap = aliasMap;
|
|
353
444
|
}
|
|
@@ -355,14 +446,85 @@ export class HubRequestExecutor {
|
|
|
355
446
|
loadConvertProviderResponse(),
|
|
356
447
|
loadSnapshotRecorderFactory()
|
|
357
448
|
]);
|
|
358
|
-
const stageRecorder = createSnapshotRecorder(adapterContext, adapterContext.entryEndpoint
|
|
449
|
+
const stageRecorder = createSnapshotRecorder(adapterContext, typeof adapterContext.entryEndpoint === 'string'
|
|
450
|
+
? adapterContext.entryEndpoint
|
|
451
|
+
: options.entryEndpoint || entry);
|
|
452
|
+
const providerInvoker = async (invokeOptions) => {
|
|
453
|
+
// 将 server-side 工具的 routeHint 注入到内部 payload 的 metadata,
|
|
454
|
+
// 以便后续在标准 HubPipeline 中保持路由上下文一致(例如强制 web_search)。
|
|
455
|
+
if (invokeOptions.routeHint) {
|
|
456
|
+
const carrier = invokeOptions.payload;
|
|
457
|
+
const existingMeta = carrier.metadata && typeof carrier.metadata === 'object'
|
|
458
|
+
? carrier.metadata
|
|
459
|
+
: {};
|
|
460
|
+
carrier.metadata = {
|
|
461
|
+
...existingMeta,
|
|
462
|
+
routeHint: existingMeta.routeHint ?? invokeOptions.routeHint
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
// Delegate to existing runtimeManager / Provider V2 stack.
|
|
466
|
+
const runtimeKey = this.deps.runtimeManager.resolveRuntimeKey(invokeOptions.providerKey);
|
|
467
|
+
if (!runtimeKey) {
|
|
468
|
+
throw new Error(`Runtime for provider ${invokeOptions.providerKey} not initialized`);
|
|
469
|
+
}
|
|
470
|
+
const handle = this.deps.runtimeManager.getHandleByRuntimeKey(runtimeKey);
|
|
471
|
+
if (!handle) {
|
|
472
|
+
throw new Error(`Provider runtime ${runtimeKey} not found`);
|
|
473
|
+
}
|
|
474
|
+
const providerResponse = await handle.instance.processIncoming(invokeOptions.payload);
|
|
475
|
+
const normalized = this.normalizeProviderResponse(providerResponse);
|
|
476
|
+
const bodyPayload = normalized.body && typeof normalized.body === 'object'
|
|
477
|
+
? normalized.body
|
|
478
|
+
: normalized;
|
|
479
|
+
return { providerResponse: bodyPayload };
|
|
480
|
+
};
|
|
481
|
+
const reenterPipeline = async (reenterOpts) => {
|
|
482
|
+
const nestedEntry = reenterOpts.entryEndpoint || options.entryEndpoint || entry;
|
|
483
|
+
const nestedExtra = asRecord(reenterOpts.metadata) ?? {};
|
|
484
|
+
const nestedEntryLower = nestedEntry.toLowerCase();
|
|
485
|
+
// 基于首次 HubPipeline metadata + 调用方注入的 metadata 构建新的请求 metadata。
|
|
486
|
+
// 不在 Host 层编码 servertool/web_search 等语义,由 llmswitch-core 负责。
|
|
487
|
+
const nestedMetadata = {
|
|
488
|
+
...(metadataBag ?? {}),
|
|
489
|
+
...nestedExtra,
|
|
490
|
+
entryEndpoint: nestedEntry,
|
|
491
|
+
direction: 'request',
|
|
492
|
+
stage: 'inbound'
|
|
493
|
+
};
|
|
494
|
+
// 针对 reenterPipeline 的入口端点,纠正 providerProtocol,避免沿用外层协议。
|
|
495
|
+
if (nestedEntryLower.includes('/v1/chat/completions')) {
|
|
496
|
+
nestedMetadata.providerProtocol = 'openai-chat';
|
|
497
|
+
}
|
|
498
|
+
else if (nestedEntryLower.includes('/v1/responses')) {
|
|
499
|
+
nestedMetadata.providerProtocol = 'openai-responses';
|
|
500
|
+
}
|
|
501
|
+
else if (nestedEntryLower.includes('/v1/messages')) {
|
|
502
|
+
nestedMetadata.providerProtocol = 'anthropic-messages';
|
|
503
|
+
}
|
|
504
|
+
const nestedInput = {
|
|
505
|
+
entryEndpoint: nestedEntry,
|
|
506
|
+
method: 'POST',
|
|
507
|
+
requestId: reenterOpts.requestId,
|
|
508
|
+
headers: {},
|
|
509
|
+
query: {},
|
|
510
|
+
body: reenterOpts.body,
|
|
511
|
+
metadata: nestedMetadata
|
|
512
|
+
};
|
|
513
|
+
const nestedResult = await this.execute(nestedInput);
|
|
514
|
+
const nestedBody = nestedResult.body && typeof nestedResult.body === 'object'
|
|
515
|
+
? nestedResult.body
|
|
516
|
+
: undefined;
|
|
517
|
+
return { body: nestedBody };
|
|
518
|
+
};
|
|
359
519
|
const converted = await convertProviderResponse({
|
|
360
520
|
providerProtocol,
|
|
361
521
|
providerResponse: body,
|
|
362
522
|
context: adapterContext,
|
|
363
523
|
entryEndpoint: options.entryEndpoint || entry,
|
|
364
524
|
wantsStream: options.wantsStream,
|
|
365
|
-
|
|
525
|
+
providerInvoker,
|
|
526
|
+
stageRecorder,
|
|
527
|
+
reenterPipeline
|
|
366
528
|
});
|
|
367
529
|
if (converted.__sse_responses) {
|
|
368
530
|
return {
|
|
@@ -376,6 +538,20 @@ export class HubRequestExecutor {
|
|
|
376
538
|
};
|
|
377
539
|
}
|
|
378
540
|
catch (error) {
|
|
541
|
+
const err = error;
|
|
542
|
+
const message = err instanceof Error ? err.message : String(err ?? 'Unknown error');
|
|
543
|
+
const providerProtocol = mapProviderProtocol(options.providerType);
|
|
544
|
+
// 对于 Gemini 等基于 SSE 的 provider,如果 llmswitch-core 报告
|
|
545
|
+
// “Failed to convert SSE payload ...” 之类错误,说明上游流式响应已异常
|
|
546
|
+
// 终止(如 Cloud Code 终止/上下文超限),继续回退到原始 SSE 只会让
|
|
547
|
+
// 客户端挂起。因此在此类场景下直接抛出错误,让 HTTP 层返回明确的
|
|
548
|
+
// 5xx/4xx,而不是静默退回原始 payload。
|
|
549
|
+
const isSseConvertFailure = typeof message === 'string' &&
|
|
550
|
+
message.toLowerCase().includes('failed to convert sse payload');
|
|
551
|
+
if (providerProtocol === 'gemini-chat' && isSseConvertFailure) {
|
|
552
|
+
console.error('[RequestExecutor] Fatal SSE decode error for Gemini provider, bubbling as HTTP error', error);
|
|
553
|
+
throw error;
|
|
554
|
+
}
|
|
379
555
|
console.error('[RequestExecutor] Failed to convert provider response via llmswitch-core', error);
|
|
380
556
|
return options.response;
|
|
381
557
|
}
|
|
@@ -439,6 +615,18 @@ export class HubRequestExecutor {
|
|
|
439
615
|
logStage(stage, requestId, details) {
|
|
440
616
|
this.deps.logStage(stage, requestId, details);
|
|
441
617
|
}
|
|
618
|
+
logUsageSummary(requestId, info) {
|
|
619
|
+
if (process.env.ROUTECODEX_USAGE_LOG === '0') {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const providerLabel = this.buildProviderLabel(info.providerKey, info.model) ?? '-';
|
|
623
|
+
const prompt = info.usage?.prompt_tokens;
|
|
624
|
+
const completion = info.usage?.completion_tokens;
|
|
625
|
+
const total = info.usage?.total_tokens ?? (prompt !== undefined && completion !== undefined ? prompt + completion : undefined);
|
|
626
|
+
const usageText = `prompt=${prompt ?? 'n/a'} completion=${completion ?? 'n/a'} total=${total ?? 'n/a'}`;
|
|
627
|
+
const latency = info.latencyMs.toFixed(1);
|
|
628
|
+
console.log(`[usage] request ${requestId} provider=${providerLabel} latency=${latency}ms (${usageText})`);
|
|
629
|
+
}
|
|
442
630
|
extractUsageFromResult(result, metadata) {
|
|
443
631
|
const candidates = [];
|
|
444
632
|
if (metadata && typeof metadata === 'object') {
|
|
@@ -472,9 +660,22 @@ export class HubRequestExecutor {
|
|
|
472
660
|
return undefined;
|
|
473
661
|
}
|
|
474
662
|
const record = value;
|
|
475
|
-
const prompt = typeof record.prompt_tokens === 'number'
|
|
476
|
-
|
|
477
|
-
|
|
663
|
+
const prompt = typeof record.prompt_tokens === 'number'
|
|
664
|
+
? record.prompt_tokens
|
|
665
|
+
: typeof record.input_tokens === 'number'
|
|
666
|
+
? record.input_tokens
|
|
667
|
+
: undefined;
|
|
668
|
+
const completion = typeof record.completion_tokens === 'number'
|
|
669
|
+
? record.completion_tokens
|
|
670
|
+
: typeof record.output_tokens === 'number'
|
|
671
|
+
? record.output_tokens
|
|
672
|
+
: undefined;
|
|
673
|
+
let total = typeof record.total_tokens === 'number'
|
|
674
|
+
? record.total_tokens
|
|
675
|
+
: undefined;
|
|
676
|
+
if (total === undefined && prompt !== undefined && completion !== undefined) {
|
|
677
|
+
total = prompt + completion;
|
|
678
|
+
}
|
|
478
679
|
if (prompt === undefined && completion === undefined && total === undefined) {
|
|
479
680
|
return undefined;
|
|
480
681
|
}
|
|
@@ -484,6 +685,294 @@ export class HubRequestExecutor {
|
|
|
484
685
|
total_tokens: total
|
|
485
686
|
};
|
|
486
687
|
}
|
|
688
|
+
mergeUsageMetrics(base, delta) {
|
|
689
|
+
if (!delta) {
|
|
690
|
+
return base;
|
|
691
|
+
}
|
|
692
|
+
if (!base) {
|
|
693
|
+
return { ...delta };
|
|
694
|
+
}
|
|
695
|
+
const merged = {
|
|
696
|
+
prompt_tokens: (base.prompt_tokens ?? 0) + (delta.prompt_tokens ?? 0),
|
|
697
|
+
completion_tokens: (base.completion_tokens ?? 0) + (delta.completion_tokens ?? 0)
|
|
698
|
+
};
|
|
699
|
+
const total = (base.total_tokens ?? 0) + (delta.total_tokens ?? 0);
|
|
700
|
+
merged.total_tokens = total || undefined;
|
|
701
|
+
return merged;
|
|
702
|
+
}
|
|
703
|
+
buildVisionFollowupPayload(options) {
|
|
704
|
+
const { originalPayload, visionResponse } = options;
|
|
705
|
+
if (!originalPayload || typeof originalPayload !== 'object') {
|
|
706
|
+
return null;
|
|
707
|
+
}
|
|
708
|
+
const clone = this.cloneRequestPayload(originalPayload) ?? { ...originalPayload };
|
|
709
|
+
if (!clone) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
712
|
+
const visionText = this.extractVisionDescription(visionResponse?.body);
|
|
713
|
+
if (!visionText) {
|
|
714
|
+
return null;
|
|
715
|
+
}
|
|
716
|
+
if (this.rewriteResponsesInput(clone, visionText)) {
|
|
717
|
+
return clone;
|
|
718
|
+
}
|
|
719
|
+
if (this.rewriteChatMessages(clone, visionText)) {
|
|
720
|
+
return clone;
|
|
721
|
+
}
|
|
722
|
+
return null;
|
|
723
|
+
}
|
|
724
|
+
rewriteResponsesInput(payload, visionText) {
|
|
725
|
+
const inputList = payload.input;
|
|
726
|
+
if (!Array.isArray(inputList)) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
for (let i = inputList.length - 1; i >= 0; i -= 1) {
|
|
730
|
+
const item = inputList[i];
|
|
731
|
+
if (!item || typeof item !== 'object') {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const role = typeof item.role === 'string'
|
|
735
|
+
? item.role
|
|
736
|
+
: '';
|
|
737
|
+
if (role !== 'user') {
|
|
738
|
+
continue;
|
|
739
|
+
}
|
|
740
|
+
const contentBlocks = Array.isArray(item.content)
|
|
741
|
+
? [...item.content]
|
|
742
|
+
: [];
|
|
743
|
+
const originalText = this.extractTextFromContentBlocks(contentBlocks, ['input_text', 'text']);
|
|
744
|
+
const textType = this.detectContentTextType(contentBlocks, 'input_text');
|
|
745
|
+
const composed = this.composeVisionUserText(visionText, originalText);
|
|
746
|
+
item.content = [
|
|
747
|
+
{
|
|
748
|
+
type: textType,
|
|
749
|
+
text: composed
|
|
750
|
+
}
|
|
751
|
+
];
|
|
752
|
+
inputList[i] = item;
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
rewriteChatMessages(payload, visionText) {
|
|
758
|
+
const messages = payload.messages;
|
|
759
|
+
if (!Array.isArray(messages)) {
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
763
|
+
const message = messages[i];
|
|
764
|
+
if (!message || typeof message !== 'object') {
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
const role = typeof message.role === 'string'
|
|
768
|
+
? message.role
|
|
769
|
+
: '';
|
|
770
|
+
if (role !== 'user') {
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
const contentBlocks = Array.isArray(message.content)
|
|
774
|
+
? [...message.content]
|
|
775
|
+
: [];
|
|
776
|
+
const originalText = this.extractTextFromContentBlocks(contentBlocks, ['text']);
|
|
777
|
+
const textType = this.detectContentTextType(contentBlocks, 'text');
|
|
778
|
+
const composed = this.composeVisionUserText(visionText, originalText);
|
|
779
|
+
message.content = [
|
|
780
|
+
{
|
|
781
|
+
type: textType,
|
|
782
|
+
text: composed
|
|
783
|
+
}
|
|
784
|
+
];
|
|
785
|
+
messages[i] = message;
|
|
786
|
+
return true;
|
|
787
|
+
}
|
|
788
|
+
return false;
|
|
789
|
+
}
|
|
790
|
+
extractTextFromContentBlocks(content, allowedTypes) {
|
|
791
|
+
if (typeof content === 'string') {
|
|
792
|
+
return content;
|
|
793
|
+
}
|
|
794
|
+
if (!Array.isArray(content)) {
|
|
795
|
+
return '';
|
|
796
|
+
}
|
|
797
|
+
const collected = [];
|
|
798
|
+
for (const block of content) {
|
|
799
|
+
if (!block || typeof block !== 'object') {
|
|
800
|
+
continue;
|
|
801
|
+
}
|
|
802
|
+
const typeValue = typeof block.type === 'string'
|
|
803
|
+
? block.type
|
|
804
|
+
: '';
|
|
805
|
+
if (allowedTypes.length && !allowedTypes.includes(typeValue)) {
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
const textValue = block.text;
|
|
809
|
+
if (typeof textValue === 'string' && textValue.trim()) {
|
|
810
|
+
collected.push(textValue.trim());
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return collected.join('\n');
|
|
814
|
+
}
|
|
815
|
+
detectContentTextType(content, fallback) {
|
|
816
|
+
if (Array.isArray(content)) {
|
|
817
|
+
for (const block of content) {
|
|
818
|
+
if (!block || typeof block !== 'object') {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
const typeValue = block.type;
|
|
822
|
+
if (typeof typeValue === 'string' && (typeValue === 'text' || typeValue === 'input_text')) {
|
|
823
|
+
return typeValue;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return fallback;
|
|
828
|
+
}
|
|
829
|
+
composeVisionUserText(visionText, originalText) {
|
|
830
|
+
const sections = [];
|
|
831
|
+
const cleanedVision = (visionText || '').trim();
|
|
832
|
+
if (cleanedVision) {
|
|
833
|
+
sections.push(`【图片分析】\n${cleanedVision}`);
|
|
834
|
+
}
|
|
835
|
+
const cleanedOriginal = (originalText || '').trim();
|
|
836
|
+
if (cleanedOriginal) {
|
|
837
|
+
sections.push(`【用户原始请求】\n${cleanedOriginal}`);
|
|
838
|
+
}
|
|
839
|
+
return sections.join('\n\n');
|
|
840
|
+
}
|
|
841
|
+
extractVisionDescription(body) {
|
|
842
|
+
if (!body) {
|
|
843
|
+
return null;
|
|
844
|
+
}
|
|
845
|
+
if (typeof body === 'string') {
|
|
846
|
+
const trimmed = body.trim();
|
|
847
|
+
return trimmed.length ? trimmed : null;
|
|
848
|
+
}
|
|
849
|
+
if (typeof body !== 'object') {
|
|
850
|
+
return null;
|
|
851
|
+
}
|
|
852
|
+
const record = body;
|
|
853
|
+
const direct = this.extractTextCandidate(record);
|
|
854
|
+
if (direct) {
|
|
855
|
+
return direct;
|
|
856
|
+
}
|
|
857
|
+
if (record.response && typeof record.response === 'object') {
|
|
858
|
+
const responseNode = record.response;
|
|
859
|
+
const nested = this.extractTextCandidate(responseNode);
|
|
860
|
+
if (nested) {
|
|
861
|
+
return nested;
|
|
862
|
+
}
|
|
863
|
+
const output = responseNode.output;
|
|
864
|
+
if (Array.isArray(output)) {
|
|
865
|
+
for (const entry of output) {
|
|
866
|
+
if (entry && typeof entry === 'object') {
|
|
867
|
+
const nestedText = this.extractTextCandidate(entry);
|
|
868
|
+
if (nestedText) {
|
|
869
|
+
return nestedText;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (Array.isArray(record.output)) {
|
|
876
|
+
for (const entry of record.output) {
|
|
877
|
+
if (entry && typeof entry === 'object') {
|
|
878
|
+
const nested = this.extractTextCandidate(entry);
|
|
879
|
+
if (nested) {
|
|
880
|
+
return nested;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const choices = record.choices;
|
|
886
|
+
if (Array.isArray(choices)) {
|
|
887
|
+
for (const choice of choices) {
|
|
888
|
+
if (!choice || typeof choice !== 'object') {
|
|
889
|
+
continue;
|
|
890
|
+
}
|
|
891
|
+
const message = choice.message;
|
|
892
|
+
if (message && typeof message === 'object') {
|
|
893
|
+
const msg = message;
|
|
894
|
+
const content = msg.content;
|
|
895
|
+
if (typeof content === 'string' && content.trim()) {
|
|
896
|
+
return content.trim();
|
|
897
|
+
}
|
|
898
|
+
if (Array.isArray(content)) {
|
|
899
|
+
for (const part of content) {
|
|
900
|
+
if (part && typeof part === 'object' && typeof part.text === 'string') {
|
|
901
|
+
const textValue = part.text.trim();
|
|
902
|
+
if (textValue) {
|
|
903
|
+
return textValue;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
extractTextCandidate(record) {
|
|
914
|
+
const candidates = [
|
|
915
|
+
{ key: 'output_text', allowJson: true },
|
|
916
|
+
{ key: 'text' },
|
|
917
|
+
{ key: 'content' }
|
|
918
|
+
];
|
|
919
|
+
for (const candidate of candidates) {
|
|
920
|
+
if (!(candidate.key in record)) {
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
const text = this.normalizeTextCandidateValue(record[candidate.key], candidate.allowJson === true);
|
|
924
|
+
if (text) {
|
|
925
|
+
return text;
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
normalizeTextCandidateValue(value, allowJsonStringify = false) {
|
|
931
|
+
if (!value) {
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
if (typeof value === 'string') {
|
|
935
|
+
const trimmed = value.trim();
|
|
936
|
+
return trimmed.length ? trimmed : null;
|
|
937
|
+
}
|
|
938
|
+
if (Array.isArray(value)) {
|
|
939
|
+
const collected = [];
|
|
940
|
+
for (const entry of value) {
|
|
941
|
+
const nested = this.normalizeTextCandidateValue(entry, allowJsonStringify);
|
|
942
|
+
if (nested) {
|
|
943
|
+
collected.push(nested);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
return collected.length ? collected.join('\n') : null;
|
|
947
|
+
}
|
|
948
|
+
if (typeof value === 'object') {
|
|
949
|
+
const bag = value;
|
|
950
|
+
const textField = bag.text;
|
|
951
|
+
if (typeof textField === 'string' && textField.trim()) {
|
|
952
|
+
return textField.trim();
|
|
953
|
+
}
|
|
954
|
+
const summaryField = bag.summary;
|
|
955
|
+
if (typeof summaryField === 'string' && summaryField.trim()) {
|
|
956
|
+
return summaryField.trim();
|
|
957
|
+
}
|
|
958
|
+
if ('content' in bag) {
|
|
959
|
+
const nested = this.normalizeTextCandidateValue(bag.content, allowJsonStringify);
|
|
960
|
+
if (nested) {
|
|
961
|
+
return nested;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
if (allowJsonStringify) {
|
|
965
|
+
try {
|
|
966
|
+
const serialized = JSON.stringify(value, null, 2);
|
|
967
|
+
return serialized.trim() || null;
|
|
968
|
+
}
|
|
969
|
+
catch {
|
|
970
|
+
return null;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
return null;
|
|
975
|
+
}
|
|
487
976
|
}
|
|
488
977
|
export function createRequestExecutor(deps) {
|
|
489
978
|
return new HubRequestExecutor(deps);
|