@jsonstudio/rcc 0.90.814 → 0.90.872
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 +8 -0
- package/configsamples/provider-default/ali-coding-plan/config.v2.json +76 -0
- package/configsamples/provider-default/antigravity/config.v2.json +142 -0
- package/configsamples/provider-default/ark-coding-plan/config.v2.json +64 -0
- package/configsamples/provider-default/crs/config.v2.json +54 -0
- package/configsamples/provider-default/deepseek-web/config.v2.json +56 -0
- package/configsamples/provider-default/gemini/config.v2.json +43 -0
- package/configsamples/provider-default/gemini-cli/config.v2.json +45 -0
- package/configsamples/provider-default/gemini-native/config.v2.json +208 -0
- package/configsamples/provider-default/glm/config.v2.json +33 -0
- package/configsamples/provider-default/glm-anthropic/config.v2.json +29 -0
- package/configsamples/provider-default/kimi/config.v2.json +25 -0
- package/configsamples/provider-default/lmstudio/config.v2.json +79 -0
- package/configsamples/provider-default/lmstudio-proxy/config.v2.json +78 -0
- package/configsamples/provider-default/manifest.json +31 -0
- package/configsamples/provider-default/meituan/config.v2.json +20 -0
- package/configsamples/provider-default/mimo/config.v2.json +26 -0
- package/configsamples/provider-default/modelscope/config.v2.json +81 -0
- package/configsamples/provider-default/my-openai/config.v2.json +20 -0
- package/configsamples/provider-default/nvidia/config.v2.json +32 -0
- package/configsamples/provider-default/opencode-zen-free/config.v2.json +56 -0
- package/configsamples/provider-default/openrouter/config.v2.json +210 -0
- package/configsamples/provider-default/qwen/config.v2.json +38 -0
- package/configsamples/provider-default/qwenchat/config.v2.json +53 -0
- package/configsamples/provider-default/tab/config.v2.json +26 -0
- package/configsamples/provider-default/tabglm/config.v2.json +77 -0
- package/dist/build-info.js +2 -2
- package/dist/cli/commands/config.d.ts +5 -0
- package/dist/cli/commands/config.js +369 -1
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/examples.js +3 -0
- package/dist/cli/commands/examples.js.map +1 -1
- package/dist/cli/commands/init.js +25 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/launcher-kernel.js +122 -46
- package/dist/cli/commands/launcher-kernel.js.map +1 -1
- package/dist/cli/commands/start.js +60 -3
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/config/bundled-provider-pack.d.ts +20 -0
- package/dist/cli/config/bundled-provider-pack.js +146 -0
- package/dist/cli/config/bundled-provider-pack.js.map +1 -0
- package/dist/cli/register/status-config-commands.d.ts +2 -0
- package/dist/cli/register/status-config-commands.js.map +1 -1
- package/dist/cli.js +81 -28
- package/dist/cli.js.map +1 -1
- package/dist/debug/snapshot-store.js +2 -1
- package/dist/debug/snapshot-store.js.map +1 -1
- package/dist/index.js +23 -14
- package/dist/index.js.map +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js +1 -1
- package/dist/manager/modules/quota/provider-quota-daemon.model-backoff.js.map +1 -1
- package/dist/manager/quota/provider-quota-center.js +1 -1
- package/dist/manager/quota/provider-quota-center.js.map +1 -1
- package/dist/manager/storage/file-store.js +10 -0
- package/dist/manager/storage/file-store.js.map +1 -1
- package/dist/modules/llmswitch/bridge/snapshot-recorder-runtime.js +18 -1
- package/dist/modules/llmswitch/bridge/snapshot-recorder-runtime.js.map +1 -1
- package/dist/modules/llmswitch/bridge/snapshot-recorder.js +132 -51
- package/dist/modules/llmswitch/bridge/snapshot-recorder.js.map +1 -1
- package/dist/provider-sdk/provider-runtime-inference.js +2 -2
- package/dist/provider-sdk/provider-runtime-inference.js.map +1 -1
- package/dist/providers/auth/deepseek-account-token-acquirer.js +32 -3
- package/dist/providers/auth/deepseek-account-token-acquirer.js.map +1 -1
- package/dist/providers/core/api/provider-types.d.ts +11 -0
- package/dist/providers/core/config/service-profiles.js +1 -1
- package/dist/providers/core/runtime/deepseek-http-provider.d.ts +2 -0
- package/dist/providers/core/runtime/deepseek-http-provider.js +31 -1
- package/dist/providers/core/runtime/deepseek-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/qwenchat-http-provider-helpers.d.ts +3 -2
- package/dist/providers/core/runtime/qwenchat-http-provider-helpers.js +513 -96
- package/dist/providers/core/runtime/qwenchat-http-provider-helpers.js.map +1 -1
- package/dist/providers/core/runtime/standard-tool-text-harvest.d.ts +8 -0
- package/dist/providers/core/runtime/standard-tool-text-harvest.js +16 -0
- package/dist/providers/core/runtime/standard-tool-text-harvest.js.map +1 -0
- package/dist/providers/core/runtime/standard-tool-text-request-transform.d.ts +4 -1
- package/dist/providers/core/runtime/standard-tool-text-request-transform.js +121 -3
- package/dist/providers/core/runtime/standard-tool-text-request-transform.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.js +5 -2
- package/dist/providers/core/utils/snapshot-writer.js.map +1 -1
- package/dist/providers/profile/provider-profile-loader.js +52 -1
- package/dist/providers/profile/provider-profile-loader.js.map +1 -1
- package/dist/providers/profile/provider-profile.d.ts +3 -0
- package/dist/server/handlers/handler-response-utils.js +1 -0
- package/dist/server/handlers/handler-response-utils.js.map +1 -1
- package/dist/server/handlers/images-handler.d.ts +9 -0
- package/dist/server/handlers/images-handler.js +258 -0
- package/dist/server/handlers/images-handler.js.map +1 -0
- package/dist/server/handlers/types.d.ts +7 -0
- package/dist/server/runtime/http-server/antigravity-startup-tasks.d.ts +3 -0
- package/dist/server/runtime/http-server/antigravity-startup-tasks.js +16 -0
- package/dist/server/runtime/http-server/antigravity-startup-tasks.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js +3 -18
- package/dist/server/runtime/http-server/daemon-admin/auth-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.d.ts +7 -0
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js +17 -0
- package/dist/server/runtime/http-server/daemon-admin/providers-handler-utils.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js +50 -17
- package/dist/server/runtime/http-server/daemon-admin/providers-handler.js.map +1 -1
- package/dist/server/runtime/http-server/daemon-admin-routes.js +32 -2
- package/dist/server/runtime/http-server/daemon-admin-routes.js.map +1 -1
- package/dist/server/runtime/http-server/executor/provider-response-converter.js +42 -13
- package/dist/server/runtime/http-server/executor/provider-response-converter.js.map +1 -1
- package/dist/server/runtime/http-server/executor/provider-response-utils.js +41 -3
- package/dist/server/runtime/http-server/executor/provider-response-utils.js.map +1 -1
- package/dist/server/runtime/http-server/executor/usage-aggregator.js +7 -7
- package/dist/server/runtime/http-server/executor/usage-aggregator.js.map +1 -1
- package/dist/server/runtime/http-server/executor/usage-logger.d.ts +9 -0
- package/dist/server/runtime/http-server/executor/usage-logger.js +35 -2
- package/dist/server/runtime/http-server/executor/usage-logger.js.map +1 -1
- package/dist/server/runtime/http-server/executor-metadata.js +12 -4
- package/dist/server/runtime/http-server/executor-metadata.js.map +1 -1
- package/dist/server/runtime/http-server/executor-pipeline.js +24 -15
- package/dist/server/runtime/http-server/executor-pipeline.js.map +1 -1
- package/dist/server/runtime/http-server/executor-provider.d.ts +6 -1
- package/dist/server/runtime/http-server/executor-provider.js +137 -5
- package/dist/server/runtime/http-server/executor-provider.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-bootstrap.js +6 -0
- package/dist/server/runtime/http-server/http-server-bootstrap.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-lifecycle.js +23 -15
- package/dist/server/runtime/http-server/http-server-lifecycle.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-runtime-providers.js +14 -4
- package/dist/server/runtime/http-server/http-server-runtime-providers.js.map +1 -1
- package/dist/server/runtime/http-server/http-server-runtime-setup.js +83 -1
- package/dist/server/runtime/http-server/http-server-runtime-setup.js.map +1 -1
- package/dist/server/runtime/http-server/hub-shadow-compare.js +2 -41
- package/dist/server/runtime/http-server/hub-shadow-compare.js.map +1 -1
- package/dist/server/runtime/http-server/provider-routing-scope.d.ts +9 -0
- package/dist/server/runtime/http-server/provider-routing-scope.js +20 -0
- package/dist/server/runtime/http-server/provider-routing-scope.js.map +1 -0
- package/dist/server/runtime/http-server/provider-traffic-governor.d.ts +67 -0
- package/dist/server/runtime/http-server/provider-traffic-governor.js +467 -0
- package/dist/server/runtime/http-server/provider-traffic-governor.js.map +1 -0
- package/dist/server/runtime/http-server/request-executor.d.ts +8 -0
- package/dist/server/runtime/http-server/request-executor.js +446 -21
- package/dist/server/runtime/http-server/request-executor.js.map +1 -1
- package/dist/server/runtime/http-server/routes.js +13 -0
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/session-client-registry.js +30 -4
- package/dist/server/runtime/http-server/session-client-registry.js.map +1 -1
- package/dist/server/runtime/http-server/session-client-route-utils.d.ts +7 -0
- package/dist/server/runtime/http-server/session-client-route-utils.js +38 -0
- package/dist/server/runtime/http-server/session-client-route-utils.js.map +1 -1
- package/dist/server/runtime/http-server/session-client-routes.js +12 -2
- package/dist/server/runtime/http-server/session-client-routes.js.map +1 -1
- package/dist/server/utils/request-id-manager.js +42 -5
- package/dist/server/utils/request-id-manager.js.map +1 -1
- package/dist/server/utils/stage-logger.d.ts +1 -0
- package/dist/server/utils/stage-logger.js +27 -0
- package/dist/server/utils/stage-logger.js.map +1 -1
- package/dist/utils/errorsamples.js +3 -1
- package/dist/utils/errorsamples.js.map +1 -1
- package/dist/utils/sensitive-redaction.d.ts +1 -0
- package/dist/utils/sensitive-redaction.js +122 -0
- package/dist/utils/sensitive-redaction.js.map +1 -0
- package/docs/INSTALLATION_AND_QUICKSTART.md +14 -1
- package/docs/PORTS.md +12 -0
- package/docs/lmstudio-tool-calling.md +25 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.d.ts +3 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/actions/qwenchat-web-request.js +62 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwenchat-web.json +47 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/operation-table-runner.js +68 -7
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper-from-chat.js +138 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-chat-process-request-utils.js +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-chat-process-entry.js +7 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-execute-request-stage.js +7 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.d.ts +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-heavy-input-fastpath.js +203 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline-route-and-outbound.js +17 -12
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.d.ts +11 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-stage-timing.js +82 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +47 -14
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +43 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/client-remap-protocol-switch.js +222 -19
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/policy/policy-engine.js +2 -2
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/process/chat-process-pending-tool-sync.js +24 -7
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/response/provider-response.js +90 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/snapshot-recorder.js +252 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge/utils.js +5 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/responses/responses-openai-bridge.js +44 -4
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.d.ts +3 -1
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils-openai-request.js +20 -23
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-governor.js +68 -30
- package/node_modules/@jsonstudio/llms/dist/conversion/snapshot-utils.js +48 -8
- package/node_modules/@jsonstudio/llms/dist/native/router_hotpath_napi.node +0 -0
- package/node_modules/@jsonstudio/llms/dist/quota/quota-state.js +2 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine/routing-state/store.js +35 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +9 -9
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/sticky-session-store.js +104 -18
- package/node_modules/@jsonstudio/llms/dist/servertool/engine.js +79 -32
- package/node_modules/@jsonstudio/llms/dist/servertool/handlers/vision.js +49 -0
- package/node_modules/@jsonstudio/llms/dist/servertool/pending-session.js +48 -2
- package/node_modules/@jsonstudio/llms/dist/servertool/server-side-tools.js +14 -1
- package/node_modules/@jsonstudio/llms/dist/servertool/types.d.ts +1 -0
- package/node_modules/@jsonstudio/llms/package.json +1 -1
- package/node_modules/ajv/dist/compile/jtd/serialize.js +9 -2
- package/node_modules/ajv/dist/compile/jtd/serialize.js.map +1 -1
- package/node_modules/ajv/dist/core.d.ts +1 -0
- package/node_modules/ajv/dist/core.js.map +1 -1
- package/node_modules/ajv/dist/vocabularies/validation/pattern.js +13 -4
- package/node_modules/ajv/dist/vocabularies/validation/pattern.js.map +1 -1
- package/node_modules/ajv/lib/compile/jtd/serialize.ts +13 -2
- package/node_modules/ajv/lib/core.ts +1 -0
- package/node_modules/ajv/lib/vocabularies/validation/pattern.ts +15 -4
- package/node_modules/ajv/package.json +2 -1
- package/package.json +15 -10
- package/scripts/ci/repo-sanity.mjs +23 -2
- package/scripts/ci/secrets-check.mjs +48 -0
- package/scripts/ci/silent-failure-audit.mjs +192 -0
- package/scripts/mock-provider/run-regressions.mjs +1 -0
- package/scripts/pack-mode.mjs +32 -36
- package/scripts/publish-rcc.mjs +38 -60
- package/scripts/tests/apply-patch-loop.mjs +1 -0
- package/scripts/tests/blackbox-rcc-vs-routecodex-antigravity.mjs +2 -0
- package/scripts/tools-dev/responses-debug-client/src/index.ts +8 -3
- package/scripts/verify-e2e-toolcall.mjs +1 -0
- package/scripts/verify-install-e2e.mjs +2 -1
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const REDACTED = '[REDACTED]';
|
|
2
|
+
const MAX_DEPTH = 20;
|
|
3
|
+
const SENSITIVE_KEYS = new Set([
|
|
4
|
+
'authorization',
|
|
5
|
+
'proxyauthorization',
|
|
6
|
+
'xapikey',
|
|
7
|
+
'apikey',
|
|
8
|
+
'apikeyvalue',
|
|
9
|
+
'apikeyheader',
|
|
10
|
+
'api_key',
|
|
11
|
+
'api-key',
|
|
12
|
+
'access_token',
|
|
13
|
+
'accesstoken',
|
|
14
|
+
'refresh_token',
|
|
15
|
+
'refreshtoken',
|
|
16
|
+
'id_token',
|
|
17
|
+
'idtoken',
|
|
18
|
+
'sessiontoken',
|
|
19
|
+
'tokenvalue',
|
|
20
|
+
'bearertoken',
|
|
21
|
+
'password',
|
|
22
|
+
'passwd',
|
|
23
|
+
'passcode',
|
|
24
|
+
'secret',
|
|
25
|
+
'clientsecret',
|
|
26
|
+
'client_secret',
|
|
27
|
+
'cookie',
|
|
28
|
+
'setcookie'
|
|
29
|
+
]);
|
|
30
|
+
function normalizeKey(key) {
|
|
31
|
+
return String(key || '')
|
|
32
|
+
.trim()
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9_]/g, '');
|
|
35
|
+
}
|
|
36
|
+
function isSensitiveKey(key) {
|
|
37
|
+
const normalized = normalizeKey(key);
|
|
38
|
+
if (!normalized) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
if (SENSITIVE_KEYS.has(normalized)) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return normalized.endsWith('token') && normalized !== 'tokenfile';
|
|
45
|
+
}
|
|
46
|
+
function isSafeSecretReference(value) {
|
|
47
|
+
const trimmed = String(value || '').trim();
|
|
48
|
+
if (!trimmed) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (trimmed.startsWith('authfile-')) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (/^\$\{[A-Z0-9_]+\}$/i.test(trimmed)) {
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
if (/^[A-Z][A-Z0-9_]+$/.test(trimmed)) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
function maskSecretValue(value) {
|
|
63
|
+
const trimmed = String(value || '').trim();
|
|
64
|
+
if (!trimmed) {
|
|
65
|
+
return REDACTED;
|
|
66
|
+
}
|
|
67
|
+
return `${REDACTED}:${trimmed.length}`;
|
|
68
|
+
}
|
|
69
|
+
function redactEmbeddedSecrets(text) {
|
|
70
|
+
let out = String(text || '');
|
|
71
|
+
out = out.replace(/(\bBearer\s+)[A-Za-z0-9._~+/=-]{8,}/gi, '$1[REDACTED]');
|
|
72
|
+
out = out.replace(/\bsk-[A-Za-z0-9]{12,}\b/g, 'sk-[REDACTED]');
|
|
73
|
+
out = out.replace(/(\bapi[_-]?key\b\s*[:=]\s*)(["']?)[^"'\s,;]+(\2)/gi, '$1$2[REDACTED]$3');
|
|
74
|
+
out = out.replace(/(\bpassword\b\s*[:=]\s*)(["']?)[^"'\s,;]+(\2)/gi, '$1$2[REDACTED]$3');
|
|
75
|
+
out = out.replace(/(\bcookie\b\s*[:=]\s*)(["']?)[^"'\n]+(\2)/gi, '$1$2[REDACTED]$3');
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
function redactByKey(value) {
|
|
79
|
+
if (typeof value === 'string') {
|
|
80
|
+
if (isSafeSecretReference(value)) {
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
return maskSecretValue(value);
|
|
84
|
+
}
|
|
85
|
+
if (value == null) {
|
|
86
|
+
return REDACTED;
|
|
87
|
+
}
|
|
88
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
89
|
+
return REDACTED;
|
|
90
|
+
}
|
|
91
|
+
return REDACTED;
|
|
92
|
+
}
|
|
93
|
+
function redactInternal(value, state, depth, keyHint) {
|
|
94
|
+
if (depth > MAX_DEPTH) {
|
|
95
|
+
return '[TRUNCATED_DEPTH]';
|
|
96
|
+
}
|
|
97
|
+
if (keyHint && isSensitiveKey(keyHint)) {
|
|
98
|
+
return redactByKey(value);
|
|
99
|
+
}
|
|
100
|
+
if (typeof value === 'string') {
|
|
101
|
+
return redactEmbeddedSecrets(value);
|
|
102
|
+
}
|
|
103
|
+
if (!value || typeof value !== 'object') {
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
if (state.has(value)) {
|
|
107
|
+
return '[CIRCULAR]';
|
|
108
|
+
}
|
|
109
|
+
state.add(value);
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
return value.map((item) => redactInternal(item, state, depth + 1));
|
|
112
|
+
}
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const [key, child] of Object.entries(value)) {
|
|
115
|
+
out[key] = redactInternal(child, state, depth + 1, key);
|
|
116
|
+
}
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
export function redactSensitiveData(input) {
|
|
120
|
+
return redactInternal(input, new WeakSet(), 0);
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=sensitive-redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitive-redaction.js","sourceRoot":"","sources":["../../src/utils/sensitive-redaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,6 +26,12 @@ rcc init
|
|
|
26
26
|
|
|
27
27
|
初始化成功后,`rcc init` 会把内置文档复制到:`~/.rcc/docs`
|
|
28
28
|
|
|
29
|
+
如果你希望一次性解压内置的脱敏 provider 模板到 `~/.rcc/provider`(便于后续直接填 key / tokenFile):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
rcc init default
|
|
33
|
+
```
|
|
34
|
+
|
|
29
35
|
非交互式(CI/脚本):
|
|
30
36
|
|
|
31
37
|
```bash
|
|
@@ -38,11 +44,12 @@ rcc init --providers openai,tab --default-provider tab
|
|
|
38
44
|
rcc init --list-providers
|
|
39
45
|
```
|
|
40
46
|
|
|
41
|
-
生成后你需要把 `apiKey` / `tokenFile` / `cookieFile`
|
|
47
|
+
生成后你需要把 `apiKey` / `tokenFile` / `cookieFile` 按需补齐(脱敏模板会用环境变量占位,例如 `${OPENROUTER_API_KEY}`)。
|
|
42
48
|
|
|
43
49
|
参考配置:
|
|
44
50
|
- `configsamples/config.reference.json`
|
|
45
51
|
- `configsamples/provider/*/config.v1.json`
|
|
52
|
+
- `configsamples/provider-default/*/config.v2.json`
|
|
46
53
|
|
|
47
54
|
## 3) 启动服务器
|
|
48
55
|
|
|
@@ -56,6 +63,12 @@ rcc start
|
|
|
56
63
|
rcc start --config ./config.json
|
|
57
64
|
```
|
|
58
65
|
|
|
66
|
+
离线单模型(例如 LM Studio)建议单独配置并独立端口运行:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
routecodex start --port 5520 --config "/Volumes/extension/.rcc/config.offline.json"
|
|
70
|
+
```
|
|
71
|
+
|
|
59
72
|
## 4) 验证服务可用
|
|
60
73
|
|
|
61
74
|
```bash
|
package/docs/PORTS.md
CHANGED
|
@@ -34,3 +34,15 @@
|
|
|
34
34
|
rcc status
|
|
35
35
|
curl http://127.0.0.1:5555/ready
|
|
36
36
|
```
|
|
37
|
+
|
|
38
|
+
## 离线单模型模式(常用)
|
|
39
|
+
|
|
40
|
+
当你需要把所有主路由收敛到本地模型(例如 LM Studio)时,建议使用单独配置文件(例如 `config.offline.json`)并在独立端口启动:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
routecodex start --port 5520 --config "/Volumes/extension/.rcc/config.offline.json"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
说明:
|
|
47
|
+
- `--port` 优先级高于配置文件端口。
|
|
48
|
+
- 若端口已被非托管进程占用,会直接失败(fail-fast),不会静默抢占。
|
|
@@ -4,6 +4,31 @@
|
|
|
4
4
|
|
|
5
5
|
LM Studio provides comprehensive tool calling functionality that enables Large Language Models (LLMs) to interact with external functions and APIs. All models in LM Studio support at least some degree of tool use, with two levels of support: **Native** and **Default**.
|
|
6
6
|
|
|
7
|
+
## RouteCodex 集成注意(2026-04-04 更新)
|
|
8
|
+
|
|
9
|
+
在 RouteCodex → LM Studio 的 `/v1/responses` 路径下,真实回放曾出现:
|
|
10
|
+
|
|
11
|
+
- `HTTP 400`
|
|
12
|
+
- `type: invalid_request_error`
|
|
13
|
+
- `param: tools.*.type`
|
|
14
|
+
- `code: invalid_string`
|
|
15
|
+
|
|
16
|
+
根因确认(2026-04-04 实测):
|
|
17
|
+
|
|
18
|
+
- LM Studio `/v1/responses` **不接受** chat-style 嵌套工具形状
|
|
19
|
+
`{"type":"function","function":{...}}`(会触发 `tools.0.type invalid_string`)。
|
|
20
|
+
- LM Studio `/v1/responses` 接受的是 Responses-style 扁平工具形状
|
|
21
|
+
`{"type":"function","name":"...","parameters":{...}}`。
|
|
22
|
+
- `tool_choice` 需使用字符串枚举(`"none" | "auto" | "required"`),不应传对象。
|
|
23
|
+
|
|
24
|
+
因此在 RouteCodex 侧必须保证:
|
|
25
|
+
|
|
26
|
+
1. `tools[]` 仅保留 **function tools**。
|
|
27
|
+
2. 请求工具统一归一为扁平形状:`{"type":"function","name","parameters",...}`。
|
|
28
|
+
3. 当 `tools` 被清空时,移除 `tool_choice`;当 `tool_choice` 为对象时,归一为 `"required"`。
|
|
29
|
+
|
|
30
|
+
> 结论:LM Studio 文档层面的“工具支持”不代表任意 OpenAI 兼容形状都可透传;RouteCodex 必须做请求侧 schema 约束与形状修复。
|
|
31
|
+
|
|
7
32
|
## Tool Support Levels
|
|
8
33
|
|
|
9
34
|
### Native Tool Use Support
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { buildNativeReqOutboundCompatAdapterContext } from '../../hub/pipeline/compat/native-adapter-context.js';
|
|
2
|
+
import { runReqOutboundStage3CompatWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
3
|
+
const PROFILE = 'chat:qwenchat-web';
|
|
4
|
+
const DEFAULT_PROVIDER_PROTOCOL = 'openai-chat';
|
|
5
|
+
const DEFAULT_ENTRY_ENDPOINT = '/v1/chat/completions';
|
|
6
|
+
const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
7
|
+
const readBoolean = (value) => {
|
|
8
|
+
if (typeof value === 'boolean') {
|
|
9
|
+
return value;
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === 'string') {
|
|
12
|
+
const normalized = value.trim().toLowerCase();
|
|
13
|
+
if (['true', '1', 'yes', 'on'].includes(normalized)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (['false', '0', 'no', 'off'].includes(normalized)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
};
|
|
22
|
+
const readToolProtocol = (value) => {
|
|
23
|
+
if (typeof value !== 'string') {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
const normalized = value.trim().toLowerCase();
|
|
27
|
+
return normalized === 'native' || normalized === 'text' ? normalized : undefined;
|
|
28
|
+
};
|
|
29
|
+
function resolveToolTextNode(adapterContext) {
|
|
30
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
31
|
+
const baseNode = isRecord(nativeContext.deepseek) ? nativeContext.deepseek : {};
|
|
32
|
+
const baseProtocol = readToolProtocol(baseNode.toolProtocol);
|
|
33
|
+
const baseFallback = readBoolean(baseNode.textToolFallback);
|
|
34
|
+
const protocol = baseProtocol ?? (baseFallback === undefined ? undefined : baseFallback ? 'text' : 'native');
|
|
35
|
+
return {
|
|
36
|
+
...baseNode,
|
|
37
|
+
strictToolRequired: readBoolean(baseNode.strictToolRequired) ?? true,
|
|
38
|
+
textToolFallback: protocol ? protocol === 'text' : baseFallback ?? true,
|
|
39
|
+
...(protocol ? { toolProtocol: protocol } : {})
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function buildCompatInput(payload, adapterContext) {
|
|
43
|
+
const nativeContext = buildNativeReqOutboundCompatAdapterContext(adapterContext);
|
|
44
|
+
const normalizedContext = {
|
|
45
|
+
...nativeContext,
|
|
46
|
+
compatibilityProfile: PROFILE,
|
|
47
|
+
providerProtocol: nativeContext.providerProtocol ?? adapterContext?.providerProtocol ?? DEFAULT_PROVIDER_PROTOCOL,
|
|
48
|
+
entryEndpoint: nativeContext.entryEndpoint ?? adapterContext?.entryEndpoint ?? DEFAULT_ENTRY_ENDPOINT,
|
|
49
|
+
deepseek: resolveToolTextNode(adapterContext)
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
payload,
|
|
53
|
+
adapterContext: normalizedContext,
|
|
54
|
+
explicitProfile: PROFILE
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function applyQwenChatWebRequestTransform(payload, adapterContext) {
|
|
58
|
+
if (!payload || typeof payload !== 'object') {
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
return runReqOutboundStage3CompatWithNative(buildCompatInput(payload, adapterContext)).payload;
|
|
62
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "chat:qwenchat-web",
|
|
3
|
+
"protocol": "openai-chat",
|
|
4
|
+
"request": {
|
|
5
|
+
"mappings": [
|
|
6
|
+
{ "action": "snapshot", "phase": "compat-pre" },
|
|
7
|
+
{
|
|
8
|
+
"action": "tool_text_request_guidance",
|
|
9
|
+
"config": {
|
|
10
|
+
"marker": "Tool-call output contract (STRICT)",
|
|
11
|
+
"includeToolNames": true,
|
|
12
|
+
"requireTools": true
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{ "action": "deepseek_web_request" },
|
|
16
|
+
{ "action": "snapshot", "phase": "compat-post" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"response": {
|
|
20
|
+
"mappings": [
|
|
21
|
+
{ "action": "snapshot", "phase": "compat-pre" },
|
|
22
|
+
{
|
|
23
|
+
"action": "deepseek_web_response",
|
|
24
|
+
"config": {
|
|
25
|
+
"textNormalizer": {
|
|
26
|
+
"jsonToolRepair": {
|
|
27
|
+
"toolNameAliases": {
|
|
28
|
+
"shell_command": "exec_command",
|
|
29
|
+
"shell": "exec_command",
|
|
30
|
+
"bash": "exec_command",
|
|
31
|
+
"terminal": "exec_command"
|
|
32
|
+
},
|
|
33
|
+
"argumentAliases": {
|
|
34
|
+
"exec_command": {
|
|
35
|
+
"cmd": ["cmd", "command", "input.command", "script"],
|
|
36
|
+
"command": ["cmd", "command", "input.command", "script"],
|
|
37
|
+
"workdir": ["workdir", "cwd", "input.cwd"]
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{ "action": "snapshot", "phase": "compat-post" }
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
package/node_modules/@jsonstudio/llms/dist/conversion/hub/operation-table/operation-table-runner.js
CHANGED
|
@@ -10,7 +10,10 @@ const INBOUND_BRIDGE_SPECS = {
|
|
|
10
10
|
'gemini-chat': { protocol: 'gemini-chat', stage: 'request_inbound', messages: 'chat_envelope' }
|
|
11
11
|
};
|
|
12
12
|
const OUTBOUND_BRIDGE_SPECS = {
|
|
13
|
-
|
|
13
|
+
// openai-chat outbound post-map hooks do not write back `state.messages` into payload.
|
|
14
|
+
// Feeding full payload.messages here only adds O(n) scan cost on large histories.
|
|
15
|
+
// Keep hooks metadata-only by not passing message arrays.
|
|
16
|
+
'openai-chat': { protocol: 'openai-chat', stage: 'request_outbound', messages: 'none', includeCapturedToolResults: true },
|
|
14
17
|
// Keep parity: openai-responses outbound actions should not touch normalized messages.
|
|
15
18
|
'openai-responses': { protocol: 'openai-responses', stage: 'request_outbound', messages: 'none', moduleType: 'openai-responses' },
|
|
16
19
|
'anthropic-messages': { protocol: 'anthropic-messages', stage: 'request_outbound', messages: 'none', includeCapturedToolResults: true },
|
|
@@ -41,24 +44,82 @@ function buildCapturedToolResults(toolOutputs) {
|
|
|
41
44
|
name: entry.name
|
|
42
45
|
}));
|
|
43
46
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
function hasToolSignalsInMessages(messages) {
|
|
48
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
for (const message of messages) {
|
|
52
|
+
if (!message || typeof message !== 'object') {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
56
|
+
if (role === 'tool') {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (typeof message.tool_call_id === 'string' && message.tool_call_id.trim().length > 0) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const toolCalls = message.tool_calls;
|
|
63
|
+
if (Array.isArray(toolCalls) && toolCalls.length > 0) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
function filterToolOnlyActionsWhenNoToolSignals(stage, actions, messages) {
|
|
47
70
|
if (!actions?.length) {
|
|
48
|
-
return;
|
|
71
|
+
return actions;
|
|
49
72
|
}
|
|
50
|
-
|
|
73
|
+
if (stage !== 'request_outbound' && stage !== 'request_inbound') {
|
|
74
|
+
return actions;
|
|
75
|
+
}
|
|
76
|
+
if (hasToolSignalsInMessages(messages)) {
|
|
77
|
+
return actions;
|
|
78
|
+
}
|
|
79
|
+
const toolOnlyActions = new Set([
|
|
80
|
+
'tools.capture-results',
|
|
81
|
+
'tools.normalize-call-ids',
|
|
82
|
+
'compat.fix-apply-patch',
|
|
83
|
+
'tools.ensure-placeholders'
|
|
84
|
+
]);
|
|
85
|
+
return actions.filter((action) => {
|
|
86
|
+
const name = typeof action?.name === 'string' ? action.name.trim().toLowerCase() : '';
|
|
87
|
+
return !toolOnlyActions.has(name);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function applyBridgePolicy(spec, options) {
|
|
91
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: spec.protocol, moduleType: spec.moduleType ?? spec.protocol });
|
|
92
|
+
const resolvedActions = resolvePolicyActions(bridgePolicy, spec.stage);
|
|
51
93
|
const messages = spec.messages === 'chat_envelope'
|
|
52
94
|
? options.chatEnvelope.messages
|
|
53
95
|
: spec.messages === 'format_payload_messages'
|
|
54
96
|
? extractPayloadMessages(options.payload)
|
|
55
97
|
: undefined;
|
|
98
|
+
const actions = filterToolOnlyActionsWhenNoToolSignals(spec.stage, resolvedActions, messages);
|
|
99
|
+
if (!actions?.length) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const metadata = options.chatEnvelope.metadata;
|
|
103
|
+
const rawRequestForActionState = (() => {
|
|
104
|
+
if (spec.messages !== 'format_payload_messages') {
|
|
105
|
+
return options.payload;
|
|
106
|
+
}
|
|
107
|
+
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
|
108
|
+
return options.payload;
|
|
109
|
+
}
|
|
110
|
+
// Performance stop-bleed: avoid duplicating a very large messages[] payload in both
|
|
111
|
+
// `state.messages` and `state.rawRequest.messages`. Bridge actions still receive the
|
|
112
|
+
// canonical messages via `state.messages`.
|
|
113
|
+
const compact = { ...options.payload };
|
|
114
|
+
delete compact.messages;
|
|
115
|
+
return compact;
|
|
116
|
+
})();
|
|
56
117
|
const capturedToolResults = spec.includeCapturedToolResults
|
|
57
118
|
? buildCapturedToolResults(options.chatEnvelope.toolOutputs)
|
|
58
119
|
: undefined;
|
|
59
120
|
const actionState = createBridgeActionState({
|
|
60
121
|
...(messages ? { messages } : {}),
|
|
61
|
-
rawRequest:
|
|
122
|
+
rawRequest: rawRequestForActionState,
|
|
62
123
|
metadata,
|
|
63
124
|
...(capturedToolResults ? { capturedToolResults } : {})
|
|
64
125
|
});
|
|
@@ -1,10 +1,88 @@
|
|
|
1
1
|
import { isJsonObject, jsonClone } from '../../types/json.js';
|
|
2
2
|
import { buildAnthropicRequestFromOpenAIChat } from '../../../codecs/anthropic-openai-codec.js';
|
|
3
|
+
import { buildAnthropicFromOpenAIChatWithNative } from '../../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
|
|
3
4
|
import { encodeMetadataPassthrough } from '../../../metadata-passthrough.js';
|
|
5
|
+
import { isHubStageTimingDetailEnabled, logHubStageTiming } from '../../pipeline/hub-stage-timing.js';
|
|
4
6
|
import { applyEffortBudget, buildAnthropicThinkingFromConfig, mergeAnthropicOutputConfig, mergeAnthropicThinkingConfig, normalizeAnthropicThinkingConfigFromUnknown, resolveConfiguredAnthropicThinkingBudgets, resolveConfiguredAnthropicThinkingConfig } from './anthropic-thinking-config.js';
|
|
5
7
|
import { appendDroppedFieldAudit, appendLossyFieldAudit, hasExplicitEmptyToolsSemantics, isResponsesOrigin, } from './anthropic-semantics-audit.js';
|
|
6
8
|
import { ANTHROPIC_TOP_LEVEL_FIELDS, PASSTHROUGH_METADATA_PREFIX, PASSTHROUGH_PARAMETERS, RESPONSES_DROPPED_PARAMETER_KEYS, sanitizeAnthropicPayload, } from './anthropic-mapper-config.js';
|
|
9
|
+
const TRUTHY = new Set(['1', 'true', 'yes', 'on']);
|
|
10
|
+
const FALSY = new Set(['0', 'false', 'no', 'off']);
|
|
11
|
+
const DEFAULT_HEAVY_INPUT_THRESHOLD = 120_000;
|
|
12
|
+
function readBooleanEnv(names, fallback) {
|
|
13
|
+
for (const name of names) {
|
|
14
|
+
const raw = process.env[name];
|
|
15
|
+
if (raw === undefined) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const normalized = String(raw).trim().toLowerCase();
|
|
19
|
+
if (TRUTHY.has(normalized)) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (FALSY.has(normalized)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return fallback;
|
|
27
|
+
}
|
|
28
|
+
function readPositiveIntEnv(names, fallback) {
|
|
29
|
+
for (const name of names) {
|
|
30
|
+
const raw = process.env[name];
|
|
31
|
+
if (raw === undefined) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const parsed = Number.parseInt(String(raw).trim(), 10);
|
|
35
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
36
|
+
return parsed;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return fallback;
|
|
40
|
+
}
|
|
41
|
+
function shouldUseNativeBuild(ctx) {
|
|
42
|
+
const enabled = readBooleanEnv([
|
|
43
|
+
'ROUTECODEX_HUB_FASTPATH_ANTHROPIC_NATIVE_BUILD',
|
|
44
|
+
'RCC_HUB_FASTPATH_ANTHROPIC_NATIVE_BUILD',
|
|
45
|
+
// backward-compatible manual knob
|
|
46
|
+
'ROUTECODEX_HUB_ANTHROPIC_NATIVE_BUILD',
|
|
47
|
+
'RCC_HUB_ANTHROPIC_NATIVE_BUILD',
|
|
48
|
+
], false);
|
|
49
|
+
if (!enabled) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const threshold = readPositiveIntEnv([
|
|
53
|
+
'ROUTECODEX_HUB_FASTPATH_INPUT_TOKEN_THRESHOLD',
|
|
54
|
+
'RCC_HUB_FASTPATH_INPUT_TOKEN_THRESHOLD',
|
|
55
|
+
], DEFAULT_HEAVY_INPUT_THRESHOLD);
|
|
56
|
+
const rt = ctx.__rt;
|
|
57
|
+
if (rt &&
|
|
58
|
+
typeof rt === 'object' &&
|
|
59
|
+
rt.hubFastpathHeavyInput === true) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
const estimatedInputTokens = ctx.estimatedInputTokens;
|
|
63
|
+
return (typeof estimatedInputTokens === 'number' &&
|
|
64
|
+
Number.isFinite(estimatedInputTokens) &&
|
|
65
|
+
estimatedInputTokens >= threshold);
|
|
66
|
+
}
|
|
67
|
+
function hasAnthropicSystemSemantic(chat) {
|
|
68
|
+
try {
|
|
69
|
+
const sysNode = chat.semantics && typeof chat.semantics === 'object'
|
|
70
|
+
? chat.semantics.system
|
|
71
|
+
: undefined;
|
|
72
|
+
if (!sysNode || typeof sysNode !== 'object' || Array.isArray(sysNode)) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return Object.prototype.hasOwnProperty.call(sysNode, 'blocks');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
7
81
|
export function buildAnthropicFormatEnvelopeFromChat(chat, ctx) {
|
|
82
|
+
const requestId = typeof ctx.requestId === 'string' && ctx.requestId.trim().length
|
|
83
|
+
? ctx.requestId
|
|
84
|
+
: 'unknown';
|
|
85
|
+
const forceDetailLog = isHubStageTimingDetailEnabled();
|
|
8
86
|
const model = chat.parameters?.model;
|
|
9
87
|
if (typeof model !== 'string' || !model.trim()) {
|
|
10
88
|
throw new Error('ChatEnvelope.parameters.model is required for anthropic-messages outbound conversion');
|
|
@@ -114,15 +192,72 @@ export function buildAnthropicFormatEnvelopeFromChat(chat, ctx) {
|
|
|
114
192
|
catch {
|
|
115
193
|
// ignore
|
|
116
194
|
}
|
|
117
|
-
const
|
|
118
|
-
|
|
195
|
+
const useNativeBuild = shouldUseNativeBuild(ctx);
|
|
196
|
+
let payloadSource;
|
|
197
|
+
if (useNativeBuild) {
|
|
198
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_native', 'start');
|
|
199
|
+
const nativeBuildStartedAt = Date.now();
|
|
200
|
+
try {
|
|
201
|
+
payloadSource = buildAnthropicFromOpenAIChatWithNative(baseRequest, {
|
|
202
|
+
requestId: typeof ctx.requestId === 'string' && ctx.requestId.trim().length
|
|
203
|
+
? ctx.requestId
|
|
204
|
+
: undefined,
|
|
205
|
+
entryEndpoint: typeof ctx.entryEndpoint === 'string' && ctx.entryEndpoint.trim().length
|
|
206
|
+
? ctx.entryEndpoint
|
|
207
|
+
: undefined,
|
|
208
|
+
});
|
|
209
|
+
if (hasAnthropicSystemSemantic(chat) &&
|
|
210
|
+
!Object.prototype.hasOwnProperty.call(payloadSource, 'system')) {
|
|
211
|
+
throw new Error('native_missing_system_semantic_replay');
|
|
212
|
+
}
|
|
213
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_native', 'completed', {
|
|
214
|
+
elapsedMs: Date.now() - nativeBuildStartedAt,
|
|
215
|
+
forceLog: forceDetailLog,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_native', 'completed', {
|
|
220
|
+
elapsedMs: Date.now() - nativeBuildStartedAt,
|
|
221
|
+
forceLog: true,
|
|
222
|
+
fallbackToJs: true,
|
|
223
|
+
});
|
|
224
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_js_fallback', 'start');
|
|
225
|
+
const jsFallbackStartedAt = Date.now();
|
|
226
|
+
payloadSource = buildAnthropicRequestFromOpenAIChat(baseRequest, {
|
|
227
|
+
requestId,
|
|
228
|
+
});
|
|
229
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_js_fallback', 'completed', {
|
|
230
|
+
elapsedMs: Date.now() - jsFallbackStartedAt,
|
|
231
|
+
forceLog: forceDetailLog,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_js', 'start');
|
|
237
|
+
const jsBuildStartedAt = Date.now();
|
|
238
|
+
payloadSource = buildAnthropicRequestFromOpenAIChat(baseRequest, {
|
|
239
|
+
requestId,
|
|
240
|
+
});
|
|
241
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.build_request_js', 'completed', {
|
|
242
|
+
elapsedMs: Date.now() - jsBuildStartedAt,
|
|
243
|
+
forceLog: forceDetailLog,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.payload_sanitize', 'start');
|
|
247
|
+
const sanitizeStartedAt = Date.now();
|
|
248
|
+
const payload = sanitizeAnthropicPayload({
|
|
249
|
+
...payloadSource,
|
|
250
|
+
});
|
|
119
251
|
if (baseRequest.thinking !== undefined) {
|
|
120
252
|
payload.thinking = jsonClone(baseRequest.thinking);
|
|
121
253
|
}
|
|
122
254
|
if (baseRequest.output_config !== undefined) {
|
|
123
255
|
payload.output_config = jsonClone(baseRequest.output_config);
|
|
124
256
|
}
|
|
125
|
-
|
|
257
|
+
logHubStageTiming(requestId, 'req_outbound.anthropic.payload_sanitize', 'completed', {
|
|
258
|
+
elapsedMs: Date.now() - sanitizeStartedAt,
|
|
259
|
+
forceLog: forceDetailLog,
|
|
260
|
+
});
|
|
126
261
|
return {
|
|
127
262
|
protocol: 'anthropic-messages',
|
|
128
263
|
direction: 'response',
|
|
@@ -3,6 +3,7 @@ import { buildPassthroughAuditWithNative, readResponsesResumeFromRequestSemantic
|
|
|
3
3
|
import { readRuntimeMetadata } from "../../runtime-metadata.js";
|
|
4
4
|
import { computeRequestTokens } from "../../../router/virtual-router/token-estimator.js";
|
|
5
5
|
import { estimateSessionBoundTokens } from "../process/chat-process-session-usage.js";
|
|
6
|
+
import { isHeavyInputFastpathEnabled, markHeavyInputFastpath, resolveHeavyInputTokenThreshold, roughEstimateInputTokensFromRequest, } from "./hub-pipeline-heavy-input-fastpath.js";
|
|
6
7
|
export function sanitizeStandardizedRequestMessages(standardizedRequest) {
|
|
7
8
|
return {
|
|
8
9
|
...standardizedRequest,
|
|
@@ -37,12 +38,35 @@ export function resolveActiveProcessModeAndAudit(args) {
|
|
|
37
38
|
export function estimateInputTokensForWorkingRequest(args) {
|
|
38
39
|
const { workingRequest, normalizedMetadata } = args;
|
|
39
40
|
try {
|
|
41
|
+
const fastpathEnabled = isHeavyInputFastpathEnabled();
|
|
42
|
+
const threshold = resolveHeavyInputTokenThreshold();
|
|
43
|
+
if (fastpathEnabled && threshold > 0) {
|
|
44
|
+
const roughEstimate = roughEstimateInputTokensFromRequest(workingRequest);
|
|
45
|
+
if (roughEstimate >= threshold) {
|
|
46
|
+
if (normalizedMetadata && typeof normalizedMetadata === "object") {
|
|
47
|
+
normalizedMetadata.estimatedInputTokens = roughEstimate;
|
|
48
|
+
markHeavyInputFastpath({
|
|
49
|
+
metadata: normalizedMetadata,
|
|
50
|
+
estimatedInputTokens: roughEstimate,
|
|
51
|
+
reason: "rough_estimate",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
40
57
|
const estimatedTokens = estimateSessionBoundTokens(workingRequest, normalizedMetadata) ?? computeRequestTokens(workingRequest, "");
|
|
41
58
|
if (typeof estimatedTokens === "number" &&
|
|
42
59
|
Number.isFinite(estimatedTokens) &&
|
|
43
60
|
estimatedTokens > 0) {
|
|
44
61
|
if (normalizedMetadata && typeof normalizedMetadata === "object") {
|
|
45
62
|
normalizedMetadata.estimatedInputTokens = estimatedTokens;
|
|
63
|
+
if (fastpathEnabled && estimatedTokens >= threshold) {
|
|
64
|
+
markHeavyInputFastpath({
|
|
65
|
+
metadata: normalizedMetadata,
|
|
66
|
+
estimatedInputTokens: estimatedTokens,
|
|
67
|
+
reason: "full_estimate",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
46
70
|
}
|
|
47
71
|
}
|
|
48
72
|
}
|