@jsonstudio/llms 0.6.2979 → 0.6.3238
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/conversion/args-mapping.js +8 -0
- package/dist/conversion/{shared/bridge-actions.js → bridge-actions.js} +2 -1
- package/dist/conversion/{shared/bridge-id-utils.js → bridge-id-utils.js} +1 -1
- package/dist/conversion/{shared/bridge-instructions.js → bridge-instructions.js} +1 -1
- package/dist/conversion/{shared/bridge-message-utils.d.ts → bridge-message-utils.d.ts} +1 -1
- package/dist/conversion/{shared/bridge-message-utils.js → bridge-message-utils.js} +5 -149
- package/dist/conversion/{shared/bridge-metadata.js → bridge-metadata.js} +1 -1
- package/dist/conversion/{shared/bridge-policies.js → bridge-policies.js} +1 -1
- package/dist/conversion/codecs/gemini-openai-codec.js +27 -8
- package/dist/conversion/codecs/responses-openai-codec.js +1 -1
- package/dist/conversion/{shared/compaction-detect.d.ts → compaction-detect.d.ts} +1 -1
- package/dist/conversion/compaction-detect.js +4 -0
- package/dist/conversion/compat/actions/apply-patch-fixer.js +2 -2
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +0 -1
- package/dist/conversion/compat/actions/deepseek-web-response.js +15 -405
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +1 -1
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +1 -1
- package/dist/conversion/compat/actions/qwen-transform.js +74 -2
- package/dist/conversion/compat/actions/snapshot.js +1 -1
- package/dist/conversion/compat/antigravity-session-signature.js +36 -0
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +0 -22
- package/dist/conversion/compat/profiles/chat-glm.json +251 -72
- package/dist/conversion/compat/profiles/chat-iflow.json +174 -39
- package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -14
- package/dist/conversion/hub/operation-table/operation-table-runner.js +2 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +1 -1
- package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.d.ts +8 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.js +404 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +5 -381
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +2 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +2 -8
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +50 -3
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +1 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +62 -0
- package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +3 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/chat-process-semantics-bridge.d.ts +1 -1
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +42 -29
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +12 -0
- package/dist/conversion/hub/policy/protocol-spec.js +1 -1
- package/dist/conversion/hub/process/chat-process-clock-reminders.js +1 -1
- package/dist/conversion/hub/process/chat-process-clock-tools.js +1 -1
- package/dist/conversion/hub/process/chat-process-continue-execution.js +1 -1
- package/dist/conversion/hub/process/chat-process-servertool-orchestration.js +1 -1
- package/dist/conversion/hub/process/chat-process-web-search.js +1 -1
- package/dist/conversion/hub/response/provider-response.js +14 -5
- package/dist/conversion/hub/response/response-mappers.js +23 -1
- package/dist/conversion/hub/response/response-runtime.js +28 -5
- package/dist/conversion/hub/snapshot-recorder.js +3 -92
- package/dist/conversion/hub/tool-governance/engine.d.ts +8 -0
- package/dist/conversion/hub/tool-governance/engine.js +40 -193
- package/dist/conversion/hub/tool-governance/rules.js +73 -69
- package/dist/conversion/hub/tool-surface/tool-surface-engine.js +1 -1
- package/dist/conversion/index.d.ts +1 -2
- package/dist/conversion/index.js +1 -2
- package/dist/conversion/{shared/jsonish.js → jsonish.js} +1 -1
- package/dist/conversion/{shared/mcp-injection.js → mcp-injection.js} +1 -1
- package/dist/conversion/media.js +4 -0
- package/dist/conversion/{shared/metadata-passthrough.d.ts → metadata-passthrough.d.ts} +1 -1
- package/dist/conversion/{shared/metadata-passthrough.js → metadata-passthrough.js} +2 -2
- package/dist/conversion/payload-budget.js +47 -0
- package/dist/conversion/protocol-field-allowlists.d.ts +7 -0
- package/dist/conversion/protocol-field-allowlists.js +9 -0
- package/dist/conversion/{shared/protocol-state.d.ts → protocol-state.d.ts} +2 -2
- package/dist/conversion/{shared/protocol-state.js → protocol-state.js} +2 -2
- package/dist/conversion/{shared/errors.d.ts → provider-protocol-error.d.ts} +0 -3
- package/dist/conversion/provider-protocol-error.js +25 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +8 -5
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -1
- package/dist/conversion/responses/responses-openai-bridge.d.ts +1 -1
- package/dist/conversion/responses/responses-openai-bridge.js +43 -10
- package/dist/conversion/{shared/runtime-metadata.d.ts → runtime-metadata.d.ts} +1 -1
- package/dist/conversion/{shared/runtime-metadata.js → runtime-metadata.js} +2 -2
- package/dist/conversion/shared/anthropic-message-utils.js +19 -8
- package/dist/conversion/shared/chat-request-filters.d.ts +3 -4
- package/dist/conversion/shared/chat-request-filters.js +22 -78
- package/dist/conversion/shared/gemini-tool-utils.d.ts +1 -1
- package/dist/conversion/shared/openai-finalizer.js +1 -0
- package/dist/conversion/shared/openai-message-normalize.js +2 -2
- package/dist/conversion/shared/reasoning-normalizer.js +6 -0
- package/dist/conversion/shared/reasoning-utils.js +5 -2
- package/dist/conversion/shared/responses-conversation-store.js +1 -1
- package/dist/conversion/shared/responses-output-builder.js +55 -11
- package/dist/conversion/shared/responses-reasoning-registry.d.ts +14 -2
- package/dist/conversion/shared/responses-reasoning-registry.js +34 -6
- package/dist/conversion/shared/responses-response-utils.js +99 -9
- package/dist/conversion/shared/responses-tool-utils.js +1 -1
- package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +1 -1
- package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -2
- package/dist/conversion/shared/text-markup-normalizer.js +1 -1
- package/dist/conversion/shared/tool-filter-pipeline.js +1 -1
- package/dist/conversion/shared/tool-governor.js +3 -3
- package/dist/conversion/shared/tool-mapping.d.ts +1 -1
- package/dist/conversion/{shared/snapshot-utils.d.ts → snapshot-utils.d.ts} +11 -0
- package/dist/conversion/{shared/snapshot-utils.js → snapshot-utils.js} +14 -23
- package/dist/conversion/types/text-markup-normalizer.d.ts +13 -0
- package/dist/conversion/types/text-markup-normalizer.js +1 -0
- package/dist/filters/special/request-tools-normalize.js +1 -1
- package/dist/filters/special/response-tool-text-canonicalize.js +2 -2
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/quota/quota-manager.js +31 -59
- package/dist/quota/quota-state.js +14 -7
- package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +1 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +13 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +2 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +4 -1
- package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +7 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.js +44 -0
- package/dist/router/virtual-router/bootstrap.js +2 -0
- package/dist/router/virtual-router/engine/routing-state/store.d.ts +1 -2
- package/dist/router/virtual-router/engine/routing-state/store.js +2 -2
- package/dist/router/virtual-router/engine-legacy/config.d.ts +11 -0
- package/dist/router/virtual-router/engine-legacy/config.js +108 -0
- package/dist/router/virtual-router/engine-legacy/direct-model.d.ts +10 -0
- package/dist/router/virtual-router/engine-legacy/direct-model.js +38 -0
- package/dist/router/virtual-router/engine-legacy/health.d.ts +13 -0
- package/dist/router/virtual-router/engine-legacy/health.js +104 -0
- package/dist/router/virtual-router/engine-legacy/helpers.d.ts +16 -0
- package/dist/router/virtual-router/engine-legacy/helpers.js +226 -0
- package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +9 -0
- package/dist/router/virtual-router/engine-legacy/route-finalize.js +84 -0
- package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +17 -0
- package/dist/router/virtual-router/engine-legacy/route-selection.js +205 -0
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +3 -0
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +36 -0
- package/dist/router/virtual-router/engine-legacy/route-state.d.ts +12 -0
- package/dist/router/virtual-router/engine-legacy/route-state.js +386 -0
- package/dist/router/virtual-router/engine-legacy/route-utils.d.ts +19 -0
- package/dist/router/virtual-router/engine-legacy/route-utils.js +212 -0
- package/dist/router/virtual-router/engine-legacy/routing.d.ts +8 -0
- package/dist/router/virtual-router/engine-legacy/routing.js +8 -0
- package/dist/router/virtual-router/engine-legacy/selection-core.d.ts +28 -0
- package/dist/router/virtual-router/engine-legacy/selection-core.js +112 -0
- package/dist/router/virtual-router/engine-legacy/selection-state.d.ts +16 -0
- package/dist/router/virtual-router/engine-legacy/selection-state.js +187 -0
- package/dist/router/virtual-router/engine-legacy/state-accessors.d.ts +21 -0
- package/dist/router/virtual-router/engine-legacy/state-accessors.js +118 -0
- package/dist/router/virtual-router/engine-legacy.d.ts +123 -0
- package/dist/router/virtual-router/engine-legacy.js +194 -0
- package/dist/router/virtual-router/engine-logging.d.ts +2 -0
- package/dist/router/virtual-router/engine-logging.js +7 -2
- package/dist/router/virtual-router/engine-selection/key-parsing.js +0 -3
- package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.js +54 -0
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.d.ts +10 -0
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.js +67 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.d.ts +30 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.js +202 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.d.ts +2 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js +83 -0
- package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +43 -2
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +75 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +205 -0
- package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.js +109 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.d.ts +16 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.js +14 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +2 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +86 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.js +100 -0
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +99 -0
- package/dist/router/virtual-router/engine.d.ts +22 -105
- package/dist/router/virtual-router/engine.js +274 -1641
- package/dist/router/virtual-router/load-balancer.d.ts +8 -0
- package/dist/router/virtual-router/load-balancer.js +65 -2
- package/dist/router/virtual-router/provider-registry.js +2 -0
- package/dist/router/virtual-router/routing-instructions/clean.d.ts +3 -0
- package/dist/router/virtual-router/routing-instructions/clean.js +34 -0
- package/dist/router/virtual-router/routing-instructions/parse.d.ts +18 -0
- package/dist/router/virtual-router/routing-instructions/parse.js +377 -0
- package/dist/router/virtual-router/routing-instructions/state.d.ts +4 -0
- package/dist/router/virtual-router/routing-instructions/state.js +245 -0
- package/dist/router/virtual-router/routing-instructions/types.d.ts +70 -0
- package/dist/router/virtual-router/routing-instructions/types.js +2 -0
- package/dist/router/virtual-router/routing-instructions.d.ts +5 -89
- package/dist/router/virtual-router/routing-instructions.js +4 -655
- package/dist/router/virtual-router/sticky-session-store.d.ts +4 -0
- package/dist/router/virtual-router/sticky-session-store.js +19 -81
- package/dist/router/virtual-router/tool-signals.js +21 -3
- package/dist/router/virtual-router/types.d.ts +4 -0
- package/dist/servertool/clock/session-scope.js +32 -1
- package/dist/servertool/engine.js +79 -8
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +1 -1
- package/dist/servertool/handlers/clock-auto.js +1 -1
- package/dist/servertool/handlers/clock.js +1 -1
- package/dist/servertool/handlers/compaction-detect.d.ts +1 -1
- package/dist/servertool/handlers/compaction-detect.js +1 -1
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
- package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
- package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
- package/dist/servertool/handlers/review.js +1 -1
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +1 -1
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +1 -1
- package/dist/servertool/handlers/vision.js +1 -1
- package/dist/servertool/handlers/web-search.js +1 -1
- package/dist/servertool/reenter-backend.js +1 -1
- package/dist/servertool/server-side-tools.js +2 -2
- package/dist/servertool/stop-gateway-context.js +1 -1
- package/dist/servertool/stop-message-compare-context.js +1 -1
- package/dist/sse/json-to-sse/event-generators/responses.d.ts +4 -0
- package/dist/sse/json-to-sse/event-generators/responses.js +95 -1
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +6 -4
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +8 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +162 -4
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +2 -0
- package/dist/sse/types/responses-types.d.ts +6 -2
- package/dist/tools/apply-patch/structured/coercion.js +5 -0
- package/dist/tools/args-json.js +29 -0
- package/package.json +8 -5
- package/dist/conversion/shared/args-mapping.js +0 -77
- package/dist/conversion/shared/compaction-detect.js +0 -4
- package/dist/conversion/shared/errors.js +0 -31
- package/dist/conversion/shared/media.js +0 -4
- package/dist/conversion/shared/payload-budget.js +0 -165
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +0 -7
- package/dist/conversion/shared/protocol-field-allowlists.js +0 -149
- package/dist/conversion/shared/snapshot-hooks.d.ts +0 -11
- package/dist/conversion/shared/snapshot-hooks.js +0 -503
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +0 -2
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +0 -129
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +0 -4
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +0 -637
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +0 -21
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +0 -177
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +0 -5
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +0 -385
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +0 -10
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +0 -602
- package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +0 -5
- package/dist/conversion/shared/text-markup-normalizer/extractors.js +0 -4
- package/dist/conversion/shared/tool-canonicalizer.d.ts +0 -2
- package/dist/conversion/shared/tool-canonicalizer.js +0 -38
- /package/dist/conversion/{shared/args-mapping.d.ts → args-mapping.d.ts} +0 -0
- /package/dist/conversion/{shared/bridge-actions.d.ts → bridge-actions.d.ts} +0 -0
- /package/dist/conversion/{shared/bridge-id-utils.d.ts → bridge-id-utils.d.ts} +0 -0
- /package/dist/conversion/{shared/bridge-instructions.d.ts → bridge-instructions.d.ts} +0 -0
- /package/dist/conversion/{shared/bridge-metadata.d.ts → bridge-metadata.d.ts} +0 -0
- /package/dist/conversion/{shared/bridge-policies.d.ts → bridge-policies.d.ts} +0 -0
- /package/dist/conversion/{shared/jsonish.d.ts → jsonish.d.ts} +0 -0
- /package/dist/conversion/{shared/mcp-injection.d.ts → mcp-injection.d.ts} +0 -0
- /package/dist/conversion/{shared/media.d.ts → media.d.ts} +0 -0
- /package/dist/conversion/{shared/payload-budget.d.ts → payload-budget.d.ts} +0 -0
- /package/dist/conversion/{shared → types}/bridge-message-types.d.ts +0 -0
- /package/dist/conversion/{shared → types}/bridge-message-types.js +0 -0
|
@@ -83,6 +83,29 @@ export function normalizeModelContextTokens(provider) {
|
|
|
83
83
|
defaultContextTokens: defaultCandidate
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Normalize model-level output token limits (maxTokens / max_output_tokens).
|
|
88
|
+
*/
|
|
89
|
+
export function normalizeModelOutputTokens(provider) {
|
|
90
|
+
const modelsNode = asRecord(provider.models);
|
|
91
|
+
const normalized = {};
|
|
92
|
+
for (const [modelId, modelRaw] of Object.entries(modelsNode)) {
|
|
93
|
+
if (!modelRaw || typeof modelRaw !== 'object') {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const candidate = readOutputTokens(modelRaw);
|
|
97
|
+
if (candidate) {
|
|
98
|
+
normalized[modelId] = candidate;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const configNode = asRecord(provider.config);
|
|
102
|
+
const defaultsNode = asRecord(configNode?.userConfigDefaults);
|
|
103
|
+
const defaultCandidate = readOutputTokens(provider) ?? readOutputTokens(configNode) ?? readOutputTokens(defaultsNode);
|
|
104
|
+
return {
|
|
105
|
+
modelOutputTokens: Object.keys(normalized).length ? normalized : undefined,
|
|
106
|
+
defaultOutputTokens: defaultCandidate
|
|
107
|
+
};
|
|
108
|
+
}
|
|
86
109
|
/**
|
|
87
110
|
* Read context tokens from various field names.
|
|
88
111
|
*/
|
|
@@ -107,6 +130,27 @@ function readContextTokens(record) {
|
|
|
107
130
|
}
|
|
108
131
|
return undefined;
|
|
109
132
|
}
|
|
133
|
+
function readOutputTokens(record) {
|
|
134
|
+
if (!record) {
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
const keys = [
|
|
138
|
+
'maxOutputTokens',
|
|
139
|
+
'max_output_tokens',
|
|
140
|
+
'maxTokens',
|
|
141
|
+
'max_tokens',
|
|
142
|
+
'outputTokens',
|
|
143
|
+
'output_tokens'
|
|
144
|
+
];
|
|
145
|
+
for (const key of keys) {
|
|
146
|
+
const value = record[key];
|
|
147
|
+
const parsed = normalizePositiveInteger(value);
|
|
148
|
+
if (parsed) {
|
|
149
|
+
return parsed;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
110
154
|
/**
|
|
111
155
|
* Normalize positive integer value.
|
|
112
156
|
*/
|
|
@@ -129,6 +129,8 @@ function buildProviderRuntimeEntries(providers) {
|
|
|
129
129
|
responsesConfig: normalizedProvider.responsesConfig,
|
|
130
130
|
streaming: normalizedProvider.streaming,
|
|
131
131
|
modelStreaming: normalizedProvider.modelStreaming,
|
|
132
|
+
modelOutputTokens: normalizedProvider.modelOutputTokens,
|
|
133
|
+
defaultOutputTokens: normalizedProvider.defaultOutputTokens,
|
|
132
134
|
modelContextTokens: normalizedProvider.modelContextTokens,
|
|
133
135
|
defaultContextTokens: normalizedProvider.defaultContextTokens,
|
|
134
136
|
...(normalizedProvider.deepseek ? { deepseek: normalizedProvider.deepseek } : {}),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RouterMetadataInput } from '../../types.js';
|
|
2
2
|
import type { RoutingInstructionState } from '../../routing-instructions.js';
|
|
3
|
-
type RoutingInstructionStateStoreLike = {
|
|
3
|
+
export type RoutingInstructionStateStoreLike = {
|
|
4
4
|
loadSync: (key: string) => RoutingInstructionState | null;
|
|
5
5
|
saveAsync: (key: string, state: RoutingInstructionState | null) => void;
|
|
6
6
|
saveSync?: (key: string, state: RoutingInstructionState | null) => void;
|
|
@@ -8,4 +8,3 @@ type RoutingInstructionStateStoreLike = {
|
|
|
8
8
|
export declare function resolveStopMessageScope(metadata: RouterMetadataInput): string | undefined;
|
|
9
9
|
export declare function getRoutingInstructionState(stickyKey: string | undefined, routingInstructionState: Map<string, RoutingInstructionState>, routingStateStore: RoutingInstructionStateStoreLike): RoutingInstructionState;
|
|
10
10
|
export declare function persistRoutingInstructionState(key: string, state: RoutingInstructionState, routingStateStore: RoutingInstructionStateStoreLike): void;
|
|
11
|
-
export {};
|
|
@@ -32,7 +32,7 @@ export function resolveStopMessageScope(metadata) {
|
|
|
32
32
|
export function getRoutingInstructionState(stickyKey, routingInstructionState, routingStateStore) {
|
|
33
33
|
const key = stickyKey || 'default';
|
|
34
34
|
const existing = routingInstructionState.get(key);
|
|
35
|
-
// 对 session:/conversation: 作用域,在每次读取时尝试从磁盘刷新 stopMessage 相关字段,
|
|
35
|
+
// 对 session:/conversation:/tmux: 作用域,在每次读取时尝试从磁盘刷新 stopMessage 相关字段,
|
|
36
36
|
// 确保 servertool(如 stop_message_auto)通过 sticky-session-store 更新的使用次数
|
|
37
37
|
// 能在 VirtualRouter 日志中实时反映出来。
|
|
38
38
|
if (existing && isPersistentScopeKey(key)) {
|
|
@@ -61,7 +61,7 @@ export function getRoutingInstructionState(stickyKey, routingInstructionState, r
|
|
|
61
61
|
return existing;
|
|
62
62
|
}
|
|
63
63
|
let initial = null;
|
|
64
|
-
// 仅对 session:/conversation: 作用域的 key 尝试从磁盘恢复持久化状态
|
|
64
|
+
// 仅对 session:/conversation:/tmux: 作用域的 key 尝试从磁盘恢复持久化状态
|
|
65
65
|
if (isPersistentScopeKey(key)) {
|
|
66
66
|
initial = routingStateStore.loadSync(key);
|
|
67
67
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ProviderQuotaView } from '../types.js';
|
|
2
|
+
import type { VirtualRouterConfig, VirtualRouterHealthStore } from '../types.js';
|
|
3
|
+
import type { RoutingInstructionStateStoreLike } from '../engine/routing-state/store.js';
|
|
4
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
5
|
+
export declare function updateDeps(engine: VirtualRouterEngine, deps: {
|
|
6
|
+
healthStore?: VirtualRouterHealthStore | null;
|
|
7
|
+
routingStateStore?: RoutingInstructionStateStoreLike | null;
|
|
8
|
+
quotaView?: ProviderQuotaView | null;
|
|
9
|
+
}): void;
|
|
10
|
+
export declare function initialize(engine: VirtualRouterEngine, config: VirtualRouterConfig): void;
|
|
11
|
+
export declare function validateConfig(engine: VirtualRouterEngine, config: VirtualRouterConfig): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { RouteLoadBalancer } from '../load-balancer.js';
|
|
2
|
+
import { StickySessionManager } from '../engine/sticky-session-manager.js';
|
|
3
|
+
import { RoutingClassifier } from '../classifier.js';
|
|
4
|
+
import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync, saveRoutingInstructionStateSync } from '../sticky-session-store.js';
|
|
5
|
+
import { resolveAntigravityAliasReuseCooldownMs, hydrateAntigravityAliasLeaseStoreIfNeeded } from '../engine/antigravity/alias-lease.js';
|
|
6
|
+
import { routeHasTargets, hasPrimaryPool } from './route-utils.js';
|
|
7
|
+
import { DEFAULT_ROUTE, VirtualRouterError, VirtualRouterErrorCode } from '../types.js';
|
|
8
|
+
export function updateDeps(engine, deps) {
|
|
9
|
+
if (!deps || typeof deps !== 'object') {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if ('healthStore' in deps) {
|
|
13
|
+
engine.healthStore = deps.healthStore ?? undefined;
|
|
14
|
+
engine.cooldownManager.updateDeps({ healthStore: engine.healthStore });
|
|
15
|
+
}
|
|
16
|
+
if ('routingStateStore' in deps) {
|
|
17
|
+
engine.routingStateStore =
|
|
18
|
+
deps.routingStateStore ??
|
|
19
|
+
{
|
|
20
|
+
loadSync: loadRoutingInstructionStateSync,
|
|
21
|
+
saveAsync: saveRoutingInstructionStateAsync,
|
|
22
|
+
saveSync: saveRoutingInstructionStateSync
|
|
23
|
+
};
|
|
24
|
+
// Routing state store changes require clearing in-memory cache to avoid stale reads.
|
|
25
|
+
engine.routingInstructionState.clear();
|
|
26
|
+
}
|
|
27
|
+
if ('quotaView' in deps) {
|
|
28
|
+
const prevQuotaEnabled = Boolean(engine.quotaView);
|
|
29
|
+
engine.quotaView = deps.quotaView ?? undefined;
|
|
30
|
+
engine.cooldownManager.updateDeps({ quotaView: engine.quotaView });
|
|
31
|
+
const nextQuotaEnabled = Boolean(engine.quotaView);
|
|
32
|
+
// When quotaView is enabled, health/cooldown decisions must be driven by quotaView only.
|
|
33
|
+
// - Enabling quotaView: clear any legacy router-local cooldown TTLs immediately.
|
|
34
|
+
// - Disabling quotaView: reload legacy cooldown state from health snapshots.
|
|
35
|
+
if (!prevQuotaEnabled && nextQuotaEnabled) {
|
|
36
|
+
engine.cooldownManager.clearAllCooldowns();
|
|
37
|
+
}
|
|
38
|
+
else if (prevQuotaEnabled && !nextQuotaEnabled) {
|
|
39
|
+
engine.cooldownManager.clearAllCooldowns();
|
|
40
|
+
engine.restoreHealthFromStore();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function initialize(engine, config) {
|
|
45
|
+
validateConfig(engine, config);
|
|
46
|
+
engine.routing = config.routing;
|
|
47
|
+
engine.providerRegistry.load(config.providers);
|
|
48
|
+
engine.healthManager.configure(config.health);
|
|
49
|
+
engine.healthConfig = config.health ?? null;
|
|
50
|
+
engine.healthManager.registerProviders(Object.keys(config.providers));
|
|
51
|
+
engine.cooldownManager.clearAllCooldowns();
|
|
52
|
+
engine.restoreHealthFromStore();
|
|
53
|
+
engine.loadBalancer = new RouteLoadBalancer(config.loadBalancing);
|
|
54
|
+
const aliasReuseCooldownMs = resolveAntigravityAliasReuseCooldownMs(config);
|
|
55
|
+
engine.stickySessionManager = new StickySessionManager(aliasReuseCooldownMs);
|
|
56
|
+
hydrateAntigravityAliasLeaseStoreIfNeeded({
|
|
57
|
+
force: true,
|
|
58
|
+
leaseStore: engine.stickySessionManager.getAllStores().aliasLeaseStore,
|
|
59
|
+
persistence: engine.antigravityLeasePersistence,
|
|
60
|
+
aliasReuseCooldownMs: engine.stickySessionManager.getAliasReuseCooldownMs()
|
|
61
|
+
});
|
|
62
|
+
engine.classifier = new RoutingClassifier(config.classifier);
|
|
63
|
+
engine.contextRouting = config.contextRouting ?? { warnRatio: 0.9, hardLimit: false };
|
|
64
|
+
engine.contextAdvisor.configure(engine.contextRouting);
|
|
65
|
+
engine.webSearchForce = config.webSearch?.force === true;
|
|
66
|
+
engine.routeAnalytics.getAllRouteStats().clear();
|
|
67
|
+
for (const routeName of Object.keys(engine.routing)) {
|
|
68
|
+
engine.routeAnalytics.getRouteStats(routeName) ||
|
|
69
|
+
engine.routeAnalytics.incrementRouteStat(routeName, '', {
|
|
70
|
+
timestampMs: Date.now(),
|
|
71
|
+
stopMessage: { active: false }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function validateConfig(engine, config) {
|
|
76
|
+
if (!config.routing || typeof config.routing !== 'object') {
|
|
77
|
+
throw new VirtualRouterError('routing configuration is required', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
78
|
+
}
|
|
79
|
+
if (!config.providers || Object.keys(config.providers).length === 0) {
|
|
80
|
+
throw new VirtualRouterError('providers configuration is required', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
81
|
+
}
|
|
82
|
+
const defaultPools = config.routing[DEFAULT_ROUTE];
|
|
83
|
+
if (!routeHasTargets(engine, defaultPools)) {
|
|
84
|
+
throw new VirtualRouterError('default route must be configured with at least one provider', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
85
|
+
}
|
|
86
|
+
if (!hasPrimaryPool(engine, defaultPools)) {
|
|
87
|
+
throw new VirtualRouterError('default route must define at least one non-backup pool', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
88
|
+
}
|
|
89
|
+
const providerKeys = new Set(Object.keys(config.providers));
|
|
90
|
+
for (const [routeName, pools] of Object.entries(config.routing)) {
|
|
91
|
+
if (!routeHasTargets(engine, pools)) {
|
|
92
|
+
if (routeName === DEFAULT_ROUTE) {
|
|
93
|
+
throw new VirtualRouterError('default route cannot be empty', VirtualRouterErrorCode.CONFIG_ERROR);
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
for (const pool of pools) {
|
|
98
|
+
if (!Array.isArray(pool.targets) || !pool.targets.length) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const providerKey of pool.targets) {
|
|
102
|
+
if (!providerKeys.has(providerKey)) {
|
|
103
|
+
throw new VirtualRouterError(`Route ${routeName} references unknown provider ${providerKey}`, VirtualRouterErrorCode.CONFIG_ERROR);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RoutingFeatures } from '../types.js';
|
|
2
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
3
|
+
export declare function parseDirectProviderModel(engine: VirtualRouterEngine, model: string | undefined): {
|
|
4
|
+
providerId: string;
|
|
5
|
+
modelId: string;
|
|
6
|
+
} | null;
|
|
7
|
+
export declare function shouldFallbackDirectModelForMedia(engine: VirtualRouterEngine, direct: {
|
|
8
|
+
providerId: string;
|
|
9
|
+
modelId: string;
|
|
10
|
+
}, features: RoutingFeatures): boolean;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { routeHasTargets } from './route-utils.js';
|
|
2
|
+
export function parseDirectProviderModel(engine, model) {
|
|
3
|
+
const raw = typeof model === 'string' ? model.trim() : '';
|
|
4
|
+
if (!raw) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const firstDot = raw.indexOf('.');
|
|
8
|
+
if (firstDot <= 0 || firstDot === raw.length - 1) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const providerId = raw.slice(0, firstDot).trim();
|
|
12
|
+
const modelId = raw.slice(firstDot + 1).trim();
|
|
13
|
+
if (!providerId || !modelId) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
if (engine.providerRegistry.listProviderKeys(providerId).length === 0) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return { providerId, modelId };
|
|
20
|
+
}
|
|
21
|
+
export function shouldFallbackDirectModelForMedia(engine, direct, features) {
|
|
22
|
+
if (!features.hasImageAttachment) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const providerId = direct.providerId.trim().toLowerCase();
|
|
26
|
+
const modelId = direct.modelId.trim().toLowerCase();
|
|
27
|
+
if (providerId !== 'qwen') {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
const isQwen35Plus = modelId === 'qwen3.5-plus' || modelId === 'qwen3-5-plus' || modelId === 'qwen3_5-plus';
|
|
31
|
+
if (!isQwen35Plus) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (!(features.hasVideoAttachment === true && features.hasLocalVideoAttachment === true)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return routeHasTargets(engine, engine.routing.vision);
|
|
38
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ProviderErrorEvent, ProviderFailureEvent, ProviderHealthConfig, ProviderSuccessEvent, VirtualRouterHealthSnapshot } from '../types.js';
|
|
2
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
3
|
+
export declare function providerHealthConfig(engine: VirtualRouterEngine): Required<ProviderHealthConfig>;
|
|
4
|
+
export declare function markProviderCooldown(engine: VirtualRouterEngine, providerKey: string, cooldownMs: number | undefined): void;
|
|
5
|
+
export declare function clearProviderCooldown(engine: VirtualRouterEngine, providerKey: string): void;
|
|
6
|
+
export declare function isProviderCoolingDown(engine: VirtualRouterEngine, providerKey: string): boolean;
|
|
7
|
+
export declare function getProviderCooldownRemainingMs(engine: VirtualRouterEngine, providerKey: string): number;
|
|
8
|
+
export declare function restoreHealthFromStore(engine: VirtualRouterEngine): void;
|
|
9
|
+
export declare function buildHealthSnapshot(engine: VirtualRouterEngine): VirtualRouterHealthSnapshot;
|
|
10
|
+
export declare function persistHealthSnapshot(engine: VirtualRouterEngine): void;
|
|
11
|
+
export declare function handleProviderFailure(engine: VirtualRouterEngine, event: ProviderFailureEvent): void;
|
|
12
|
+
export declare function handleProviderError(engine: VirtualRouterEngine, event: ProviderErrorEvent): void;
|
|
13
|
+
export declare function handleProviderSuccess(engine: VirtualRouterEngine, event: ProviderSuccessEvent): void;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { applyAntigravityRiskPolicyImpl, applyQuotaDepletedImpl, applyQuotaRecoveryImpl, applySeriesCooldownImpl, handleProviderFailureImpl, mapProviderErrorImpl } from '../engine/health/index.js';
|
|
2
|
+
import { recordAntigravitySessionLease } from '../engine/antigravity/alias-lease.js';
|
|
3
|
+
export function providerHealthConfig(engine) {
|
|
4
|
+
return engine.healthManager.getConfig();
|
|
5
|
+
}
|
|
6
|
+
export function markProviderCooldown(engine, providerKey, cooldownMs) {
|
|
7
|
+
engine.cooldownManager.markProviderCooldown(providerKey, cooldownMs);
|
|
8
|
+
}
|
|
9
|
+
export function clearProviderCooldown(engine, providerKey) {
|
|
10
|
+
engine.cooldownManager.clearProviderCooldown(providerKey);
|
|
11
|
+
}
|
|
12
|
+
export function isProviderCoolingDown(engine, providerKey) {
|
|
13
|
+
return engine.cooldownManager.isProviderCoolingDown(providerKey);
|
|
14
|
+
}
|
|
15
|
+
export function getProviderCooldownRemainingMs(engine, providerKey) {
|
|
16
|
+
if (!providerKey) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
const expiry = engine.cooldownManager.getCooldownMap().get(providerKey);
|
|
20
|
+
if (!expiry || !Number.isFinite(expiry)) {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
const remaining = Math.floor(expiry - Date.now());
|
|
24
|
+
return remaining > 0 ? remaining : 0;
|
|
25
|
+
}
|
|
26
|
+
export function restoreHealthFromStore(engine) {
|
|
27
|
+
engine.cooldownManager.restoreHealthFromStore();
|
|
28
|
+
}
|
|
29
|
+
export function buildHealthSnapshot(engine) {
|
|
30
|
+
const providers = engine.healthManager.getSnapshot();
|
|
31
|
+
const cooldownSnapshot = engine.cooldownManager.buildHealthSnapshot();
|
|
32
|
+
return { providers, cooldowns: cooldownSnapshot.cooldowns };
|
|
33
|
+
}
|
|
34
|
+
export function persistHealthSnapshot(engine) {
|
|
35
|
+
engine.cooldownManager.persistHealthSnapshot();
|
|
36
|
+
}
|
|
37
|
+
export function handleProviderFailure(engine, event) {
|
|
38
|
+
handleProviderFailureImpl(event, engine.healthManager, providerHealthConfig(engine), (key, ttl) => markProviderCooldown(engine, key, ttl));
|
|
39
|
+
}
|
|
40
|
+
export function handleProviderError(engine, event) {
|
|
41
|
+
if (engine.healthStore && typeof engine.healthStore.recordProviderError === 'function') {
|
|
42
|
+
try {
|
|
43
|
+
engine.healthStore.recordProviderError(event);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// ignore persistence errors
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Quota routing mode: health/cooldown must be driven by quotaView only (host/core quota center).
|
|
50
|
+
// VirtualRouter must not produce or persist its own cooldown state in this mode.
|
|
51
|
+
// 当 Host 注入 quotaView 时,VirtualRouter 的入池/优先级决策应以 quota 为准;
|
|
52
|
+
// 此时不再在 engine-health 内部进行 429/backoff/series cooldown 等健康决策,
|
|
53
|
+
// 以避免与 daemon/quota-center 的长期熔断策略重复维护并导致日志噪声。
|
|
54
|
+
if (engine.quotaView) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Antigravity account safety policy uses router-local cooldown TTLs; only applies when quota routing is disabled.
|
|
58
|
+
applyAntigravityRiskPolicyImpl(event, engine.providerRegistry, engine.healthManager, (key, ttl) => markProviderCooldown(engine, key, ttl), engine.debug);
|
|
59
|
+
// 配额恢复事件优先处理:一旦识别到 virtualRouterQuotaRecovery,
|
|
60
|
+
// 直接清理健康状态/冷却 TTL,避免继续走常规错误映射逻辑。
|
|
61
|
+
const handledByQuota = applyQuotaRecoveryImpl(event, engine.healthManager, (key) => clearProviderCooldown(engine, key), engine.debug);
|
|
62
|
+
if (handledByQuota) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const handledByQuotaDepleted = applyQuotaDepletedImpl(event, engine.healthManager, (key, ttl) => markProviderCooldown(engine, key, ttl), engine.debug);
|
|
66
|
+
if (handledByQuotaDepleted) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
applySeriesCooldownImpl(event, engine.providerRegistry, engine.healthManager, (key, ttl) => markProviderCooldown(engine, key, ttl), engine.debug);
|
|
70
|
+
const derived = mapProviderErrorImpl(event, providerHealthConfig(engine));
|
|
71
|
+
if (!derived) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
handleProviderFailure(engine, derived);
|
|
75
|
+
}
|
|
76
|
+
export function handleProviderSuccess(engine, event) {
|
|
77
|
+
if (!event || typeof event !== 'object') {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const providerKey = event.runtime && typeof event.runtime.providerKey === 'string' ? event.runtime.providerKey.trim() : '';
|
|
81
|
+
if (!providerKey) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const metadata = event.metadata && typeof event.metadata === 'object' && !Array.isArray(event.metadata)
|
|
85
|
+
? event.metadata
|
|
86
|
+
: null;
|
|
87
|
+
if (!metadata) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (providerKey.toLowerCase().startsWith('antigravity.')) {
|
|
91
|
+
recordAntigravitySessionLease({
|
|
92
|
+
metadata: metadata,
|
|
93
|
+
providerKey,
|
|
94
|
+
sessionKey: engine.resolveSessionScope(metadata),
|
|
95
|
+
providerRegistry: engine.providerRegistry,
|
|
96
|
+
leaseStore: engine.stickySessionManager.getAllStores().aliasLeaseStore,
|
|
97
|
+
sessionAliasStore: engine.stickySessionManager.getAllStores().sessionAliasStore,
|
|
98
|
+
persistence: engine.antigravityLeasePersistence,
|
|
99
|
+
aliasReuseCooldownMs: engine.stickySessionManager.getAliasReuseCooldownMs(),
|
|
100
|
+
commitSessionBinding: true,
|
|
101
|
+
debug: engine.debug
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StandardizedMessage } from '../../../conversion/hub/types/standardized.js';
|
|
2
|
+
import type { RouterMetadataInput } from '../types.js';
|
|
3
|
+
import type { RoutingInstructionState } from '../routing-instructions.js';
|
|
4
|
+
export declare const DEFAULT_STOP_MESSAGE_MAX_REPEATS = 10;
|
|
5
|
+
export declare function isStopScopeTraceEnabled(): boolean;
|
|
6
|
+
export declare function normalizeStopMessageStageMode(value: unknown): 'on' | 'off' | 'auto' | undefined;
|
|
7
|
+
export declare function normalizeStopMessageAiMode(value: unknown): 'on' | 'off' | undefined;
|
|
8
|
+
export declare function stripStopMessageFields(state: RoutingInstructionState): RoutingInstructionState;
|
|
9
|
+
export declare function stripClientInjectScopedFields(state: RoutingInstructionState): RoutingInstructionState;
|
|
10
|
+
export declare function hasClientInjectScopedFields(state: RoutingInstructionState | null | undefined): boolean;
|
|
11
|
+
export declare function hasRoutingInstructionMarker(messages: StandardizedMessage[]): boolean;
|
|
12
|
+
export declare function hasLatestUserRoutingInstructionMarker(messages: StandardizedMessage[]): boolean;
|
|
13
|
+
export declare function getLatestUserTextFromResponsesContext(context: unknown): string;
|
|
14
|
+
export declare function hasRoutingInstructionMarkerInResponsesContext(context: unknown): boolean;
|
|
15
|
+
export declare function cleanResponsesContextFromRoutingInstructions(context: unknown): void;
|
|
16
|
+
export declare function isServerToolFollowupRequest(metadata: RouterMetadataInput): boolean;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { extractMessageText } from '../message-utils.js';
|
|
2
|
+
export const DEFAULT_STOP_MESSAGE_MAX_REPEATS = 10;
|
|
3
|
+
export function isStopScopeTraceEnabled() {
|
|
4
|
+
const raw = String(process.env.ROUTECODEX_STOP_SCOPE_TRACE ?? process.env.RCC_STOP_SCOPE_TRACE ?? '')
|
|
5
|
+
.trim()
|
|
6
|
+
.toLowerCase();
|
|
7
|
+
return (raw === '1' ||
|
|
8
|
+
raw === 'true' ||
|
|
9
|
+
raw === 'on' ||
|
|
10
|
+
raw === 'yes' ||
|
|
11
|
+
raw === 'debug' ||
|
|
12
|
+
raw === 'trace' ||
|
|
13
|
+
raw === 'verbose');
|
|
14
|
+
}
|
|
15
|
+
export function normalizeStopMessageStageMode(value) {
|
|
16
|
+
if (typeof value !== 'string') {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const normalized = value.trim().toLowerCase();
|
|
20
|
+
if (normalized === 'on' || normalized === 'off' || normalized === 'auto') {
|
|
21
|
+
return normalized;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
export function normalizeStopMessageAiMode(value) {
|
|
26
|
+
if (typeof value !== 'string') {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const normalized = value.trim().toLowerCase();
|
|
30
|
+
if (normalized === 'on' || normalized === 'off') {
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
export function stripStopMessageFields(state) {
|
|
36
|
+
return {
|
|
37
|
+
...state,
|
|
38
|
+
stopMessageSource: undefined,
|
|
39
|
+
stopMessageText: undefined,
|
|
40
|
+
stopMessageMaxRepeats: undefined,
|
|
41
|
+
stopMessageUsed: undefined,
|
|
42
|
+
stopMessageUpdatedAt: undefined,
|
|
43
|
+
stopMessageLastUsedAt: undefined,
|
|
44
|
+
stopMessageStageMode: undefined,
|
|
45
|
+
stopMessageAiMode: undefined,
|
|
46
|
+
stopMessageAiSeedPrompt: undefined,
|
|
47
|
+
stopMessageAiHistory: undefined
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function stripClientInjectScopedFields(state) {
|
|
51
|
+
return {
|
|
52
|
+
...stripStopMessageFields(state),
|
|
53
|
+
preCommandSource: undefined,
|
|
54
|
+
preCommandScriptPath: undefined,
|
|
55
|
+
preCommandUpdatedAt: undefined
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function hasClientInjectScopedFields(state) {
|
|
59
|
+
if (!state) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return (typeof state.stopMessageText === 'string' ||
|
|
63
|
+
typeof state.stopMessageMaxRepeats === 'number' ||
|
|
64
|
+
typeof state.stopMessageUsed === 'number' ||
|
|
65
|
+
typeof state.stopMessageStageMode === 'string' ||
|
|
66
|
+
typeof state.stopMessageAiMode === 'string' ||
|
|
67
|
+
typeof state.stopMessageAiSeedPrompt === 'string' ||
|
|
68
|
+
Array.isArray(state.stopMessageAiHistory) ||
|
|
69
|
+
typeof state.preCommandScriptPath === 'string' ||
|
|
70
|
+
typeof state.preCommandUpdatedAt === 'number');
|
|
71
|
+
}
|
|
72
|
+
export function hasRoutingInstructionMarker(messages) {
|
|
73
|
+
for (const message of messages) {
|
|
74
|
+
if (!message || message.role !== 'user') {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const content = extractMessageText(message);
|
|
78
|
+
if (!content) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (/<\*\*[\s\S]*?\*\*>/.test(content)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
export function hasLatestUserRoutingInstructionMarker(messages) {
|
|
88
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
89
|
+
const message = messages[idx];
|
|
90
|
+
if (!message) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (message.role !== 'user') {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const content = extractMessageText(message);
|
|
97
|
+
if (!content) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return /<\*\*[\s\S]*?\*\*>/.test(content);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
function extractResponsesInputText(entry) {
|
|
105
|
+
if (typeof entry.content === 'string' && entry.content.trim()) {
|
|
106
|
+
return entry.content;
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(entry.content)) {
|
|
109
|
+
return extractMessageText({ role: 'user', content: entry.content });
|
|
110
|
+
}
|
|
111
|
+
return '';
|
|
112
|
+
}
|
|
113
|
+
export function getLatestUserTextFromResponsesContext(context) {
|
|
114
|
+
if (!context || typeof context !== 'object') {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
const input = context.input;
|
|
118
|
+
if (!Array.isArray(input) || input.length === 0) {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
for (let idx = input.length - 1; idx >= 0; idx -= 1) {
|
|
122
|
+
const entry = input[idx];
|
|
123
|
+
if (!entry || typeof entry !== 'object') {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const record = entry;
|
|
127
|
+
const entryType = typeof record.type === 'string' ? record.type.trim().toLowerCase() : 'message';
|
|
128
|
+
if (entryType !== 'message') {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const role = typeof record.role === 'string' ? record.role.trim().toLowerCase() : 'user';
|
|
132
|
+
if (role !== 'user') {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const text = extractResponsesInputText(record).trim();
|
|
136
|
+
if (text) {
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return '';
|
|
141
|
+
}
|
|
142
|
+
export function hasRoutingInstructionMarkerInResponsesContext(context) {
|
|
143
|
+
const latestUserText = getLatestUserTextFromResponsesContext(context);
|
|
144
|
+
if (!latestUserText) {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
return /<\*\*[\s\S]*?\*\*>/.test(latestUserText);
|
|
148
|
+
}
|
|
149
|
+
export function cleanResponsesContextFromRoutingInstructions(context) {
|
|
150
|
+
if (!context || typeof context !== 'object') {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const record = context;
|
|
154
|
+
if (!Array.isArray(record.input)) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const cleaned = record.input
|
|
158
|
+
.map((entry) => {
|
|
159
|
+
if (!entry || typeof entry !== 'object') {
|
|
160
|
+
return entry;
|
|
161
|
+
}
|
|
162
|
+
const row = entry;
|
|
163
|
+
const entryType = typeof row.type === 'string' ? row.type.trim().toLowerCase() : 'message';
|
|
164
|
+
if (entryType !== 'message') {
|
|
165
|
+
return entry;
|
|
166
|
+
}
|
|
167
|
+
const role = typeof row.role === 'string' ? row.role.trim().toLowerCase() : 'user';
|
|
168
|
+
if (role !== 'user') {
|
|
169
|
+
return entry;
|
|
170
|
+
}
|
|
171
|
+
if (typeof row.content === 'string') {
|
|
172
|
+
const cleanedText = row.content.replace(/<\*\*[\s\S]*?\*\*>/g, '').trim();
|
|
173
|
+
if (!cleanedText) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
return { ...row, content: cleanedText };
|
|
177
|
+
}
|
|
178
|
+
if (Array.isArray(row.content)) {
|
|
179
|
+
const nextContent = row.content
|
|
180
|
+
.map((part) => {
|
|
181
|
+
if (typeof part === 'string') {
|
|
182
|
+
const cleanedText = part.replace(/<\*\*[\s\S]*?\*\*>/g, '').trim();
|
|
183
|
+
return cleanedText ? cleanedText : null;
|
|
184
|
+
}
|
|
185
|
+
if (!part || typeof part !== 'object' || Array.isArray(part)) {
|
|
186
|
+
return part;
|
|
187
|
+
}
|
|
188
|
+
const partRecord = part;
|
|
189
|
+
if (typeof partRecord.text === 'string') {
|
|
190
|
+
const cleanedText = partRecord.text.replace(/<\*\*[\s\S]*?\*\*>/g, '').trim();
|
|
191
|
+
if (!cleanedText) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
return { ...partRecord, text: cleanedText };
|
|
195
|
+
}
|
|
196
|
+
if (typeof partRecord.content === 'string') {
|
|
197
|
+
const cleanedText = partRecord.content.replace(/<\*\*[\s\S]*?\*\*>/g, '').trim();
|
|
198
|
+
if (!cleanedText) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
return { ...partRecord, content: cleanedText };
|
|
202
|
+
}
|
|
203
|
+
return part;
|
|
204
|
+
})
|
|
205
|
+
.filter((part) => part !== null);
|
|
206
|
+
if (!nextContent.length) {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
return { ...row, content: nextContent };
|
|
210
|
+
}
|
|
211
|
+
return entry;
|
|
212
|
+
})
|
|
213
|
+
.filter((entry) => entry !== null);
|
|
214
|
+
record.input = cleaned;
|
|
215
|
+
}
|
|
216
|
+
export function isServerToolFollowupRequest(metadata) {
|
|
217
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
const rt = metadata.__rt;
|
|
221
|
+
if (!rt || typeof rt !== 'object' || Array.isArray(rt)) {
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
const flag = rt.serverToolFollowup;
|
|
225
|
+
return flag === true || (typeof flag === 'string' && flag.trim().toLowerCase() === 'true');
|
|
226
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RoutingInstruction, RoutingInstructionState } from '../routing-instructions.js';
|
|
2
|
+
import type { RouterMetadataInput, TargetMetadata, RoutingDecision, RoutingDiagnostics } from '../types.js';
|
|
3
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
4
|
+
import type { RoutingSelectionResult } from './route-selection.js';
|
|
5
|
+
export declare function finalizeRoutingDecision(engine: VirtualRouterEngine, metadata: RouterMetadataInput, routingState: RoutingInstructionState, metadataInstructions: RoutingInstruction[], instructions: RoutingInstruction[], selectionResult: RoutingSelectionResult): {
|
|
6
|
+
target: TargetMetadata;
|
|
7
|
+
decision: RoutingDecision;
|
|
8
|
+
diagnostics: RoutingDiagnostics;
|
|
9
|
+
};
|