@jsonstudio/llms 0.6.1739 → 0.6.1890
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/compat/actions/deepseek-web-request.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-request.js +350 -0
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +3 -0
- package/dist/conversion/compat/actions/deepseek-web-response.js +886 -0
- package/dist/conversion/compat/actions/gemini-cli-request.js +3 -1
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +18 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +166 -2
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +169 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +6 -0
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +12 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -0
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +4 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +365 -144
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +9 -0
- package/dist/conversion/hub/policy/policy-engine.d.ts +2 -0
- package/dist/conversion/hub/policy/policy-engine.js +8 -0
- package/dist/conversion/hub/process/chat-process.js +466 -16
- package/dist/conversion/hub/response/provider-response.js +0 -35
- package/dist/conversion/responses/responses-openai-bridge.d.ts +2 -0
- package/dist/conversion/responses/responses-openai-bridge.js +166 -8
- package/dist/conversion/shared/anthropic-message-utils.js +10 -1
- package/dist/conversion/shared/protocol-field-allowlists.d.ts +2 -2
- package/dist/conversion/shared/protocol-field-allowlists.js +4 -0
- package/dist/conversion/shared/tool-governor.js +102 -0
- package/dist/guidance/index.js +17 -0
- package/dist/router/virtual-router/bootstrap.js +46 -1
- package/dist/router/virtual-router/classifier.js +59 -4
- package/dist/router/virtual-router/engine/health/index.js +6 -6
- package/dist/router/virtual-router/engine/routing-state/store.js +16 -3
- package/dist/router/virtual-router/engine-logging.js +62 -24
- package/dist/router/virtual-router/engine-selection/route-utils.js +20 -20
- package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -2
- package/dist/router/virtual-router/engine.d.ts +3 -1
- package/dist/router/virtual-router/engine.js +359 -39
- package/dist/router/virtual-router/features.js +2 -1
- package/dist/router/virtual-router/pre-command-file-resolver.d.ts +2 -0
- package/dist/router/virtual-router/pre-command-file-resolver.js +90 -0
- package/dist/router/virtual-router/provider-registry.js +3 -1
- package/dist/router/virtual-router/routing-instructions.d.ts +15 -1
- package/dist/router/virtual-router/routing-instructions.js +110 -151
- package/dist/router/virtual-router/routing-pre-command-actions.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-actions.js +26 -0
- package/dist/router/virtual-router/routing-pre-command-parser.d.ts +2 -0
- package/dist/router/virtual-router/routing-pre-command-parser.js +85 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.d.ts +3 -0
- package/dist/router/virtual-router/routing-pre-command-state-codec.js +24 -0
- package/dist/router/virtual-router/routing-stop-message-actions.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-actions.js +96 -0
- package/dist/router/virtual-router/routing-stop-message-parser.d.ts +3 -0
- package/dist/router/virtual-router/routing-stop-message-parser.js +142 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +4 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +85 -0
- package/dist/router/virtual-router/sticky-session-store.js +206 -57
- package/dist/router/virtual-router/stop-message-stage-template-files.d.ts +12 -0
- package/dist/router/virtual-router/stop-message-stage-template-files.js +67 -0
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +5 -0
- package/dist/router/virtual-router/token-file-scanner.d.ts +9 -0
- package/dist/router/virtual-router/token-file-scanner.js +64 -3
- package/dist/router/virtual-router/tool-signals.d.ts +5 -0
- package/dist/router/virtual-router/tool-signals.js +42 -3
- package/dist/router/virtual-router/types.d.ts +19 -1
- package/dist/router/virtual-router/types.js +1 -0
- package/dist/servertool/clock/config.d.ts +1 -1
- package/dist/servertool/clock/config.js +27 -4
- package/dist/servertool/clock/state.js +41 -2
- package/dist/servertool/clock/task-store.d.ts +2 -2
- package/dist/servertool/clock/task-store.js +1 -1
- package/dist/servertool/clock/tasks.d.ts +3 -1
- package/dist/servertool/clock/tasks.js +209 -18
- package/dist/servertool/clock/types.d.ts +17 -0
- package/dist/servertool/continue-execution/log.d.ts +3 -0
- package/dist/servertool/continue-execution/log.js +13 -0
- package/dist/servertool/engine.js +414 -68
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +6 -6
- package/dist/servertool/handlers/clock-auto.js +54 -71
- package/dist/servertool/handlers/clock.js +121 -6
- package/dist/servertool/handlers/continue-execution.d.ts +1 -0
- package/dist/servertool/handlers/continue-execution.js +91 -0
- package/dist/servertool/handlers/followup-request-builder.js +13 -0
- 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/stop-message-auto.js +386 -257
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +43 -0
- package/dist/servertool/handlers/stop-message-stage-policy.js +684 -0
- package/dist/servertool/handlers/vision.js +1 -1
- package/dist/servertool/log/progress-file.d.ts +14 -0
- package/dist/servertool/log/progress-file.js +88 -0
- package/dist/servertool/pre-command-hooks.d.ts +17 -0
- package/dist/servertool/pre-command-hooks.js +491 -0
- package/dist/servertool/registry.d.ts +23 -6
- package/dist/servertool/registry.js +66 -1
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +216 -14
- package/dist/servertool/stop-gateway-context.d.ts +14 -0
- package/dist/servertool/stop-gateway-context.js +167 -0
- package/dist/servertool/stop-message-compare-context.d.ts +24 -0
- package/dist/servertool/stop-message-compare-context.js +133 -0
- package/dist/servertool/types.d.ts +12 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +36 -1
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +3 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +118 -1
- package/dist/tools/apply-patch/args-normalizer/default-actions.js +1 -1
- package/package.json +1 -1
|
@@ -11,6 +11,12 @@ export class RoutingClassifier {
|
|
|
11
11
|
}
|
|
12
12
|
classify(features) {
|
|
13
13
|
const lastToolCategory = features.lastAssistantToolCategory;
|
|
14
|
+
const webSearchDeclared = features.hasWebSearchToolDeclared === true;
|
|
15
|
+
const webSearchIntent = detectWebSearchIntent(features.userTextSample);
|
|
16
|
+
const localToolContinuation = lastToolCategory === 'read' ||
|
|
17
|
+
lastToolCategory === 'write' ||
|
|
18
|
+
lastToolCategory === 'search' ||
|
|
19
|
+
lastToolCategory === 'other';
|
|
14
20
|
const reachedLongContext = features.estimatedTokens >= (this.config.longContextThresholdTokens ?? DEFAULT_LONG_CONTEXT_THRESHOLD);
|
|
15
21
|
const latestMessageFromUser = features.latestMessageFromUser === true;
|
|
16
22
|
const thinkingContinuation = lastToolCategory === 'read';
|
|
@@ -21,6 +27,10 @@ export class RoutingClassifier {
|
|
|
21
27
|
const toolsContinuation = lastToolCategory === 'other';
|
|
22
28
|
const hasToolActivity = features.hasTools || features.hasToolCallResponses;
|
|
23
29
|
const evaluationMap = {
|
|
30
|
+
multimodal: {
|
|
31
|
+
triggered: features.hasImageAttachment,
|
|
32
|
+
reason: 'multimodal:media-detected'
|
|
33
|
+
},
|
|
24
34
|
vision: {
|
|
25
35
|
triggered: features.hasImageAttachment,
|
|
26
36
|
reason: 'vision:media-detected'
|
|
@@ -38,10 +48,16 @@ export class RoutingClassifier {
|
|
|
38
48
|
reason: 'coding:last-tool-write'
|
|
39
49
|
},
|
|
40
50
|
web_search: {
|
|
41
|
-
// web_search
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// web_search 仅由“当前请求”触发:
|
|
52
|
+
// - 显式声明 web_search 工具;或
|
|
53
|
+
// - 用户输入命中联网搜索意图关键词。
|
|
54
|
+
// 不再使用上一轮 websearch 续写,避免连续轮次被粘住。
|
|
55
|
+
triggered: !localToolContinuation && (webSearchDeclared || webSearchIntent),
|
|
56
|
+
reason: webSearchDeclared && webSearchIntent
|
|
57
|
+
? 'web_search:tool+intent'
|
|
58
|
+
: webSearchDeclared
|
|
59
|
+
? 'web_search:tool-declared'
|
|
60
|
+
: 'web_search:intent-keyword'
|
|
45
61
|
},
|
|
46
62
|
search: {
|
|
47
63
|
// search 路由:仅在上一轮 assistant 使用 search 类工具时继续命中,
|
|
@@ -99,6 +115,45 @@ export class RoutingClassifier {
|
|
|
99
115
|
return index >= 0 ? index : ROUTE_PRIORITY.length;
|
|
100
116
|
}
|
|
101
117
|
}
|
|
118
|
+
function detectWebSearchIntent(text) {
|
|
119
|
+
if (!text || !text.trim()) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
const normalized = text.toLowerCase();
|
|
123
|
+
const directKeywords = [
|
|
124
|
+
'web search',
|
|
125
|
+
'web_search',
|
|
126
|
+
'websearch',
|
|
127
|
+
'search the web',
|
|
128
|
+
'internet search',
|
|
129
|
+
'search online',
|
|
130
|
+
'搜索网页',
|
|
131
|
+
'联网搜索',
|
|
132
|
+
'上网搜索',
|
|
133
|
+
'上网查',
|
|
134
|
+
'网上搜',
|
|
135
|
+
'谷歌搜索',
|
|
136
|
+
'google search'
|
|
137
|
+
];
|
|
138
|
+
if (directKeywords.some((keyword) => normalized.includes(keyword))) {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
const enVerb = ['search', 'find', 'lookup', 'look up', 'google'];
|
|
142
|
+
const enNoun = ['web', 'internet', 'online', 'news', 'latest', 'today'];
|
|
143
|
+
const hasEnVerb = enVerb.some((keyword) => normalized.includes(keyword));
|
|
144
|
+
const hasEnNoun = enNoun.some((keyword) => normalized.includes(keyword));
|
|
145
|
+
if (hasEnVerb && hasEnNoun) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
const zhVerb = ['搜索', '查找', '查询', '搜'];
|
|
149
|
+
const zhNoun = ['网络', '联网', '网页', '新闻', '资讯', '实时', '最新', '今天'];
|
|
150
|
+
const hasZhVerb = zhVerb.some((keyword) => text.includes(keyword));
|
|
151
|
+
const hasZhNoun = zhNoun.some((keyword) => text.includes(keyword));
|
|
152
|
+
if (text.includes('上网') || (hasZhVerb && hasZhNoun)) {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
102
157
|
function normalizeList(source, fallback) {
|
|
103
158
|
if (!source || source.length === 0) {
|
|
104
159
|
return fallback;
|
|
@@ -66,17 +66,17 @@ function readEnvDuration(name, fallbackMs) {
|
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
68
|
* 对没有 quotaResetDelay 的 429 错误,在 VirtualRouter 内部维护一个简单的阶梯退避策略:
|
|
69
|
-
* - 默认:第 1 次
|
|
70
|
-
* - 可通过环境变量 ROUTECODEX_RL_SCHEDULE / RCC_RL_SCHEDULE 调整(例如 "
|
|
69
|
+
* - 默认:第 1 次 3 秒,第 2 次 10 秒,第 3 次 31 秒,第 4 次及以上 61 秒封顶;
|
|
70
|
+
* - 可通过环境变量 ROUTECODEX_RL_SCHEDULE / RCC_RL_SCHEDULE 调整(例如 "3s,10s,31s,61s")。
|
|
71
71
|
*
|
|
72
72
|
* 这里的“次数”针对 providerKey 计数,并带有简单的时间窗口:若距离上次 429 超过 24 小时,则重置计数。
|
|
73
73
|
* 该状态仅用于路由决策,不反映在 healthConfig 上,使 Host 与 VirtualRouter 对 429 处理职责清晰分层。
|
|
74
74
|
*/
|
|
75
75
|
const NO_QUOTA_RATE_LIMIT_SCHEDULE_MS = readEnvSchedule('ROUTECODEX_RL_SCHEDULE', [
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
3_000,
|
|
77
|
+
10_000,
|
|
78
|
+
31_000,
|
|
79
|
+
61_000
|
|
80
80
|
]);
|
|
81
81
|
const rateLimitBackoffByProvider = new Map();
|
|
82
82
|
const RATE_LIMIT_RESET_WINDOW_MS = readEnvDuration('ROUTECODEX_RL_RESET_WINDOW', 24 * 60 * 60_000);
|
|
@@ -22,6 +22,11 @@ export function getRoutingInstructionState(stickyKey, routingInstructionState, r
|
|
|
22
22
|
existing.stopMessageUsed = merged.stopMessageUsed;
|
|
23
23
|
existing.stopMessageUpdatedAt = merged.stopMessageUpdatedAt;
|
|
24
24
|
existing.stopMessageLastUsedAt = merged.stopMessageLastUsedAt;
|
|
25
|
+
if (persisted) {
|
|
26
|
+
existing.preCommandSource = persisted.preCommandSource;
|
|
27
|
+
existing.preCommandScriptPath = persisted.preCommandScriptPath;
|
|
28
|
+
existing.preCommandUpdatedAt = persisted.preCommandUpdatedAt;
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
catch {
|
|
27
32
|
// 刷新失败不影响原有内存状态
|
|
@@ -46,7 +51,10 @@ export function getRoutingInstructionState(stickyKey, routingInstructionState, r
|
|
|
46
51
|
stopMessageMaxRepeats: undefined,
|
|
47
52
|
stopMessageUsed: undefined,
|
|
48
53
|
stopMessageUpdatedAt: undefined,
|
|
49
|
-
stopMessageLastUsedAt: undefined
|
|
54
|
+
stopMessageLastUsedAt: undefined,
|
|
55
|
+
preCommandSource: undefined,
|
|
56
|
+
preCommandScriptPath: undefined,
|
|
57
|
+
preCommandUpdatedAt: undefined
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
routingInstructionState.set(key, initial);
|
|
@@ -68,6 +76,8 @@ function isRoutingStateEmpty(state) {
|
|
|
68
76
|
(typeof state.stopMessageUsed !== 'number' || !Number.isFinite(state.stopMessageUsed)) &&
|
|
69
77
|
(typeof state.stopMessageUpdatedAt !== 'number' || !Number.isFinite(state.stopMessageUpdatedAt)) &&
|
|
70
78
|
(typeof state.stopMessageLastUsedAt !== 'number' || !Number.isFinite(state.stopMessageLastUsedAt));
|
|
79
|
+
const noPreCommand = (!state.preCommandScriptPath || !state.preCommandScriptPath.trim()) &&
|
|
80
|
+
(typeof state.preCommandUpdatedAt !== 'number' || !Number.isFinite(state.preCommandUpdatedAt));
|
|
71
81
|
return (noForced &&
|
|
72
82
|
noSticky &&
|
|
73
83
|
noPrefer &&
|
|
@@ -75,7 +85,8 @@ function isRoutingStateEmpty(state) {
|
|
|
75
85
|
noDisabledProviders &&
|
|
76
86
|
noDisabledKeys &&
|
|
77
87
|
noDisabledModels &&
|
|
78
|
-
noStopMessage
|
|
88
|
+
noStopMessage &&
|
|
89
|
+
noPreCommand);
|
|
79
90
|
}
|
|
80
91
|
export function persistRoutingInstructionState(key, state, routingStateStore) {
|
|
81
92
|
if (!key || (!key.startsWith('session:') && !key.startsWith('conversation:'))) {
|
|
@@ -88,7 +99,9 @@ export function persistRoutingInstructionState(key, state, routingStateStore) {
|
|
|
88
99
|
(typeof state.stopMessageMaxRepeats === 'number' && Number.isFinite(state.stopMessageMaxRepeats)) ||
|
|
89
100
|
(typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)) ||
|
|
90
101
|
(typeof state.stopMessageUpdatedAt === 'number' && Number.isFinite(state.stopMessageUpdatedAt)) ||
|
|
91
|
-
(typeof state.stopMessageLastUsedAt === 'number' && Number.isFinite(state.stopMessageLastUsedAt))
|
|
102
|
+
(typeof state.stopMessageLastUsedAt === 'number' && Number.isFinite(state.stopMessageLastUsedAt)) ||
|
|
103
|
+
Boolean(state.preCommandScriptPath && state.preCommandScriptPath.trim()) ||
|
|
104
|
+
(typeof state.preCommandUpdatedAt === 'number' && Number.isFinite(state.preCommandUpdatedAt)));
|
|
92
105
|
if (isRoutingStateEmpty(state)) {
|
|
93
106
|
if (prefersSync) {
|
|
94
107
|
routingStateStore.saveSync(key, null);
|
|
@@ -1,4 +1,46 @@
|
|
|
1
1
|
import { DEFAULT_MODEL_CONTEXT_TOKENS, DEFAULT_ROUTE } from './types.js';
|
|
2
|
+
function summarizeStopMessageRuntime(state) {
|
|
3
|
+
if (!state) {
|
|
4
|
+
return {
|
|
5
|
+
hasAny: false,
|
|
6
|
+
mode: 'unset',
|
|
7
|
+
maxRepeats: 0,
|
|
8
|
+
used: 0,
|
|
9
|
+
remaining: -1,
|
|
10
|
+
active: false
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
const text = typeof state.stopMessageText === 'string' ? state.stopMessageText.trim() : '';
|
|
14
|
+
const safeText = text ? (text.length > 24 ? `${text.slice(0, 21)}…` : text) : undefined;
|
|
15
|
+
const modeRaw = typeof state.stopMessageStageMode === 'string' ? state.stopMessageStageMode.trim().toLowerCase() : '';
|
|
16
|
+
const mode = modeRaw === 'on' || modeRaw === 'off' || modeRaw === 'auto' ? modeRaw : 'unset';
|
|
17
|
+
const maxRepeats = typeof state.stopMessageMaxRepeats === 'number' && Number.isFinite(state.stopMessageMaxRepeats)
|
|
18
|
+
? Math.max(0, Math.floor(state.stopMessageMaxRepeats))
|
|
19
|
+
: 0;
|
|
20
|
+
const used = typeof state.stopMessageUsed === 'number' && Number.isFinite(state.stopMessageUsed)
|
|
21
|
+
? Math.max(0, Math.floor(state.stopMessageUsed))
|
|
22
|
+
: 0;
|
|
23
|
+
const remaining = maxRepeats > 0 ? Math.max(0, maxRepeats - used) : -1;
|
|
24
|
+
const active = mode !== 'off' && maxRepeats > 0 && (Boolean(text) || mode === 'on' || mode === 'auto');
|
|
25
|
+
const updatedAt = typeof state.stopMessageUpdatedAt === 'number' && Number.isFinite(state.stopMessageUpdatedAt)
|
|
26
|
+
? state.stopMessageUpdatedAt
|
|
27
|
+
: undefined;
|
|
28
|
+
const lastUsedAt = typeof state.stopMessageLastUsedAt === 'number' && Number.isFinite(state.stopMessageLastUsedAt)
|
|
29
|
+
? state.stopMessageLastUsedAt
|
|
30
|
+
: undefined;
|
|
31
|
+
const hasAny = Boolean(text) || maxRepeats > 0 || mode !== 'unset' || used > 0;
|
|
32
|
+
return {
|
|
33
|
+
hasAny,
|
|
34
|
+
...(safeText ? { safeText } : {}),
|
|
35
|
+
mode,
|
|
36
|
+
maxRepeats,
|
|
37
|
+
used,
|
|
38
|
+
remaining,
|
|
39
|
+
active,
|
|
40
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
41
|
+
...(lastUsedAt ? { lastUsedAt } : {})
|
|
42
|
+
};
|
|
43
|
+
}
|
|
2
44
|
export function formatStickyScope(scope) {
|
|
3
45
|
if (!scope || scope.trim().length === 0) {
|
|
4
46
|
return undefined;
|
|
@@ -45,6 +87,7 @@ export function describeTargetProvider(providerKey, fallbackModelId) {
|
|
|
45
87
|
}
|
|
46
88
|
function resolveRouteColor(routeName) {
|
|
47
89
|
const map = {
|
|
90
|
+
multimodal: '\x1b[38;5;45m',
|
|
48
91
|
tools: '\x1b[38;5;214m',
|
|
49
92
|
thinking: '\x1b[34m',
|
|
50
93
|
coding: '\x1b[35m',
|
|
@@ -154,24 +197,20 @@ export function formatVirtualRouterHit(routeName, poolId, providerKey, modelId,
|
|
|
154
197
|
const stickyLabel = stickyText ? ` ${stickyColor}[sticky:${stickyText}]${reset}` : '';
|
|
155
198
|
const reasonLabel = hitReason ? ` reason=${hitReason}` : '';
|
|
156
199
|
let stopLabel = '';
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
:
|
|
163
|
-
|
|
164
|
-
?
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
: undefined;
|
|
169
|
-
const parts = [`"${safeText}"`, `${used}/${Math.floor(routingState.stopMessageMaxRepeats)}`];
|
|
170
|
-
if (updatedAt) {
|
|
171
|
-
parts.push(`set=${new Date(updatedAt).toLocaleString(undefined, { hour12: false })}`);
|
|
200
|
+
const stop = summarizeStopMessageRuntime(routingState);
|
|
201
|
+
if (stop.hasAny) {
|
|
202
|
+
const parts = [
|
|
203
|
+
stop.safeText ? `"${stop.safeText}"` : '"(mode-only)"',
|
|
204
|
+
`mode=${stop.mode}`,
|
|
205
|
+
`round=${stop.maxRepeats > 0 ? `${stop.used}/${stop.maxRepeats}` : `${stop.used}/-`}`,
|
|
206
|
+
`active=${stop.active ? 'yes' : 'no'}`,
|
|
207
|
+
`left=${stop.remaining >= 0 ? stop.remaining : 'n/a'}`
|
|
208
|
+
];
|
|
209
|
+
if (stop.updatedAt) {
|
|
210
|
+
parts.push(`set=${new Date(stop.updatedAt).toLocaleString(undefined, { hour12: false })}`);
|
|
172
211
|
}
|
|
173
|
-
if (lastUsedAt) {
|
|
174
|
-
parts.push(`last=${new Date(lastUsedAt).toLocaleString(undefined, { hour12: false })}`);
|
|
212
|
+
if (stop.lastUsedAt) {
|
|
213
|
+
parts.push(`last=${new Date(stop.lastUsedAt).toLocaleString(undefined, { hour12: false })}`);
|
|
175
214
|
}
|
|
176
215
|
stopLabel = ` ${stopColor}[stopMessage:${parts.join(' ')}]${reset}`;
|
|
177
216
|
}
|
|
@@ -184,13 +223,12 @@ export function formatVirtualRouterHit(routeName, poolId, providerKey, modelId,
|
|
|
184
223
|
const stickyText = formatStickyScope(stickyScope);
|
|
185
224
|
const stickyLabel = stickyText ? ` [sticky:${stickyText}]` : '';
|
|
186
225
|
let stopLabel = '';
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
const safeText =
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
stopLabel = ` [stopMessage:"${safeText}" ${used}/${Math.floor(routingState.stopMessageMaxRepeats)}]`;
|
|
226
|
+
const stop = summarizeStopMessageRuntime(routingState);
|
|
227
|
+
if (stop.hasAny) {
|
|
228
|
+
const safeText = stop.safeText ? `"${stop.safeText}"` : '"(mode-only)"';
|
|
229
|
+
const rounds = stop.maxRepeats > 0 ? `${stop.used}/${stop.maxRepeats}` : `${stop.used}/-`;
|
|
230
|
+
const left = stop.remaining >= 0 ? String(stop.remaining) : 'n/a';
|
|
231
|
+
stopLabel = ` [stopMessage:${safeText} mode=${stop.mode} round=${rounds} active=${stop.active ? 'yes' : 'no'} left=${left}]`;
|
|
194
232
|
}
|
|
195
233
|
return `[virtual-router-hit] ${timestamp} ${routeLabel} -> ${providerKey}${modelId ? '.' + modelId : ''}${stickyLabel}${hitReason ? ` reason=${hitReason}` : ''}${stopLabel}`;
|
|
196
234
|
}
|
|
@@ -41,6 +41,8 @@ export function sortRoutePools(pools) {
|
|
|
41
41
|
}
|
|
42
42
|
export function buildRouteCandidates(requestedRoute, classificationCandidates, features, routing, providerRegistry) {
|
|
43
43
|
const forceVision = routeHasForceFlag('vision', routing);
|
|
44
|
+
const hasMultimodalTargets = routeHasTargets(routing.multimodal);
|
|
45
|
+
const hasVisionTargets = routeHasTargets(routing.vision);
|
|
44
46
|
const normalized = normalizeRouteAlias(requestedRoute || DEFAULT_ROUTE);
|
|
45
47
|
const baseList = [];
|
|
46
48
|
if (classificationCandidates && classificationCandidates.length) {
|
|
@@ -51,35 +53,33 @@ export function buildRouteCandidates(requestedRoute, classificationCandidates, f
|
|
|
51
53
|
else if (normalized) {
|
|
52
54
|
baseList.push(normalized);
|
|
53
55
|
}
|
|
54
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
55
|
-
const visionAwareRoutes = [DEFAULT_ROUTE, 'thinking'];
|
|
56
|
-
for (const routeName of visionAwareRoutes) {
|
|
57
|
-
if (routeHasTargets(routing[routeName])) {
|
|
58
|
-
if (!baseList.includes(routeName)) {
|
|
59
|
-
baseList.push(routeName);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
56
|
if (features.hasImageAttachment) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
continue;
|
|
57
|
+
if (hasMultimodalTargets) {
|
|
58
|
+
if (!baseList.includes('multimodal')) {
|
|
59
|
+
baseList.unshift('multimodal');
|
|
69
60
|
}
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
}
|
|
62
|
+
else if (hasVisionTargets) {
|
|
63
|
+
if (!baseList.includes('vision')) {
|
|
64
|
+
baseList.unshift('vision');
|
|
72
65
|
}
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
}
|
|
67
|
+
if (!forceVision && hasMultimodalTargets) {
|
|
68
|
+
const visionAwareRoutes = [DEFAULT_ROUTE, 'thinking'];
|
|
69
|
+
for (const routeName of visionAwareRoutes) {
|
|
70
|
+
if (routeHasTargets(routing[routeName])) {
|
|
71
|
+
if (!baseList.includes(routeName)) {
|
|
72
|
+
baseList.push(routeName);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
let ordered = sortByPriority(baseList);
|
|
79
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
79
|
+
if (features.hasImageAttachment && !forceVision && hasMultimodalTargets) {
|
|
80
80
|
ordered = reorderForInlineVision(ordered, routing, providerRegistry);
|
|
81
81
|
}
|
|
82
|
-
if (features.hasImageAttachment) {
|
|
82
|
+
if (features.hasImageAttachment && hasMultimodalTargets) {
|
|
83
83
|
ordered = reorderForPreferredModel(ordered, 'kimi-k2.5', routing, providerRegistry);
|
|
84
84
|
}
|
|
85
85
|
const deduped = [];
|
|
@@ -384,7 +384,7 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
|
|
|
384
384
|
}
|
|
385
385
|
targets = filtered;
|
|
386
386
|
}
|
|
387
|
-
if (features.hasImageAttachment) {
|
|
387
|
+
if (features.hasImageAttachment && routeName === 'multimodal') {
|
|
388
388
|
const kimiTargets = targets.filter((key) => {
|
|
389
389
|
const modelId = getProviderModelId(key, deps.providerRegistry) ?? '';
|
|
390
390
|
return modelId.trim().toLowerCase() === 'kimi-k2.5';
|
|
@@ -392,7 +392,7 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
|
|
|
392
392
|
if (kimiTargets.length) {
|
|
393
393
|
targets = kimiTargets;
|
|
394
394
|
}
|
|
395
|
-
else
|
|
395
|
+
else {
|
|
396
396
|
const prioritized = [];
|
|
397
397
|
const fallthrough = [];
|
|
398
398
|
for (const key of targets) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RoutingDecision, type RoutingDiagnostics, type StopMessageStateSnapshot, type RouterMetadataInput, type VirtualRouterConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent, type ProviderSuccessEvent, type VirtualRouterHealthStore } from './types.js';
|
|
1
|
+
import { type RoutingDecision, type RoutingDiagnostics, type StopMessageStateSnapshot, type PreCommandStateSnapshot, type RouterMetadataInput, type VirtualRouterConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent, type ProviderSuccessEvent, type VirtualRouterHealthStore } from './types.js';
|
|
2
2
|
import type { ProcessedRequest, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
|
|
3
3
|
import { type RoutingInstructionState } from './routing-instructions.js';
|
|
4
4
|
import type { ProviderQuotaView } from './types.js';
|
|
@@ -48,6 +48,7 @@ export declare class VirtualRouterEngine {
|
|
|
48
48
|
diagnostics: RoutingDiagnostics;
|
|
49
49
|
};
|
|
50
50
|
getStopMessageState(metadata: RouterMetadataInput): StopMessageStateSnapshot | null;
|
|
51
|
+
getPreCommandState(metadata: RouterMetadataInput): PreCommandStateSnapshot | null;
|
|
51
52
|
handleProviderFailure(event: ProviderFailureEvent): void;
|
|
52
53
|
handleProviderError(event: ProviderErrorEvent): void;
|
|
53
54
|
handleProviderSuccess(event: ProviderSuccessEvent): void;
|
|
@@ -70,6 +71,7 @@ export declare class VirtualRouterEngine {
|
|
|
70
71
|
private providerHealthConfig;
|
|
71
72
|
private resolveStickyKey;
|
|
72
73
|
private resolveSessionScope;
|
|
74
|
+
private resolveInstructionProcessModeForSelection;
|
|
73
75
|
private resolveInstructionTarget;
|
|
74
76
|
private filterCandidatesByRoutingState;
|
|
75
77
|
private selectFromCandidates;
|