@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
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ClassificationResult, RouterMetadataInput, RoutingFeatures } from '../types.js';
|
|
2
|
+
import type { RoutingInstructionState } from '../routing-instructions.js';
|
|
3
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
4
|
+
export declare function selectProvider(engine: VirtualRouterEngine, requestedRoute: string, metadata: RouterMetadataInput, classification: ClassificationResult, features: RoutingFeatures, routingState?: RoutingInstructionState): {
|
|
5
|
+
providerKey: string;
|
|
6
|
+
routeUsed: string;
|
|
7
|
+
pool: string[];
|
|
8
|
+
poolId?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function selectFromCandidates(engine: VirtualRouterEngine, routes: string[], metadata: RouterMetadataInput, classification: ClassificationResult, features: RoutingFeatures, state: RoutingInstructionState, requiredProviderKeys?: Set<string>, allowAliasRotation?: boolean): {
|
|
11
|
+
providerKey: string;
|
|
12
|
+
routeUsed: string;
|
|
13
|
+
pool: string[];
|
|
14
|
+
poolId?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 在 sticky 模式下,仅在 sticky 池内选择 Provider:
|
|
18
|
+
* - stickyKeySet 表示已经解析并通过健康检查的 providerKey 集合;
|
|
19
|
+
* - 不再依赖 routing[*].targets 中是否挂载这些 key,避免「未初始化路由池」导致 sticky 池为空;
|
|
20
|
+
* - 仍然尊重 allowed/disabledProviders、disabledKeys、disabledModels 以及上下文长度。
|
|
21
|
+
*/
|
|
22
|
+
export declare function selectFromStickyPool(engine: VirtualRouterEngine, stickyKeySet: Set<string>, metadata: RouterMetadataInput, features: RoutingFeatures, state: RoutingInstructionState, allowAliasRotation?: boolean): {
|
|
23
|
+
providerKey: string;
|
|
24
|
+
routeUsed: string;
|
|
25
|
+
pool: string[];
|
|
26
|
+
poolId?: string;
|
|
27
|
+
} | null;
|
|
28
|
+
export declare function extractExcludedProviderKeySet(engine: VirtualRouterEngine, metadata: RouterMetadataInput | undefined): Set<string>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DEFAULT_ROUTE } from '../types.js';
|
|
2
|
+
import { getRoutingInstructionState } from '../engine/routing-state/store.js';
|
|
3
|
+
import { selectProviderImpl } from '../engine/routing-pools/index.js';
|
|
4
|
+
import { extractKeyAlias, extractKeyIndex, extractProviderId, getProviderModelId } from '../engine/provider-key/parse.js';
|
|
5
|
+
export function selectProvider(engine, requestedRoute, metadata, classification, features, routingState) {
|
|
6
|
+
const activeState = routingState ||
|
|
7
|
+
getRoutingInstructionState(engine.resolveStickyKey(metadata), engine.routingInstructionState, engine.routingStateStore);
|
|
8
|
+
return selectProviderImpl(requestedRoute, metadata, classification, features, activeState, {
|
|
9
|
+
routing: engine.routing,
|
|
10
|
+
providerRegistry: engine.providerRegistry,
|
|
11
|
+
healthManager: engine.healthManager,
|
|
12
|
+
contextAdvisor: engine.contextAdvisor,
|
|
13
|
+
loadBalancer: engine.loadBalancer,
|
|
14
|
+
isProviderCoolingDown: (key) => engine.isProviderCoolingDown(key),
|
|
15
|
+
getProviderCooldownRemainingMs: (key) => engine.getProviderCooldownRemainingMs(key),
|
|
16
|
+
resolveStickyKey: (m) => engine.resolveStickyKey(m),
|
|
17
|
+
quotaView: engine.quotaView,
|
|
18
|
+
aliasQueueStore: engine.stickySessionManager.getAllStores().aliasQueueStore,
|
|
19
|
+
antigravityAliasLeaseStore: engine.stickySessionManager.getAllStores().aliasLeaseStore,
|
|
20
|
+
antigravitySessionAliasStore: engine.stickySessionManager.getAllStores().sessionAliasStore,
|
|
21
|
+
antigravityAliasReuseCooldownMs: engine.stickySessionManager.getAliasReuseCooldownMs()
|
|
22
|
+
}, { routingState });
|
|
23
|
+
}
|
|
24
|
+
export function selectFromCandidates(engine, routes, metadata, classification, features, state, requiredProviderKeys, allowAliasRotation) {
|
|
25
|
+
// legacy helper kept for backward compatibility; selection logic moved to engine-selection.ts
|
|
26
|
+
return selectProviderImpl(engine.normalizeRouteAlias(classification.routeName || DEFAULT_ROUTE), metadata, classification, features, state, {
|
|
27
|
+
routing: engine.routing,
|
|
28
|
+
providerRegistry: engine.providerRegistry,
|
|
29
|
+
healthManager: engine.healthManager,
|
|
30
|
+
contextAdvisor: engine.contextAdvisor,
|
|
31
|
+
loadBalancer: engine.loadBalancer,
|
|
32
|
+
isProviderCoolingDown: (key) => engine.isProviderCoolingDown(key),
|
|
33
|
+
getProviderCooldownRemainingMs: (key) => engine.getProviderCooldownRemainingMs(key),
|
|
34
|
+
resolveStickyKey: (m) => engine.resolveStickyKey(m),
|
|
35
|
+
quotaView: engine.quotaView,
|
|
36
|
+
aliasQueueStore: engine.stickySessionManager.getAllStores().aliasQueueStore
|
|
37
|
+
}, { routingState: state });
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 在 sticky 模式下,仅在 sticky 池内选择 Provider:
|
|
41
|
+
* - stickyKeySet 表示已经解析并通过健康检查的 providerKey 集合;
|
|
42
|
+
* - 不再依赖 routing[*].targets 中是否挂载这些 key,避免「未初始化路由池」导致 sticky 池为空;
|
|
43
|
+
* - 仍然尊重 allowed/disabledProviders、disabledKeys、disabledModels 以及上下文长度。
|
|
44
|
+
*/
|
|
45
|
+
export function selectFromStickyPool(engine, stickyKeySet, metadata, features, state, allowAliasRotation) {
|
|
46
|
+
if (!stickyKeySet || stickyKeySet.size === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const allowedProviders = new Set(state.allowedProviders);
|
|
50
|
+
const disabledProviders = new Set(state.disabledProviders);
|
|
51
|
+
const disabledKeysMap = new Map(Array.from(state.disabledKeys.entries()).map(([provider, keys]) => [
|
|
52
|
+
provider,
|
|
53
|
+
new Set(Array.from(keys).map((k) => (typeof k === 'string' ? k : k + 1)))
|
|
54
|
+
]));
|
|
55
|
+
const disabledModels = new Map(Array.from(state.disabledModels.entries()).map(([provider, models]) => [provider, new Set(models)]));
|
|
56
|
+
// 初始候选集合:sticky 池中的所有 key
|
|
57
|
+
// In quota routing mode, cooldown is controlled by quotaView only.
|
|
58
|
+
let candidates = Array.from(stickyKeySet).filter((key) => (engine.quotaView ? true : !engine.isProviderCoolingDown(key)));
|
|
59
|
+
// 应用 provider 白名单 / 黑名单
|
|
60
|
+
if (allowedProviders.size > 0) {
|
|
61
|
+
candidates = candidates.filter((key) => {
|
|
62
|
+
const providerId = extractProviderId(key);
|
|
63
|
+
return providerId && allowedProviders.has(providerId);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (disabledProviders.size > 0) {
|
|
67
|
+
candidates = candidates.filter((key) => {
|
|
68
|
+
const providerId = extractProviderId(key);
|
|
69
|
+
return providerId && !disabledProviders.has(providerId);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// 应用 key / model 级别黑名单
|
|
73
|
+
if (disabledKeysMap.size > 0 || disabledModels.size > 0) {
|
|
74
|
+
candidates = candidates.filter((key) => {
|
|
75
|
+
const providerId = extractProviderId(key);
|
|
76
|
+
if (!providerId) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
const disabledKeys = disabledKeysMap.get(providerId);
|
|
80
|
+
if (disabledKeys && disabledKeys.size > 0) {
|
|
81
|
+
const keyAlias = extractKeyAlias(key);
|
|
82
|
+
const keyIndex = extractKeyIndex(key);
|
|
83
|
+
if (keyAlias && disabledKeys.has(keyAlias)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if (keyIndex !== undefined && disabledKeys.has(keyIndex + 1)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const disabledModelSet = disabledModels.get(providerId);
|
|
91
|
+
if (disabledModelSet && disabledModelSet.size > 0) {
|
|
92
|
+
const modelId = getProviderModelId(key, engine.providerRegistry);
|
|
93
|
+
if (modelId && disabledModelSet.has(modelId)) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
if (!candidates.length) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const stickyKey = allowAliasRotation ? undefined : engine.resolveStickyKey(metadata);
|
|
104
|
+
const estimatedTokens = typeof features.estimatedTokens === 'number' && Number.isFinite(features.estimatedTokens)
|
|
105
|
+
? Math.max(0, features.estimatedTokens)
|
|
106
|
+
: 0;
|
|
107
|
+
// delegate to selection module
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
export function extractExcludedProviderKeySet(engine, metadata) {
|
|
111
|
+
return engine.routeAnalytics.extractExcludedProviderKeySet(metadata);
|
|
112
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RoutingInstructionState } from '../routing-instructions.js';
|
|
2
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
3
|
+
export declare function resolveSelectionPenalty(engine: VirtualRouterEngine, providerKey: string): number | undefined;
|
|
4
|
+
export declare function resolveInstructionProcessModeForSelection(engine: VirtualRouterEngine, providerKey: string, routingState: RoutingInstructionState): 'chat' | 'passthrough' | undefined;
|
|
5
|
+
export declare function resolveInstructionTarget(engine: VirtualRouterEngine, target: NonNullable<RoutingInstructionState['forcedTarget']>): {
|
|
6
|
+
mode: 'exact' | 'filter';
|
|
7
|
+
keys: string[];
|
|
8
|
+
} | null;
|
|
9
|
+
export declare function filterCandidatesByRoutingState(engine: VirtualRouterEngine, routes: string[], state: RoutingInstructionState): string[];
|
|
10
|
+
/**
|
|
11
|
+
* 在已有候选路由集合上,筛选出真正挂载了 sticky 池内 providerKey 的路由,
|
|
12
|
+
* 并按 ROUTE_PRIORITY 进行排序;同时显式排除 tools 路由,保证一旦进入
|
|
13
|
+
* sticky 模式,就不会再命中独立的 tools 池(例如 glm/qwen 工具模型)。
|
|
14
|
+
* 若候选集合中完全没有挂载 sticky key 的路由,则尝试在 default 路由上兜底。
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildStickyRouteCandidatesFromFiltered(engine: VirtualRouterEngine, filteredCandidates: string[], stickyKeySet: Set<string>): string[];
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { DEFAULT_ROUTE } from '../types.js';
|
|
2
|
+
import { extractKeyAlias, extractKeyIndex, extractProviderId, getProviderModelId } from '../engine/provider-key/parse.js';
|
|
3
|
+
import { routeHasTargets, sortByPriority, flattenPoolTargets } from './route-utils.js';
|
|
4
|
+
export function resolveSelectionPenalty(engine, providerKey) {
|
|
5
|
+
if (!engine.quotaView) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
try {
|
|
9
|
+
const entry = engine.quotaView(providerKey);
|
|
10
|
+
const raw = entry?.selectionPenalty;
|
|
11
|
+
if (typeof raw !== 'number' || !Number.isFinite(raw) || raw <= 0) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return Math.floor(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function resolveInstructionProcessModeForSelection(engine, providerKey, routingState) {
|
|
21
|
+
const candidates = [
|
|
22
|
+
routingState.forcedTarget,
|
|
23
|
+
routingState.stickyTarget,
|
|
24
|
+
routingState.preferTarget
|
|
25
|
+
];
|
|
26
|
+
for (const candidate of candidates) {
|
|
27
|
+
const processMode = candidate?.processMode;
|
|
28
|
+
if (!processMode) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const resolved = resolveInstructionTarget(engine, candidate);
|
|
32
|
+
if (!resolved) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (resolved.keys.includes(providerKey)) {
|
|
36
|
+
return processMode;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
export function resolveInstructionTarget(engine, target) {
|
|
42
|
+
if (!target || !target.provider) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const providerId = target.provider;
|
|
46
|
+
const providerKeys = engine.providerRegistry.listProviderKeys(providerId);
|
|
47
|
+
if (providerKeys.length === 0) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const alias = typeof target.keyAlias === 'string' ? target.keyAlias.trim() : '';
|
|
51
|
+
const aliasExplicit = alias.length > 0 && target.pathLength === 3;
|
|
52
|
+
if (aliasExplicit) {
|
|
53
|
+
const runtimeKey = engine.providerRegistry.resolveRuntimeKeyByAlias(providerId, alias);
|
|
54
|
+
if (runtimeKey) {
|
|
55
|
+
return { mode: 'exact', keys: [runtimeKey] };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (typeof target.keyIndex === 'number' && target.keyIndex > 0) {
|
|
59
|
+
const runtimeKey = engine.providerRegistry.resolveRuntimeKeyByIndex(providerId, target.keyIndex);
|
|
60
|
+
if (runtimeKey) {
|
|
61
|
+
return { mode: 'exact', keys: [runtimeKey] };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (target.model && target.model.trim()) {
|
|
65
|
+
const normalizedModel = target.model.trim();
|
|
66
|
+
const matchingKeys = providerKeys.filter((key) => {
|
|
67
|
+
const modelId = getProviderModelId(key, engine.providerRegistry);
|
|
68
|
+
return modelId === normalizedModel;
|
|
69
|
+
});
|
|
70
|
+
if (matchingKeys.length > 0) {
|
|
71
|
+
return { mode: 'filter', keys: matchingKeys };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (alias && !aliasExplicit) {
|
|
75
|
+
const legacyKey = engine.providerRegistry.resolveRuntimeKeyByAlias(providerId, alias);
|
|
76
|
+
if (legacyKey) {
|
|
77
|
+
return { mode: 'exact', keys: [legacyKey] };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { mode: 'filter', keys: providerKeys };
|
|
81
|
+
}
|
|
82
|
+
export function filterCandidatesByRoutingState(engine, routes, state) {
|
|
83
|
+
// console.log('[filter] routes:', routes, 'state:', {
|
|
84
|
+
// allowed: Array.from(state.allowedProviders),
|
|
85
|
+
// disabled: Array.from(state.disabledProviders)
|
|
86
|
+
// });
|
|
87
|
+
if (state.allowedProviders.size === 0 &&
|
|
88
|
+
state.disabledProviders.size === 0 &&
|
|
89
|
+
state.disabledKeys.size === 0 &&
|
|
90
|
+
state.disabledModels.size === 0) {
|
|
91
|
+
return routes;
|
|
92
|
+
}
|
|
93
|
+
return routes.filter((routeName) => {
|
|
94
|
+
const pools = engine.routing[routeName];
|
|
95
|
+
if (!pools)
|
|
96
|
+
return false;
|
|
97
|
+
for (const pool of pools) {
|
|
98
|
+
if (!Array.isArray(pool.targets) || pool.targets.length === 0) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
for (const providerKey of pool.targets) {
|
|
102
|
+
const providerId = extractProviderId(providerKey);
|
|
103
|
+
// console.log('[filter] checking', providerKey, 'id=', providerId);
|
|
104
|
+
if (!providerId)
|
|
105
|
+
continue;
|
|
106
|
+
if (state.allowedProviders.size > 0 && !state.allowedProviders.has(providerId)) {
|
|
107
|
+
// console.log('[filter] dropped by allowed list');
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (state.disabledProviders.has(providerId)) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
const disabledKeys = state.disabledKeys.get(providerId);
|
|
114
|
+
if (disabledKeys && disabledKeys.size > 0) {
|
|
115
|
+
const keyAlias = extractKeyAlias(providerKey);
|
|
116
|
+
const keyIndex = extractKeyIndex(providerKey);
|
|
117
|
+
if (keyAlias && disabledKeys.has(keyAlias)) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (keyIndex !== undefined && disabledKeys.has(keyIndex + 1)) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const disabledModels = state.disabledModels.get(providerId);
|
|
125
|
+
if (disabledModels && disabledModels.size > 0) {
|
|
126
|
+
const modelId = getProviderModelId(providerKey, engine.providerRegistry);
|
|
127
|
+
if (modelId && disabledModels.has(modelId)) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* 在已有候选路由集合上,筛选出真正挂载了 sticky 池内 providerKey 的路由,
|
|
139
|
+
* 并按 ROUTE_PRIORITY 进行排序;同时显式排除 tools 路由,保证一旦进入
|
|
140
|
+
* sticky 模式,就不会再命中独立的 tools 池(例如 glm/qwen 工具模型)。
|
|
141
|
+
* 若候选集合中完全没有挂载 sticky key 的路由,则尝试在 default 路由上兜底。
|
|
142
|
+
*/
|
|
143
|
+
export function buildStickyRouteCandidatesFromFiltered(engine, filteredCandidates, stickyKeySet) {
|
|
144
|
+
const routesWithSticky = [];
|
|
145
|
+
const candidateSet = new Set(filteredCandidates.filter((name) => name && name !== 'tools'));
|
|
146
|
+
for (const routeName of candidateSet) {
|
|
147
|
+
const pools = engine.routing[routeName];
|
|
148
|
+
if (!routeHasTargets(engine, pools)) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
const targets = flattenPoolTargets(engine, pools);
|
|
152
|
+
if (!targets.some((key) => stickyKeySet.has(key))) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
routesWithSticky.push(routeName);
|
|
156
|
+
}
|
|
157
|
+
// 若当前候选路由中没有任何挂载 sticky key 的路由,尝试直接在 default 路由上兜底;
|
|
158
|
+
// 若 default 也不包含 sticky key,则视为 sticky 配置失效,由调用方回落到非 sticky 逻辑。
|
|
159
|
+
if (routesWithSticky.length === 0) {
|
|
160
|
+
const defaultPools = engine.routing[DEFAULT_ROUTE];
|
|
161
|
+
if (routeHasTargets(engine, defaultPools)) {
|
|
162
|
+
const targets = flattenPoolTargets(engine, defaultPools);
|
|
163
|
+
if (targets.some((key) => stickyKeySet.has(key))) {
|
|
164
|
+
return [DEFAULT_ROUTE];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
const ordered = sortByPriority(routesWithSticky);
|
|
170
|
+
const result = [];
|
|
171
|
+
let hasDefault = false;
|
|
172
|
+
for (const routeName of ordered) {
|
|
173
|
+
if (routeName === DEFAULT_ROUTE) {
|
|
174
|
+
hasDefault = true;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!result.includes(routeName)) {
|
|
178
|
+
result.push(routeName);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// default 路由若包含 sticky key,则始终放在候选列表最后,用于 sticky 模式兜底。
|
|
182
|
+
if (hasDefault && !result.includes(DEFAULT_ROUTE)) {
|
|
183
|
+
result.push(DEFAULT_ROUTE);
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
// Intentionally no extra exports here; keep this module focused on state filtering.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PreCommandStateSnapshot, RouterMetadataInput, StopMessageStateSnapshot } from '../types.js';
|
|
2
|
+
import type { VirtualRouterEngine } from '../engine-legacy.js';
|
|
3
|
+
export declare function getStopMessageState(engine: VirtualRouterEngine, metadata: RouterMetadataInput): StopMessageStateSnapshot | null;
|
|
4
|
+
export declare function getPreCommandState(engine: VirtualRouterEngine, metadata: RouterMetadataInput): PreCommandStateSnapshot | null;
|
|
5
|
+
export declare function getStatus(engine: VirtualRouterEngine): {
|
|
6
|
+
routes: Record<string, {
|
|
7
|
+
providers: string[];
|
|
8
|
+
hits: number;
|
|
9
|
+
lastUsedProvider?: string;
|
|
10
|
+
lastHit?: {
|
|
11
|
+
timestampMs: number;
|
|
12
|
+
reason?: string;
|
|
13
|
+
requestTokens?: number;
|
|
14
|
+
selectionPenalty?: number;
|
|
15
|
+
stopMessageActive: boolean;
|
|
16
|
+
stopMessageMode?: "on" | "off" | "auto";
|
|
17
|
+
stopMessageRemaining?: number;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
health: import("../types.js").ProviderHealthState[];
|
|
21
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { resolveStopMessageScope, getRoutingInstructionState, persistRoutingInstructionState } from '../engine/routing-state/store.js';
|
|
2
|
+
import { hasClientInjectScopedFields, stripClientInjectScopedFields } from './helpers.js';
|
|
3
|
+
import { flattenPoolTargets } from './route-utils.js';
|
|
4
|
+
export function getStopMessageState(engine, metadata) {
|
|
5
|
+
const hasArmedStopState = (candidate) => {
|
|
6
|
+
if (!candidate) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const text = typeof candidate.stopMessageText === 'string' ? candidate.stopMessageText.trim() : '';
|
|
10
|
+
const maxRepeats = typeof candidate.stopMessageMaxRepeats === 'number' && Number.isFinite(candidate.stopMessageMaxRepeats)
|
|
11
|
+
? Math.max(1, Math.floor(candidate.stopMessageMaxRepeats))
|
|
12
|
+
: 0;
|
|
13
|
+
const mode = typeof candidate.stopMessageStageMode === 'string'
|
|
14
|
+
? candidate.stopMessageStageMode.trim().toLowerCase()
|
|
15
|
+
: '';
|
|
16
|
+
if (mode === 'off') {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return Boolean(text) && maxRepeats > 0;
|
|
20
|
+
};
|
|
21
|
+
const stopMessageScope = resolveStopMessageScope(metadata);
|
|
22
|
+
if (!stopMessageScope) {
|
|
23
|
+
const sessionScope = engine.resolveSessionScope(metadata);
|
|
24
|
+
if (sessionScope) {
|
|
25
|
+
const legacyState = getRoutingInstructionState(sessionScope, engine.routingInstructionState, engine.routingStateStore);
|
|
26
|
+
if (hasClientInjectScopedFields(legacyState)) {
|
|
27
|
+
const cleared = stripClientInjectScopedFields(legacyState);
|
|
28
|
+
engine.routingInstructionState.set(sessionScope, cleared);
|
|
29
|
+
persistRoutingInstructionState(sessionScope, cleared, engine.routingStateStore);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const effectiveState = getRoutingInstructionState(stopMessageScope, engine.routingInstructionState, engine.routingStateStore);
|
|
35
|
+
if (!hasArmedStopState(effectiveState)) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const text = typeof effectiveState.stopMessageText === 'string' ? effectiveState.stopMessageText.trim() : '';
|
|
39
|
+
const maxRepeats = typeof effectiveState.stopMessageMaxRepeats === 'number' && Number.isFinite(effectiveState.stopMessageMaxRepeats)
|
|
40
|
+
? Math.max(1, Math.floor(effectiveState.stopMessageMaxRepeats))
|
|
41
|
+
: 0;
|
|
42
|
+
const stageModeRaw = typeof effectiveState.stopMessageStageMode === 'string'
|
|
43
|
+
? effectiveState.stopMessageStageMode.trim().toLowerCase()
|
|
44
|
+
: '';
|
|
45
|
+
const stageModeNormalized = stageModeRaw === 'on' || stageModeRaw === 'off' || stageModeRaw === 'auto'
|
|
46
|
+
? stageModeRaw
|
|
47
|
+
: undefined;
|
|
48
|
+
const aiModeRaw = typeof effectiveState.stopMessageAiMode === 'string'
|
|
49
|
+
? effectiveState.stopMessageAiMode.trim().toLowerCase()
|
|
50
|
+
: '';
|
|
51
|
+
const aiModeNormalized = aiModeRaw === 'on' || aiModeRaw === 'off' ? aiModeRaw : undefined;
|
|
52
|
+
if (stageModeNormalized === 'off' || !text || maxRepeats <= 0) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
...(text ? { stopMessageText: text } : {}),
|
|
57
|
+
stopMessageMaxRepeats: maxRepeats,
|
|
58
|
+
...(typeof effectiveState.stopMessageSource === 'string' && effectiveState.stopMessageSource.trim()
|
|
59
|
+
? { stopMessageSource: effectiveState.stopMessageSource.trim() }
|
|
60
|
+
: {}),
|
|
61
|
+
...(typeof effectiveState.stopMessageUsed === 'number' && Number.isFinite(effectiveState.stopMessageUsed)
|
|
62
|
+
? { stopMessageUsed: Math.max(0, Math.floor(effectiveState.stopMessageUsed)) }
|
|
63
|
+
: {}),
|
|
64
|
+
...(typeof effectiveState.stopMessageUpdatedAt === 'number' && Number.isFinite(effectiveState.stopMessageUpdatedAt)
|
|
65
|
+
? { stopMessageUpdatedAt: effectiveState.stopMessageUpdatedAt }
|
|
66
|
+
: {}),
|
|
67
|
+
...(typeof effectiveState.stopMessageLastUsedAt === 'number' && Number.isFinite(effectiveState.stopMessageLastUsedAt)
|
|
68
|
+
? { stopMessageLastUsedAt: effectiveState.stopMessageLastUsedAt }
|
|
69
|
+
: {}),
|
|
70
|
+
...(stageModeNormalized ? { stopMessageStageMode: stageModeNormalized } : {}),
|
|
71
|
+
...(aiModeNormalized ? { stopMessageAiMode: aiModeNormalized } : {}),
|
|
72
|
+
...(typeof effectiveState.stopMessageAiSeedPrompt === 'string' && effectiveState.stopMessageAiSeedPrompt.trim()
|
|
73
|
+
? { stopMessageAiSeedPrompt: effectiveState.stopMessageAiSeedPrompt.trim() }
|
|
74
|
+
: {}),
|
|
75
|
+
...(Array.isArray(effectiveState.stopMessageAiHistory)
|
|
76
|
+
? { stopMessageAiHistory: effectiveState.stopMessageAiHistory }
|
|
77
|
+
: {})
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function getPreCommandState(engine, metadata) {
|
|
81
|
+
const stopMessageScope = resolveStopMessageScope(metadata);
|
|
82
|
+
if (!stopMessageScope) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
const effectiveState = getRoutingInstructionState(stopMessageScope, engine.routingInstructionState, engine.routingStateStore);
|
|
86
|
+
if (!effectiveState) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
const scriptPath = typeof effectiveState.preCommandScriptPath === 'string' ? effectiveState.preCommandScriptPath.trim() : '';
|
|
90
|
+
if (!scriptPath) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
preCommandScriptPath: scriptPath,
|
|
95
|
+
...(typeof effectiveState.preCommandSource === 'string' && effectiveState.preCommandSource.trim()
|
|
96
|
+
? { preCommandSource: effectiveState.preCommandSource.trim() }
|
|
97
|
+
: {}),
|
|
98
|
+
...(typeof effectiveState.preCommandUpdatedAt === 'number' && Number.isFinite(effectiveState.preCommandUpdatedAt)
|
|
99
|
+
? { preCommandUpdatedAt: effectiveState.preCommandUpdatedAt }
|
|
100
|
+
: {})
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function getStatus(engine) {
|
|
104
|
+
const routes = {};
|
|
105
|
+
for (const [route, pools] of Object.entries(engine.routing)) {
|
|
106
|
+
const stats = engine.routeAnalytics.getRouteStats(route);
|
|
107
|
+
routes[route] = {
|
|
108
|
+
providers: flattenPoolTargets(engine, pools),
|
|
109
|
+
hits: stats?.hits ?? 0,
|
|
110
|
+
lastUsedProvider: stats?.lastProvider,
|
|
111
|
+
...(stats?.lastHit ? { lastHit: { ...stats.lastHit } } : {})
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
routes,
|
|
116
|
+
health: engine.healthManager.getSnapshot()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ProviderHealthManager } from './health-manager.js';
|
|
2
|
+
import { ProviderRegistry } from './provider-registry.js';
|
|
3
|
+
import { RouteLoadBalancer } from './load-balancer.js';
|
|
4
|
+
import { RoutingClassifier } from './classifier.js';
|
|
5
|
+
import { ContextAdvisor } from './context-advisor.js';
|
|
6
|
+
import type { ProcessedRequest, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
|
|
7
|
+
import { type RoutingPools, type RoutingDecision, type RoutingDiagnostics, type StopMessageStateSnapshot, type PreCommandStateSnapshot, type RoutePoolTier, type RouterMetadataInput, type RoutingFeatures, type VirtualRouterConfig, type VirtualRouterContextRoutingConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent, type ProviderSuccessEvent, type VirtualRouterHealthStore } from './types.js';
|
|
8
|
+
import type { RoutingInstructionState } from './routing-instructions.js';
|
|
9
|
+
import type { ProviderQuotaView } from './types.js';
|
|
10
|
+
import type { RoutingInstructionStateStoreLike } from './engine/routing-state/store.js';
|
|
11
|
+
import { RouteAnalytics } from './engine/route-analytics.js';
|
|
12
|
+
import { StickySessionManager } from './engine/sticky-session-manager.js';
|
|
13
|
+
import { CooldownManager } from './engine/cooldown-manager.js';
|
|
14
|
+
import { getStatus as getStatusImpl } from './engine-legacy/state-accessors.js';
|
|
15
|
+
export declare class VirtualRouterEngine {
|
|
16
|
+
routing: RoutingPools;
|
|
17
|
+
readonly providerRegistry: ProviderRegistry;
|
|
18
|
+
readonly healthManager: ProviderHealthManager;
|
|
19
|
+
loadBalancer: RouteLoadBalancer;
|
|
20
|
+
classifier: RoutingClassifier;
|
|
21
|
+
readonly contextAdvisor: ContextAdvisor;
|
|
22
|
+
contextRouting: VirtualRouterContextRoutingConfig | undefined;
|
|
23
|
+
readonly routeAnalytics: RouteAnalytics;
|
|
24
|
+
stickySessionManager: StickySessionManager;
|
|
25
|
+
cooldownManager: CooldownManager;
|
|
26
|
+
antigravityLeasePersistence: {
|
|
27
|
+
loadedOnce: boolean;
|
|
28
|
+
loadedMtimeMs: number | null;
|
|
29
|
+
flushTimer: ReturnType<typeof setTimeout> | null;
|
|
30
|
+
};
|
|
31
|
+
readonly debug: Console;
|
|
32
|
+
healthConfig: VirtualRouterConfig['health'] | null;
|
|
33
|
+
readonly statsCenter: import("../../index.js").StatsCenter;
|
|
34
|
+
webSearchForce: boolean;
|
|
35
|
+
healthStore?: VirtualRouterHealthStore;
|
|
36
|
+
routingStateStore: RoutingInstructionStateStoreLike;
|
|
37
|
+
routingInstructionState: Map<string, RoutingInstructionState>;
|
|
38
|
+
quotaView?: ProviderQuotaView;
|
|
39
|
+
/**
|
|
40
|
+
* Backward-compatible test/debug surface used by existing regression scripts.
|
|
41
|
+
* Keep this as a read-only view over StickySessionManager storage.
|
|
42
|
+
*/
|
|
43
|
+
get antigravitySessionAliasStore(): Map<string, string>;
|
|
44
|
+
constructor(deps?: {
|
|
45
|
+
healthStore?: VirtualRouterHealthStore;
|
|
46
|
+
routingStateStore?: RoutingInstructionStateStoreLike;
|
|
47
|
+
quotaView?: ProviderQuotaView;
|
|
48
|
+
});
|
|
49
|
+
updateDeps(deps: {
|
|
50
|
+
healthStore?: VirtualRouterHealthStore | null;
|
|
51
|
+
routingStateStore?: RoutingInstructionStateStoreLike | null;
|
|
52
|
+
quotaView?: ProviderQuotaView | null;
|
|
53
|
+
}): void;
|
|
54
|
+
initialize(config: VirtualRouterConfig): void;
|
|
55
|
+
route(request: StandardizedRequest | ProcessedRequest, metadata: RouterMetadataInput): {
|
|
56
|
+
target: TargetMetadata;
|
|
57
|
+
decision: RoutingDecision;
|
|
58
|
+
diagnostics: RoutingDiagnostics;
|
|
59
|
+
};
|
|
60
|
+
getStopMessageState(metadata: RouterMetadataInput): StopMessageStateSnapshot | null;
|
|
61
|
+
getPreCommandState(metadata: RouterMetadataInput): PreCommandStateSnapshot | null;
|
|
62
|
+
handleProviderFailure(event: ProviderFailureEvent): void;
|
|
63
|
+
handleProviderError(event: ProviderErrorEvent): void;
|
|
64
|
+
handleProviderSuccess(event: ProviderSuccessEvent): void;
|
|
65
|
+
getStatus(): ReturnType<typeof getStatusImpl>;
|
|
66
|
+
normalizeRouteAlias(routeName: string | undefined): string;
|
|
67
|
+
buildRouteCandidates(requestedRoute: string, classificationCandidates: string[] | undefined, features: RoutingFeatures): string[];
|
|
68
|
+
reorderForInlineVision(routeNames: string[]): string[];
|
|
69
|
+
reorderForPreferredModel(routeNames: string[], modelId: string): string[];
|
|
70
|
+
routeSupportsModel(routeName: string, modelId: string): boolean;
|
|
71
|
+
routeSupportsInlineVision(routeName: string): boolean;
|
|
72
|
+
sortByPriority(routeNames: string[]): string[];
|
|
73
|
+
routeWeight(routeName: string): number;
|
|
74
|
+
routeHasForceFlag(routeName: string): boolean;
|
|
75
|
+
routeHasTargets(pools?: RoutePoolTier[]): boolean;
|
|
76
|
+
hasPrimaryPool(pools?: RoutePoolTier[]): boolean;
|
|
77
|
+
sortRoutePools(pools?: RoutePoolTier[]): RoutePoolTier[];
|
|
78
|
+
flattenPoolTargets(pools?: RoutePoolTier[]): string[];
|
|
79
|
+
markProviderCooldown(providerKey: string, cooldownMs: number | undefined): void;
|
|
80
|
+
clearProviderCooldown(providerKey: string): void;
|
|
81
|
+
isProviderCoolingDown(providerKey: string): boolean;
|
|
82
|
+
getProviderCooldownRemainingMs(providerKey: string): number;
|
|
83
|
+
restoreHealthFromStore(): void;
|
|
84
|
+
buildHealthSnapshot(): import("./types.js").VirtualRouterHealthSnapshot;
|
|
85
|
+
persistHealthSnapshot(): void;
|
|
86
|
+
parseDirectProviderModel(model: string | undefined): {
|
|
87
|
+
providerId: string;
|
|
88
|
+
modelId: string;
|
|
89
|
+
} | null;
|
|
90
|
+
shouldFallbackDirectModelForMedia(direct: {
|
|
91
|
+
providerId: string;
|
|
92
|
+
modelId: string;
|
|
93
|
+
}, features: RoutingFeatures): boolean;
|
|
94
|
+
selectProvider(requestedRoute: string, metadata: RouterMetadataInput, classification: any, features: RoutingFeatures, routingState?: RoutingInstructionState): {
|
|
95
|
+
providerKey: string;
|
|
96
|
+
routeUsed: string;
|
|
97
|
+
pool: string[];
|
|
98
|
+
poolId?: string;
|
|
99
|
+
};
|
|
100
|
+
selectFromCandidates(routes: string[], metadata: RouterMetadataInput, classification: any, features: RoutingFeatures, state: RoutingInstructionState, requiredProviderKeys?: Set<string>, allowAliasRotation?: boolean): {
|
|
101
|
+
providerKey: string;
|
|
102
|
+
routeUsed: string;
|
|
103
|
+
pool: string[];
|
|
104
|
+
poolId?: string;
|
|
105
|
+
};
|
|
106
|
+
selectFromStickyPool(stickyKeySet: Set<string>, metadata: RouterMetadataInput, features: RoutingFeatures, state: RoutingInstructionState, allowAliasRotation?: boolean): {
|
|
107
|
+
providerKey: string;
|
|
108
|
+
routeUsed: string;
|
|
109
|
+
pool: string[];
|
|
110
|
+
poolId?: string;
|
|
111
|
+
} | null;
|
|
112
|
+
extractExcludedProviderKeySet(metadata: RouterMetadataInput | undefined): Set<string>;
|
|
113
|
+
resolveSelectionPenalty(providerKey: string): number | undefined;
|
|
114
|
+
resolveInstructionProcessModeForSelection(providerKey: string, routingState: RoutingInstructionState): 'chat' | 'passthrough' | undefined;
|
|
115
|
+
resolveInstructionTarget(target: NonNullable<RoutingInstructionState['forcedTarget']>): {
|
|
116
|
+
mode: 'exact' | 'filter';
|
|
117
|
+
keys: string[];
|
|
118
|
+
} | null;
|
|
119
|
+
buildStickyRouteCandidatesFromFiltered(filteredCandidates: string[], stickyKeySet: Set<string>): string[];
|
|
120
|
+
filterCandidatesByRoutingState(routes: string[], state: RoutingInstructionState): string[];
|
|
121
|
+
resolveStickyKey(metadata: RouterMetadataInput): string | undefined;
|
|
122
|
+
resolveSessionScope(metadata: RouterMetadataInput): string | undefined;
|
|
123
|
+
}
|