@jsonstudio/rcc 0.89.333 → 0.89.548
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/dist/build-info.js +3 -3
- package/dist/build-info.js.map +1 -1
- package/dist/cli.js +110 -1
- package/dist/cli.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/index.js +20 -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.js +3 -1
- package/dist/modules/pipeline/utils/colored-logger.js.map +1 -1
- 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 +337 -25
- package/dist/providers/auth/oauth-lifecycle.js.map +1 -1
- package/dist/providers/core/config/oauth-flows.d.ts +23 -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 +9 -3
- 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 +35 -1
- package/dist/providers/core/runtime/base-provider.js.map +1 -1
- package/dist/providers/core/runtime/gemini-cli-http-provider.js +87 -20
- 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 +75 -1
- package/dist/providers/core/runtime/http-request-executor.js.map +1 -1
- package/dist/providers/core/runtime/http-transport-provider.d.ts +2 -0
- package/dist/providers/core/runtime/http-transport-provider.js +60 -2
- package/dist/providers/core/runtime/http-transport-provider.js.map +1 -1
- package/dist/providers/core/runtime/iflow-http-provider.d.ts +4 -0
- package/dist/providers/core/runtime/iflow-http-provider.js +28 -0
- package/dist/providers/core/runtime/iflow-http-provider.js.map +1 -1
- package/dist/providers/core/runtime/rate-limit-manager.d.ts +30 -0
- package/dist/providers/core/runtime/rate-limit-manager.js +136 -0
- package/dist/providers/core/runtime/rate-limit-manager.js.map +1 -0
- package/dist/providers/core/runtime/responses-provider.js +8 -3
- 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 +75 -26
- package/dist/providers/core/strategies/oauth-auth-code-flow.js.map +1 -1
- package/dist/providers/core/utils/http-client.js +2 -1
- package/dist/providers/core/utils/http-client.js.map +1 -1
- package/dist/providers/core/utils/provider-error-reporter.js +31 -5
- package/dist/providers/core/utils/provider-error-reporter.js.map +1 -1
- package/dist/providers/core/utils/provider-type-utils.js +1 -1
- package/dist/providers/core/utils/provider-type-utils.js.map +1 -1
- package/dist/providers/core/utils/snapshot-writer.d.ts +1 -1
- package/dist/providers/core/utils/snapshot-writer.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 +9 -0
- package/dist/server/runtime/http-server/index.js +512 -144
- package/dist/server/runtime/http-server/index.js.map +1 -1
- package/dist/server/runtime/http-server/provider-utils.js +1 -1
- package/dist/server/runtime/http-server/provider-utils.js.map +1 -1
- package/dist/server/runtime/http-server/request-executor.d.ts +10 -0
- package/dist/server/runtime/http-server/request-executor.js +553 -159
- 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 +29 -0
- package/dist/server/runtime/http-server/routes.js.map +1 -1
- package/dist/server/runtime/http-server/runtime-manager.js +33 -0
- package/dist/server/runtime/http-server/runtime-manager.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/token-daemon/history-store.d.ts +75 -0
- package/dist/token-daemon/history-store.js +207 -0
- package/dist/token-daemon/history-store.js.map +1 -0
- package/dist/token-daemon/index.d.ts +7 -0
- package/dist/token-daemon/index.js +336 -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 +23 -0
- package/dist/token-daemon/token-daemon.js +249 -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/token-portal/local-token-portal.d.ts +1 -0
- package/dist/token-portal/local-token-portal.js +89 -0
- package/dist/token-portal/local-token-portal.js.map +1 -0
- package/dist/token-portal/render.d.ts +10 -0
- package/dist/token-portal/render.js +56 -0
- package/dist/token-portal/render.js.map +1 -0
- package/dist/tools/semantic-replay.js +7 -6
- package/dist/tools/semantic-replay.js.map +1 -1
- package/dist/utils/error-handler-registry.d.ts +36 -0
- package/dist/utils/error-handler-registry.js +93 -7
- package/dist/utils/error-handler-registry.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 +137 -5
- 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-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/universal-shape-filter.js +11 -0
- 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 +190 -181
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-iflow.json +195 -195
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-lmstudio.json +43 -43
- package/node_modules/@jsonstudio/llms/dist/conversion/compat/profiles/chat-qwen.json +20 -20
- 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-pipeline-executor.js +24 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/hub-pipeline.js +39 -4
- package/node_modules/@jsonstudio/llms/dist/conversion/hub/pipeline/target-utils.js +6 -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/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-openai-bridge.js +82 -3
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/anthropic-message-utils.js +92 -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/snapshot-utils.js +17 -47
- package/node_modules/@jsonstudio/llms/dist/conversion/shared/tool-filter-pipeline.js +1 -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 +308 -43
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/classifier.js +11 -17
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.d.ts +0 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/context-advisor.js +0 -12
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.d.ts +17 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/engine.js +332 -95
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/features.js +1 -1
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/message-utils.js +36 -24
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/provider-registry.js +2 -1
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/token-counter.js +14 -3
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.d.ts +66 -2
- package/node_modules/@jsonstudio/llms/dist/router/virtual-router/types.js +2 -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 +1 -1
- package/package.json +3 -2
- package/scripts/pack-mode.mjs +2 -1
- package/scripts/publish-rcc.mjs +20 -4
- package/scripts/test-iflow-web-search.mjs +141 -0
- package/scripts/test-iflow.mjs +93 -1
- package/scripts/tests/virtual-router-health.mjs +141 -6
- 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
|
@@ -56,6 +56,19 @@ function enforceBuiltinToolSchema(name, candidate) {
|
|
|
56
56
|
const base = asSchema(candidate);
|
|
57
57
|
return ensureApplyPatchSchema(base);
|
|
58
58
|
}
|
|
59
|
+
if (normalizedName === 'web_search') {
|
|
60
|
+
// For web_search we currently accept any incoming schema and fall back to a
|
|
61
|
+
// minimal object definition. Server-side web_search execution only relies
|
|
62
|
+
// on the function name + JSON arguments, so tool schema is best-effort.
|
|
63
|
+
const base = asSchema(candidate) ?? {};
|
|
64
|
+
if (!base.type) {
|
|
65
|
+
base.type = 'object';
|
|
66
|
+
}
|
|
67
|
+
if (!Object.prototype.hasOwnProperty.call(base, 'properties')) {
|
|
68
|
+
base.properties = {};
|
|
69
|
+
}
|
|
70
|
+
return base;
|
|
71
|
+
}
|
|
59
72
|
return asSchema(candidate);
|
|
60
73
|
}
|
|
61
74
|
const DEFAULT_SANITIZER = (value) => {
|
|
@@ -111,10 +124,20 @@ export function bridgeToolToChatDefinition(rawTool, options) {
|
|
|
111
124
|
}
|
|
112
125
|
const tool = rawTool;
|
|
113
126
|
const fnNode = tool.function && typeof tool.function === 'object' ? tool.function : undefined;
|
|
114
|
-
|
|
127
|
+
let name = pickToolName([fnNode?.name, tool.name], options);
|
|
128
|
+
// Special case for Responses builtin web_search tools:
|
|
129
|
+
// Codex / Claude‑code may send tools shaped as `{ type: "web_search", ... }`
|
|
130
|
+
// without a nested `function` node. Treat these as a canonical `web_search`
|
|
131
|
+
// function tool so downstream Chat / Standardized layers can reason over a
|
|
132
|
+
// single function-style web_search surface.
|
|
115
133
|
if (!name) {
|
|
116
|
-
|
|
134
|
+
const rawType = typeof tool.type === 'string' ? tool.type.trim().toLowerCase() : '';
|
|
135
|
+
if (rawType === 'web_search' || rawType.startsWith('web_search')) {
|
|
136
|
+
name = 'web_search';
|
|
137
|
+
}
|
|
117
138
|
}
|
|
139
|
+
if (!name)
|
|
140
|
+
return null;
|
|
118
141
|
const description = resolveToolDescription(fnNode?.description ?? tool.description);
|
|
119
142
|
const parameters = enforceBuiltinToolSchema(name, resolveToolParameters(fnNode, tool));
|
|
120
143
|
const strict = resolveToolStrict(fnNode, tool);
|
|
@@ -11,8 +11,7 @@ const DEFAULT_LOAD_BALANCING = { strategy: 'round-robin' };
|
|
|
11
11
|
const DEFAULT_HEALTH = { failureThreshold: 3, cooldownMs: 30_000, fatalCooldownMs: 300_000 };
|
|
12
12
|
const DEFAULT_CONTEXT_ROUTING = {
|
|
13
13
|
warnRatio: 0.9,
|
|
14
|
-
hardLimit: false
|
|
15
|
-
fallbackRoute: 'longcontext'
|
|
14
|
+
hardLimit: false
|
|
16
15
|
};
|
|
17
16
|
/**
|
|
18
17
|
* 将用户提供的 Virtual Router 配置(或包含 virtualrouter 字段的整体配置)
|
|
@@ -28,6 +27,8 @@ export function bootstrapVirtualRouterConfig(input) {
|
|
|
28
27
|
if (!Object.keys(routingSource).length) {
|
|
29
28
|
throw new VirtualRouterError('Virtual Router routing table cannot be empty', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
30
29
|
}
|
|
30
|
+
const webSearch = normalizeWebSearch(section.webSearch, routingSource);
|
|
31
|
+
validateWebSearchRouting(webSearch, routingSource);
|
|
31
32
|
const { runtimeEntries, aliasIndex } = buildProviderRuntimeEntries(providersSource);
|
|
32
33
|
const { routing, targetKeys } = expandRoutingTable(routingSource, aliasIndex);
|
|
33
34
|
if (!routing.default || routing.default.length === 0) {
|
|
@@ -44,7 +45,8 @@ export function bootstrapVirtualRouterConfig(input) {
|
|
|
44
45
|
classifier,
|
|
45
46
|
loadBalancing,
|
|
46
47
|
health,
|
|
47
|
-
contextRouting
|
|
48
|
+
contextRouting,
|
|
49
|
+
...(webSearch ? { webSearch } : {})
|
|
48
50
|
};
|
|
49
51
|
return {
|
|
50
52
|
config,
|
|
@@ -65,7 +67,8 @@ function extractVirtualRouterSection(input) {
|
|
|
65
67
|
const loadBalancing = normalizeLoadBalancing(section.loadBalancing ?? root.loadBalancing);
|
|
66
68
|
const health = normalizeHealth(section.health ?? root.health);
|
|
67
69
|
const contextRouting = normalizeContextRouting(section.contextRouting ?? root.contextRouting);
|
|
68
|
-
|
|
70
|
+
const webSearch = section.webSearch ?? root.webSearch;
|
|
71
|
+
return { providers, routing, classifier, loadBalancing, health, contextRouting, webSearch };
|
|
69
72
|
}
|
|
70
73
|
function buildProviderRuntimeEntries(providers) {
|
|
71
74
|
const runtimeEntries = {};
|
|
@@ -95,6 +98,11 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
95
98
|
userInfoUrl: entry.auth.userInfoUrl,
|
|
96
99
|
refreshUrl: entry.auth.refreshUrl
|
|
97
100
|
};
|
|
101
|
+
// 为 OAuth 类型的 auth 设置 tokenFile 为 alias(如果没有显式配置 tokenFile)
|
|
102
|
+
// 这允许 oauth-lifecycle.ts 的 resolveTokenFilePath 函数正确解析并匹配现有文件
|
|
103
|
+
if (!runtimeAuth.tokenFile && (runtimeAuth.rawType?.includes('oauth') || runtimeAuth.type === 'oauth')) {
|
|
104
|
+
runtimeAuth.tokenFile = entry.keyAlias;
|
|
105
|
+
}
|
|
98
106
|
if (runtimeAuth.type === 'apiKey' && !runtimeAuth.secretRef) {
|
|
99
107
|
runtimeAuth.secretRef = `${providerId}.${entry.keyAlias}`;
|
|
100
108
|
}
|
|
@@ -113,7 +121,8 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
113
121
|
streaming: normalizedProvider.streaming,
|
|
114
122
|
modelStreaming: normalizedProvider.modelStreaming,
|
|
115
123
|
modelContextTokens: normalizedProvider.modelContextTokens,
|
|
116
|
-
defaultContextTokens: normalizedProvider.defaultContextTokens
|
|
124
|
+
defaultContextTokens: normalizedProvider.defaultContextTokens,
|
|
125
|
+
...(normalizedProvider.serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
117
126
|
};
|
|
118
127
|
}
|
|
119
128
|
}
|
|
@@ -122,28 +131,40 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
122
131
|
function expandRoutingTable(routingSource, aliasIndex) {
|
|
123
132
|
const routing = {};
|
|
124
133
|
const targetKeys = new Set();
|
|
125
|
-
for (const [routeName,
|
|
126
|
-
const
|
|
127
|
-
for (const
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
for (const [routeName, pools] of Object.entries(routingSource)) {
|
|
135
|
+
const expandedPools = [];
|
|
136
|
+
for (const pool of pools) {
|
|
137
|
+
const expandedTargets = [];
|
|
138
|
+
for (const entry of pool.targets) {
|
|
139
|
+
const parsed = parseRouteEntry(entry, aliasIndex);
|
|
140
|
+
if (!parsed) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!aliasIndex.has(parsed.providerId)) {
|
|
144
|
+
throw new VirtualRouterError(`Route "${routeName}" references unknown provider "${parsed.providerId}"`, VirtualRouterErrorCode.CONFIG_ERROR);
|
|
145
|
+
}
|
|
146
|
+
const aliases = parsed.keyAlias ? [parsed.keyAlias] : aliasIndex.get(parsed.providerId);
|
|
147
|
+
if (!aliases.length) {
|
|
148
|
+
throw new VirtualRouterError(`Provider ${parsed.providerId} has no auth aliases but is referenced in routing`, VirtualRouterErrorCode.CONFIG_ERROR);
|
|
149
|
+
}
|
|
150
|
+
for (const alias of aliases) {
|
|
151
|
+
const runtimeKey = buildRuntimeKey(parsed.providerId, alias);
|
|
152
|
+
const targetKey = `${runtimeKey}.${parsed.modelId}`;
|
|
153
|
+
pushUnique(expandedTargets, targetKey);
|
|
154
|
+
targetKeys.add(targetKey);
|
|
155
|
+
}
|
|
134
156
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
targetKeys.add(targetKey);
|
|
157
|
+
if (expandedTargets.length) {
|
|
158
|
+
expandedPools.push({
|
|
159
|
+
id: pool.id,
|
|
160
|
+
priority: pool.priority,
|
|
161
|
+
backup: pool.backup,
|
|
162
|
+
targets: expandedTargets,
|
|
163
|
+
...(pool.force ? { force: true } : {})
|
|
164
|
+
});
|
|
144
165
|
}
|
|
145
166
|
}
|
|
146
|
-
routing[routeName] =
|
|
167
|
+
routing[routeName] = expandedPools;
|
|
147
168
|
}
|
|
148
169
|
return { routing, targetKeys };
|
|
149
170
|
}
|
|
@@ -175,7 +196,8 @@ function buildProviderProfiles(targetKeys, runtimeEntries) {
|
|
|
175
196
|
processMode: runtime.processMode || 'chat',
|
|
176
197
|
responsesConfig: runtime.responsesConfig,
|
|
177
198
|
streaming: streamingPref,
|
|
178
|
-
maxContextTokens: contextTokens
|
|
199
|
+
maxContextTokens: contextTokens,
|
|
200
|
+
...(runtime.serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
179
201
|
};
|
|
180
202
|
targetRuntime[targetKey] = {
|
|
181
203
|
...runtime,
|
|
@@ -200,15 +222,138 @@ function resolveContextTokens(runtime, modelId) {
|
|
|
200
222
|
function normalizeRouting(source) {
|
|
201
223
|
const routing = {};
|
|
202
224
|
for (const [routeName, entries] of Object.entries(source)) {
|
|
203
|
-
if (!Array.isArray(entries))
|
|
225
|
+
if (!Array.isArray(entries) || !entries.length) {
|
|
226
|
+
routing[routeName] = [];
|
|
204
227
|
continue;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
228
|
+
}
|
|
229
|
+
const allStrings = entries.every((entry) => typeof entry === 'string' || entry === null || entry === undefined);
|
|
230
|
+
if (allStrings) {
|
|
231
|
+
const targets = normalizeTargetList(entries);
|
|
232
|
+
routing[routeName] = targets.length ? [buildLegacyRoutePool(routeName, targets)] : [];
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const normalized = [];
|
|
236
|
+
const total = entries.length || 1;
|
|
237
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
238
|
+
const entry = entries[index];
|
|
239
|
+
const pool = normalizeRoutePoolEntry(routeName, entry, index, total);
|
|
240
|
+
if (pool && pool.targets.length) {
|
|
241
|
+
normalized.push(pool);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
routing[routeName] = normalized;
|
|
209
245
|
}
|
|
210
246
|
return routing;
|
|
211
247
|
}
|
|
248
|
+
function buildLegacyRoutePool(routeName, targets) {
|
|
249
|
+
return {
|
|
250
|
+
id: `${routeName}:pool0`,
|
|
251
|
+
priority: targets.length,
|
|
252
|
+
backup: false,
|
|
253
|
+
targets
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
function normalizeRoutePoolEntry(routeName, entry, index, total) {
|
|
257
|
+
if (typeof entry === 'string') {
|
|
258
|
+
const targets = normalizeTargetList(entry);
|
|
259
|
+
return targets.length
|
|
260
|
+
? {
|
|
261
|
+
id: `${routeName}:pool${index + 1}`,
|
|
262
|
+
priority: total - index,
|
|
263
|
+
backup: false,
|
|
264
|
+
targets
|
|
265
|
+
}
|
|
266
|
+
: null;
|
|
267
|
+
}
|
|
268
|
+
if (!entry || typeof entry !== 'object') {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const record = entry;
|
|
272
|
+
const id = readOptionalString(record.id) ??
|
|
273
|
+
readOptionalString(record?.poolId) ??
|
|
274
|
+
`${routeName}:pool${index + 1}`;
|
|
275
|
+
const backup = record.backup === true ||
|
|
276
|
+
record.isBackup === true ||
|
|
277
|
+
(typeof record.type === 'string' && record.type.toLowerCase() === 'backup');
|
|
278
|
+
const priority = normalizePriorityValue(record.priority, total - index);
|
|
279
|
+
const targets = normalizeRouteTargets(record);
|
|
280
|
+
const force = record.force === true ||
|
|
281
|
+
(typeof record.force === 'string' && record.force.trim().toLowerCase() === 'true');
|
|
282
|
+
return targets.length
|
|
283
|
+
? {
|
|
284
|
+
id,
|
|
285
|
+
priority,
|
|
286
|
+
backup,
|
|
287
|
+
targets,
|
|
288
|
+
...(force ? { force: true } : {})
|
|
289
|
+
}
|
|
290
|
+
: null;
|
|
291
|
+
}
|
|
292
|
+
function normalizeRouteTargets(record) {
|
|
293
|
+
const buckets = [
|
|
294
|
+
record.targets,
|
|
295
|
+
record.providers,
|
|
296
|
+
record.pool,
|
|
297
|
+
record.entries,
|
|
298
|
+
record.items,
|
|
299
|
+
record.routes
|
|
300
|
+
];
|
|
301
|
+
const normalized = [];
|
|
302
|
+
for (const bucket of buckets) {
|
|
303
|
+
for (const target of normalizeTargetList(bucket)) {
|
|
304
|
+
if (!normalized.includes(target)) {
|
|
305
|
+
normalized.push(target);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const singular = [record.target, record.provider];
|
|
310
|
+
for (const candidate of singular) {
|
|
311
|
+
for (const target of normalizeTargetList(candidate)) {
|
|
312
|
+
if (!normalized.includes(target)) {
|
|
313
|
+
normalized.push(target);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return normalized;
|
|
318
|
+
}
|
|
319
|
+
function normalizeTargetList(value) {
|
|
320
|
+
if (Array.isArray(value)) {
|
|
321
|
+
const normalized = [];
|
|
322
|
+
for (const entry of value) {
|
|
323
|
+
if (typeof entry === 'string') {
|
|
324
|
+
const trimmed = entry.trim();
|
|
325
|
+
if (trimmed && !normalized.includes(trimmed)) {
|
|
326
|
+
normalized.push(trimmed);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return normalized;
|
|
331
|
+
}
|
|
332
|
+
if (typeof value === 'string') {
|
|
333
|
+
const trimmed = value.trim();
|
|
334
|
+
return trimmed ? [trimmed] : [];
|
|
335
|
+
}
|
|
336
|
+
if (typeof value === 'number') {
|
|
337
|
+
const str = String(value).trim();
|
|
338
|
+
return str ? [str] : [];
|
|
339
|
+
}
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
function normalizePriorityValue(value, fallback) {
|
|
343
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
if (typeof value === 'string') {
|
|
347
|
+
const trimmed = value.trim();
|
|
348
|
+
if (trimmed) {
|
|
349
|
+
const parsed = Number(trimmed);
|
|
350
|
+
if (Number.isFinite(parsed)) {
|
|
351
|
+
return parsed;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return fallback;
|
|
356
|
+
}
|
|
212
357
|
function normalizeClassifier(input) {
|
|
213
358
|
const normalized = asRecord(input);
|
|
214
359
|
const result = {
|
|
@@ -247,6 +392,12 @@ function normalizeProvider(providerId, raw) {
|
|
|
247
392
|
const streaming = resolveProviderStreamingPreference(provider, responsesNode);
|
|
248
393
|
const modelStreaming = normalizeModelStreaming(provider);
|
|
249
394
|
const { modelContextTokens, defaultContextTokens } = normalizeModelContextTokens(provider);
|
|
395
|
+
const serverToolsDisabled = provider.serverToolsDisabled === true ||
|
|
396
|
+
(typeof provider.serverToolsDisabled === 'string' &&
|
|
397
|
+
provider.serverToolsDisabled.trim().toLowerCase() === 'true') ||
|
|
398
|
+
(provider.serverTools &&
|
|
399
|
+
typeof provider.serverTools === 'object' &&
|
|
400
|
+
provider.serverTools.enabled === false);
|
|
250
401
|
return {
|
|
251
402
|
providerId,
|
|
252
403
|
providerType,
|
|
@@ -259,7 +410,8 @@ function normalizeProvider(providerId, raw) {
|
|
|
259
410
|
streaming,
|
|
260
411
|
modelStreaming,
|
|
261
412
|
modelContextTokens,
|
|
262
|
-
defaultContextTokens
|
|
413
|
+
defaultContextTokens,
|
|
414
|
+
...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
263
415
|
};
|
|
264
416
|
}
|
|
265
417
|
function normalizeModelStreaming(provider) {
|
|
@@ -389,15 +541,110 @@ function normalizeContextRouting(input) {
|
|
|
389
541
|
coerceRatio(record?.warn_ratio);
|
|
390
542
|
const hardLimitCandidate = coerceBoolean(record.hardLimit) ??
|
|
391
543
|
coerceBoolean(record?.hard_limit);
|
|
392
|
-
const fallbackCandidate = readOptionalString(record.fallbackRoute) ??
|
|
393
|
-
readOptionalString(record?.fallback_route);
|
|
394
544
|
const warnRatio = clampWarnRatio(warnCandidate ?? DEFAULT_CONTEXT_ROUTING.warnRatio);
|
|
395
545
|
const hardLimit = typeof hardLimitCandidate === 'boolean' ? hardLimitCandidate : DEFAULT_CONTEXT_ROUTING.hardLimit;
|
|
396
|
-
const fallbackRoute = fallbackCandidate ?? DEFAULT_CONTEXT_ROUTING.fallbackRoute;
|
|
397
546
|
return {
|
|
398
547
|
warnRatio,
|
|
399
|
-
hardLimit
|
|
400
|
-
|
|
548
|
+
hardLimit
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function validateWebSearchRouting(webSearch, routingSource) {
|
|
552
|
+
if (!webSearch) {
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
const routePools = routingSource['web_search'] ?? routingSource['search'];
|
|
556
|
+
if (!Array.isArray(routePools) || !routePools.length) {
|
|
557
|
+
throw new VirtualRouterError('Virtual Router webSearch.engines configured but routing.web_search (or search) route is missing or empty', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
558
|
+
}
|
|
559
|
+
const targets = new Set();
|
|
560
|
+
for (const pool of routePools) {
|
|
561
|
+
if (!pool || !Array.isArray(pool.targets)) {
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
for (const target of pool.targets) {
|
|
565
|
+
if (typeof target === 'string' && target.trim()) {
|
|
566
|
+
targets.add(target.trim());
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
for (const engine of webSearch.engines) {
|
|
571
|
+
if (!targets.has(engine.providerKey)) {
|
|
572
|
+
throw new VirtualRouterError(`Virtual Router webSearch engine "${engine.id}" references providerKey "${engine.providerKey}" which is not present in routing.web_search/search`, VirtualRouterErrorCode.CONFIG_ERROR);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
function normalizeWebSearch(input, routingSource) {
|
|
577
|
+
if (!input || typeof input !== 'object') {
|
|
578
|
+
return undefined;
|
|
579
|
+
}
|
|
580
|
+
const record = input;
|
|
581
|
+
const enginesNode = Array.isArray(record.engines) ? record.engines : [];
|
|
582
|
+
const engines = [];
|
|
583
|
+
for (const raw of enginesNode) {
|
|
584
|
+
if (!raw || typeof raw !== 'object') {
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
const node = raw;
|
|
588
|
+
const idRaw = node.id;
|
|
589
|
+
const providerKeyRaw = node.providerKey ?? node.provider ?? node.target;
|
|
590
|
+
const id = typeof idRaw === 'string' && idRaw.trim()
|
|
591
|
+
? idRaw.trim()
|
|
592
|
+
: undefined;
|
|
593
|
+
const providerKey = typeof providerKeyRaw === 'string' && providerKeyRaw.trim()
|
|
594
|
+
? providerKeyRaw.trim()
|
|
595
|
+
: undefined;
|
|
596
|
+
if (!id || !providerKey) {
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
const description = typeof node.description === 'string' && node.description.trim()
|
|
600
|
+
? node.description.trim()
|
|
601
|
+
: undefined;
|
|
602
|
+
const isDefault = node.default === true ||
|
|
603
|
+
(typeof node.default === 'string' && node.default.trim().toLowerCase() === 'true');
|
|
604
|
+
const serverToolsDisabled = node.serverToolsDisabled === true ||
|
|
605
|
+
(typeof node.serverToolsDisabled === 'string' &&
|
|
606
|
+
node.serverToolsDisabled.trim().toLowerCase() === 'true') ||
|
|
607
|
+
(node.serverTools &&
|
|
608
|
+
typeof node.serverTools === 'object' &&
|
|
609
|
+
node.serverTools.enabled === false);
|
|
610
|
+
// Deduplicate by id; first wins, subsequent are ignored.
|
|
611
|
+
if (engines.some((engine) => engine.id === id)) {
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
engines.push({
|
|
615
|
+
id,
|
|
616
|
+
providerKey,
|
|
617
|
+
description,
|
|
618
|
+
default: isDefault,
|
|
619
|
+
...(serverToolsDisabled ? { serverToolsDisabled: true } : {})
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
if (!engines.length) {
|
|
623
|
+
return undefined;
|
|
624
|
+
}
|
|
625
|
+
let injectPolicy;
|
|
626
|
+
let force;
|
|
627
|
+
const rawPolicy = record.injectPolicy ?? record?.inject_policy;
|
|
628
|
+
if (typeof rawPolicy === 'string') {
|
|
629
|
+
const normalized = rawPolicy.trim().toLowerCase();
|
|
630
|
+
if (normalized === 'always' || normalized === 'selective') {
|
|
631
|
+
injectPolicy = normalized;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
if (record.force === true ||
|
|
635
|
+
(typeof record.force === 'string' && record.force.trim().toLowerCase() === 'true')) {
|
|
636
|
+
force = true;
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
const webSearchPools = routingSource['web_search'] ?? routingSource['search'] ?? [];
|
|
640
|
+
if (Array.isArray(webSearchPools) && webSearchPools.some((pool) => pool.force)) {
|
|
641
|
+
force = true;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return {
|
|
645
|
+
engines,
|
|
646
|
+
injectPolicy: injectPolicy ?? 'selective',
|
|
647
|
+
...(force ? { force } : {})
|
|
401
648
|
};
|
|
402
649
|
}
|
|
403
650
|
function extractProviderAuthEntries(providerId, raw) {
|
|
@@ -531,19 +778,37 @@ function extractProviderAuthEntries(providerId, raw) {
|
|
|
531
778
|
pushEntry(undefined, buildAuthCandidate(baseTypeSource, { value: apiKeyField.trim() }));
|
|
532
779
|
}
|
|
533
780
|
// 自动多 token 扫描:仅在未显式声明多 key、且为受支持的 OAuth 提供方时触发
|
|
534
|
-
if (
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
|
|
781
|
+
if (baseType === 'oauth') {
|
|
782
|
+
const scanCandidates = new Set();
|
|
783
|
+
const pushCandidate = (value) => {
|
|
784
|
+
if (typeof value === 'string' && value.trim()) {
|
|
785
|
+
scanCandidates.add(value.trim().toLowerCase());
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
pushCandidate(auth?.oauthProviderId);
|
|
789
|
+
pushCandidate(baseTypeInfo.oauthProviderId);
|
|
790
|
+
pushCandidate(providerId);
|
|
791
|
+
for (const candidate of scanCandidates) {
|
|
792
|
+
if (!MULTI_TOKEN_OAUTH_PROVIDERS.has(candidate)) {
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
const tokenFiles = scanOAuthTokenFiles(candidate);
|
|
796
|
+
if (!tokenFiles.length) {
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
const baseTypeAlias = baseTypeInfo.oauthProviderId?.toLowerCase();
|
|
538
800
|
for (const match of tokenFiles) {
|
|
539
801
|
const alias = match.alias && match.alias !== 'default'
|
|
540
802
|
? `${match.sequence}-${match.alias}`
|
|
541
803
|
: String(match.sequence);
|
|
804
|
+
const typeHint = baseTypeSource && baseTypeAlias === candidate
|
|
805
|
+
? baseTypeSource
|
|
806
|
+
: `${candidate}-oauth`;
|
|
542
807
|
const authConfig = {
|
|
543
808
|
...defaults,
|
|
544
|
-
type:
|
|
809
|
+
type: typeHint,
|
|
545
810
|
tokenFile: match.filePath,
|
|
546
|
-
oauthProviderId
|
|
811
|
+
oauthProviderId: candidate
|
|
547
812
|
};
|
|
548
813
|
pushEntry(alias, authConfig);
|
|
549
814
|
}
|
|
@@ -712,7 +977,7 @@ function mergeScopes(primary, fallback) {
|
|
|
712
977
|
}
|
|
713
978
|
return merged.size ? Array.from(merged) : undefined;
|
|
714
979
|
}
|
|
715
|
-
const MULTI_TOKEN_OAUTH_PROVIDERS = new Set(['iflow']);
|
|
980
|
+
const MULTI_TOKEN_OAUTH_PROVIDERS = new Set(['iflow', 'qwen', 'gemini-cli', 'antigravity']);
|
|
716
981
|
function interpretAuthType(value) {
|
|
717
982
|
if (typeof value !== 'string') {
|
|
718
983
|
return { type: 'apiKey' };
|
|
@@ -17,34 +17,28 @@ export class RoutingClassifier {
|
|
|
17
17
|
const thinkingContinuation = lastToolCategory === 'read';
|
|
18
18
|
const searchContinuation = lastToolCategory === 'search';
|
|
19
19
|
const toolsContinuation = lastToolCategory === 'other';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const evaluations = {
|
|
23
|
-
thinking: { triggered: true, reason: reasoning }
|
|
24
|
-
};
|
|
25
|
-
const candidates = this.ensureDefaultCandidate(['thinking']);
|
|
26
|
-
return this.buildResult('thinking', reasoning, evaluations, candidates);
|
|
27
|
-
}
|
|
20
|
+
// 用户输入优先级最高(仅次于 vision),确保每次新的用户输入都走 thinking 路由进行工具检查
|
|
21
|
+
// thinking_continuation 用于区分"工具轮次中的 read 类调用"与"用户新输入"
|
|
28
22
|
const evaluationMap = {
|
|
29
23
|
vision: {
|
|
30
|
-
triggered: features.
|
|
31
|
-
reason: 'vision:
|
|
24
|
+
triggered: features.hasImageAttachment,
|
|
25
|
+
reason: 'vision:image-detected'
|
|
26
|
+
},
|
|
27
|
+
thinking: {
|
|
28
|
+
triggered: latestMessageFromUser,
|
|
29
|
+
reason: 'thinking:user-input'
|
|
32
30
|
},
|
|
33
31
|
longcontext: {
|
|
34
32
|
triggered: reachedLongContext,
|
|
35
33
|
reason: 'longcontext:token-threshold'
|
|
36
34
|
},
|
|
37
|
-
websearch: {
|
|
38
|
-
triggered: features.hasWebTool || searchContinuation,
|
|
39
|
-
reason: searchContinuation ? 'websearch:last-tool-search' : 'websearch:web-tools-detected'
|
|
40
|
-
},
|
|
41
35
|
coding: {
|
|
42
36
|
triggered: codingContinuation,
|
|
43
37
|
reason: 'coding:last-tool-write'
|
|
44
38
|
},
|
|
45
|
-
|
|
46
|
-
triggered: thinkingContinuation
|
|
47
|
-
reason:
|
|
39
|
+
thinking_continuation: {
|
|
40
|
+
triggered: thinkingContinuation,
|
|
41
|
+
reason: 'thinking:last-tool-read'
|
|
48
42
|
},
|
|
49
43
|
tools: {
|
|
50
44
|
triggered: toolsContinuation || features.hasTools || features.hasToolCallResponses,
|
|
@@ -16,6 +16,4 @@ export declare class ContextAdvisor {
|
|
|
16
16
|
private hardLimit;
|
|
17
17
|
configure(config?: VirtualRouterContextRoutingConfig | null): void;
|
|
18
18
|
classify(pool: string[], estimatedTokens: number, resolveProfile: (key: string) => ProviderProfile): ContextAdvisorResult;
|
|
19
|
-
prefersFallback(result: ContextAdvisorResult): boolean;
|
|
20
|
-
allowsOverflow(): boolean;
|
|
21
19
|
}
|
|
@@ -55,18 +55,6 @@ export class ContextAdvisor {
|
|
|
55
55
|
allOverflow: safe.length === 0 && risky.length === 0 && overflow.length > 0
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
prefersFallback(result) {
|
|
59
|
-
if (result.safe.length > 0) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
if (result.risky.length > 0) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
return result.allOverflow;
|
|
66
|
-
}
|
|
67
|
-
allowsOverflow() {
|
|
68
|
-
return !this.hardLimit;
|
|
69
|
-
}
|
|
70
58
|
}
|
|
71
59
|
function clampWarnRatio(value) {
|
|
72
60
|
if (!Number.isFinite(value)) {
|
|
@@ -11,6 +11,8 @@ export declare class VirtualRouterEngine {
|
|
|
11
11
|
private routeStats;
|
|
12
12
|
private readonly debug;
|
|
13
13
|
private healthConfig;
|
|
14
|
+
private readonly statsCenter;
|
|
15
|
+
private webSearchForce;
|
|
14
16
|
initialize(config: VirtualRouterConfig): void;
|
|
15
17
|
route(request: StandardizedRequest | ProcessedRequest, metadata: RouterMetadataInput): {
|
|
16
18
|
target: TargetMetadata;
|
|
@@ -27,24 +29,37 @@ export declare class VirtualRouterEngine {
|
|
|
27
29
|
}>;
|
|
28
30
|
health: import("./types.js").ProviderHealthState[];
|
|
29
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* 将分类器产生的逻辑路由名直接归一化为配置中的路由键。
|
|
34
|
+
* 不再维护 "websearch" 之类的别名,调用方应显式使用 "web_search" 或 "search" 等实际路由名。
|
|
35
|
+
*/
|
|
36
|
+
private normalizeRouteAlias;
|
|
30
37
|
private validateConfig;
|
|
31
38
|
private selectProvider;
|
|
39
|
+
private trySelectFromTier;
|
|
32
40
|
private incrementRouteStat;
|
|
33
41
|
private providerHealthConfig;
|
|
34
42
|
private initializeRouteQueue;
|
|
35
|
-
private resolveFallbackRoute;
|
|
36
|
-
private maybeDeferToFallback;
|
|
37
43
|
private buildContextCandidatePools;
|
|
38
44
|
private describeAttempt;
|
|
39
45
|
private resolveStickyKey;
|
|
40
46
|
private mapProviderError;
|
|
41
47
|
private deriveReason;
|
|
42
48
|
private buildRouteCandidates;
|
|
49
|
+
private reorderForInlineVision;
|
|
50
|
+
private routeSupportsInlineVision;
|
|
43
51
|
private sortByPriority;
|
|
44
52
|
private routeWeight;
|
|
53
|
+
private routeHasForceFlag;
|
|
54
|
+
private routeHasTargets;
|
|
55
|
+
private hasPrimaryPool;
|
|
56
|
+
private sortRoutePools;
|
|
57
|
+
private flattenPoolTargets;
|
|
45
58
|
private buildHitReason;
|
|
46
59
|
private decorateWithDetail;
|
|
47
60
|
private formatVirtualRouterHit;
|
|
48
61
|
private resolveRouteColor;
|
|
49
62
|
private describeContextUsage;
|
|
63
|
+
private describeTargetProvider;
|
|
64
|
+
private parseProviderKey;
|
|
50
65
|
}
|