@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
|
@@ -7,6 +7,7 @@ import { ContextAdvisor } from './context-advisor.js';
|
|
|
7
7
|
import { DEFAULT_ROUTE, ROUTE_PRIORITY, VirtualRouterError, VirtualRouterErrorCode } from './types.js';
|
|
8
8
|
import { getStatsCenter } from '../../telemetry/stats-center.js';
|
|
9
9
|
import { parseRoutingInstructions, applyRoutingInstructions, cleanMessagesFromRoutingInstructions } from './routing-instructions.js';
|
|
10
|
+
import { extractMessageText } from './message-utils.js';
|
|
10
11
|
import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync, saveRoutingInstructionStateSync } from './sticky-session-store.js';
|
|
11
12
|
import { buildHitReason, formatVirtualRouterHit } from './engine-logging.js';
|
|
12
13
|
import { selectDirectProviderModel, selectFromStickyPool, selectProviderImpl } from './engine/routing-pools/index.js';
|
|
@@ -14,8 +15,74 @@ import { applyQuotaDepletedImpl, applyQuotaRecoveryImpl, applySeriesCooldownImpl
|
|
|
14
15
|
import { hydrateAntigravityAliasLeaseStoreIfNeeded, recordAntigravitySessionLease, resolveAntigravityAliasReuseCooldownMs } from './engine/antigravity/alias-lease.js';
|
|
15
16
|
import { buildMetadataInstructions, resolveRoutingMode } from './engine/routing-state/metadata.js';
|
|
16
17
|
import { getRoutingInstructionState, persistRoutingInstructionState, resolveStopMessageScope } from './engine/routing-state/store.js';
|
|
18
|
+
import { validateStopMessageStageTemplatesCompleteness } from './stop-message-stage-template-files.js';
|
|
17
19
|
import { extractKeyAlias, extractKeyIndex, extractProviderId, getProviderModelId } from './engine/provider-key/parse.js';
|
|
18
20
|
import { resolveSessionScope as resolveSessionScopeImpl, resolveStickyKey as resolveStickyKeyImpl } from './engine/routing-state/keys.js';
|
|
21
|
+
function normalizeStopMessageStageMode(value) {
|
|
22
|
+
if (typeof value !== 'string') {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const normalized = value.trim().toLowerCase();
|
|
26
|
+
if (normalized === 'on' || normalized === 'off' || normalized === 'auto') {
|
|
27
|
+
return normalized;
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
function resolveStopMessageStageModeAfterInstructions(instructions, currentMode) {
|
|
32
|
+
let mode = normalizeStopMessageStageMode(currentMode);
|
|
33
|
+
for (const instruction of instructions) {
|
|
34
|
+
if (instruction.type === 'stopMessageClear') {
|
|
35
|
+
mode = undefined;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (instruction.type === 'stopMessageMode') {
|
|
39
|
+
const incomingMode = normalizeStopMessageStageMode(instruction.stopMessageStageMode);
|
|
40
|
+
if (incomingMode) {
|
|
41
|
+
mode = incomingMode;
|
|
42
|
+
}
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (instruction.type === 'stopMessageSet') {
|
|
46
|
+
const incomingMode = normalizeStopMessageStageMode(instruction.stopMessageStageMode);
|
|
47
|
+
if (incomingMode) {
|
|
48
|
+
mode = incomingMode;
|
|
49
|
+
}
|
|
50
|
+
else if (mode === 'off') {
|
|
51
|
+
mode = 'on';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return mode;
|
|
56
|
+
}
|
|
57
|
+
function hasRoutingInstructionMarker(messages) {
|
|
58
|
+
for (const message of messages) {
|
|
59
|
+
if (!message || message.role !== 'user') {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const content = extractMessageText(message);
|
|
63
|
+
if (!content) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (/<\*\*[^*]+\*\*>/.test(content)) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
function hasLatestUserRoutingInstructionMarker(messages) {
|
|
73
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
74
|
+
const message = messages[idx];
|
|
75
|
+
if (!message || message.role !== 'user') {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const content = extractMessageText(message);
|
|
79
|
+
if (!content) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
return /<\*\*[^*]+\*\*>/.test(content);
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
19
86
|
export class VirtualRouterEngine {
|
|
20
87
|
routing = {};
|
|
21
88
|
providerRegistry = new ProviderRegistry();
|
|
@@ -165,34 +232,68 @@ export class VirtualRouterEngine {
|
|
|
165
232
|
if (stopMessageScope) {
|
|
166
233
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
167
234
|
if (typeof sessionState.stopMessageText === 'string' ||
|
|
168
|
-
typeof sessionState.stopMessageMaxRepeats === 'number'
|
|
235
|
+
typeof sessionState.stopMessageMaxRepeats === 'number' ||
|
|
236
|
+
typeof sessionState.stopMessageStageMode === 'string') {
|
|
169
237
|
routingState = {
|
|
170
238
|
...routingState,
|
|
171
239
|
stopMessageText: sessionState.stopMessageText,
|
|
172
240
|
stopMessageMaxRepeats: sessionState.stopMessageMaxRepeats,
|
|
173
241
|
stopMessageUsed: sessionState.stopMessageUsed,
|
|
174
242
|
stopMessageUpdatedAt: sessionState.stopMessageUpdatedAt,
|
|
175
|
-
stopMessageLastUsedAt: sessionState.stopMessageLastUsedAt
|
|
243
|
+
stopMessageLastUsedAt: sessionState.stopMessageLastUsedAt,
|
|
244
|
+
stopMessageStage: sessionState.stopMessageStage,
|
|
245
|
+
stopMessageStageMode: sessionState.stopMessageStageMode,
|
|
246
|
+
stopMessageObservationHash: sessionState.stopMessageObservationHash,
|
|
247
|
+
stopMessageObservationStableCount: sessionState.stopMessageObservationStableCount,
|
|
248
|
+
stopMessageBdWorkState: sessionState.stopMessageBdWorkState
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
if (typeof sessionState.preCommandScriptPath === 'string' && sessionState.preCommandScriptPath.trim()) {
|
|
252
|
+
routingState = {
|
|
253
|
+
...routingState,
|
|
254
|
+
preCommandSource: sessionState.preCommandSource,
|
|
255
|
+
preCommandScriptPath: sessionState.preCommandScriptPath,
|
|
256
|
+
preCommandUpdatedAt: sessionState.preCommandUpdatedAt
|
|
176
257
|
};
|
|
177
258
|
}
|
|
178
259
|
}
|
|
179
260
|
const parsedInstructions = parseRoutingInstructions(request.messages);
|
|
261
|
+
const latestUserHasMarker = hasLatestUserRoutingInstructionMarker(request.messages);
|
|
180
262
|
let instructions = parsedInstructions;
|
|
181
263
|
if (stopMessageScope && parsedInstructions.length > 0) {
|
|
182
264
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
183
|
-
const
|
|
184
|
-
|
|
265
|
+
const hasStaleStopMessageInstruction = !latestUserHasMarker &&
|
|
266
|
+
parsedInstructions.some((entry) => entry.type === 'stopMessageSet' ||
|
|
267
|
+
entry.type === 'stopMessageMode' ||
|
|
268
|
+
entry.type === 'stopMessageClear');
|
|
269
|
+
if (hasStaleStopMessageInstruction) {
|
|
270
|
+
const hasActiveStopState = typeof sessionState.stopMessageText === 'string' ||
|
|
271
|
+
typeof sessionState.stopMessageMaxRepeats === 'number' ||
|
|
272
|
+
typeof sessionState.stopMessageStageMode === 'string';
|
|
273
|
+
const hasStopLifecycleStamp = (typeof sessionState.stopMessageUpdatedAt === 'number' && Number.isFinite(sessionState.stopMessageUpdatedAt)) ||
|
|
274
|
+
(typeof sessionState.stopMessageLastUsedAt === 'number' && Number.isFinite(sessionState.stopMessageLastUsedAt));
|
|
275
|
+
if (hasActiveStopState || hasStopLifecycleStamp) {
|
|
276
|
+
instructions = instructions.filter((entry) => entry.type !== 'stopMessageSet' &&
|
|
277
|
+
entry.type !== 'stopMessageMode' &&
|
|
278
|
+
entry.type !== 'stopMessageClear');
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const hasStopMessageClear = instructions.some((entry) => entry.type === 'stopMessageClear');
|
|
282
|
+
const stopMessageSets = instructions.filter((entry) => entry.type === 'stopMessageSet');
|
|
185
283
|
if (!hasStopMessageClear && stopMessageSets.length > 0) {
|
|
186
284
|
const sessionText = typeof sessionState.stopMessageText === 'string' ? sessionState.stopMessageText.trim() : '';
|
|
187
285
|
const sessionMax = typeof sessionState.stopMessageMaxRepeats === 'number' && Number.isFinite(sessionState.stopMessageMaxRepeats)
|
|
188
286
|
? Math.floor(sessionState.stopMessageMaxRepeats)
|
|
189
287
|
: undefined;
|
|
288
|
+
const sessionMode = normalizeStopMessageStageMode(sessionState.stopMessageStageMode);
|
|
190
289
|
const allSame = stopMessageSets.every((entry) => {
|
|
191
290
|
const entryText = typeof entry.stopMessageText === 'string' ? entry.stopMessageText.trim() : '';
|
|
192
291
|
const entryMax = typeof entry.stopMessageMaxRepeats === 'number' && Number.isFinite(entry.stopMessageMaxRepeats)
|
|
193
292
|
? Math.floor(entry.stopMessageMaxRepeats)
|
|
194
293
|
: undefined;
|
|
195
|
-
|
|
294
|
+
const incomingMode = normalizeStopMessageStageMode(entry.stopMessageStageMode);
|
|
295
|
+
const entryMode = incomingMode ?? (sessionMode === 'off' ? 'on' : sessionMode);
|
|
296
|
+
return Boolean(entryText) && entryText === sessionText && entryMax === sessionMax && entryMode === sessionMode;
|
|
196
297
|
});
|
|
197
298
|
const used = typeof sessionState.stopMessageUsed === 'number' && Number.isFinite(sessionState.stopMessageUsed)
|
|
198
299
|
? Math.max(0, Math.floor(sessionState.stopMessageUsed))
|
|
@@ -201,19 +302,36 @@ export class VirtualRouterEngine {
|
|
|
201
302
|
Number.isFinite(sessionState.stopMessageLastUsedAt);
|
|
202
303
|
const alreadyArmed = used === 0 && !hasLastUsedAt;
|
|
203
304
|
if (allSame && alreadyArmed) {
|
|
204
|
-
instructions =
|
|
305
|
+
instructions = instructions.filter((entry) => entry.type !== 'stopMessageSet');
|
|
205
306
|
}
|
|
206
307
|
}
|
|
207
308
|
}
|
|
208
309
|
// stopMessage must be session-scoped: require explicit sessionId in metadata.
|
|
209
310
|
// This prevents global/default persistence and ensures the trigger matches the setting sessionId.
|
|
210
311
|
if (parsedInstructions.length > 0) {
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
312
|
+
const hasSessionScopedInstruction = parsedInstructions.some((entry) => entry.type === 'stopMessageSet' ||
|
|
313
|
+
entry.type === 'stopMessageMode' ||
|
|
314
|
+
entry.type === 'stopMessageClear' ||
|
|
315
|
+
entry.type === 'preCommandSet' ||
|
|
316
|
+
entry.type === 'preCommandClear');
|
|
317
|
+
if (hasSessionScopedInstruction && !stopMessageScope) {
|
|
318
|
+
throw new VirtualRouterError('[stopMessage/precommand] requires sessionId (e.g. set x-session-id header or metadata.sessionId).', VirtualRouterErrorCode.CONFIG_ERROR, { requestId: metadata.requestId, entryEndpoint: metadata.entryEndpoint });
|
|
214
319
|
}
|
|
215
320
|
}
|
|
216
|
-
if (parsedInstructions.length > 0) {
|
|
321
|
+
if (stopMessageScope && parsedInstructions.length > 0) {
|
|
322
|
+
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
323
|
+
const nextStopMessageMode = resolveStopMessageStageModeAfterInstructions(instructions, sessionState.stopMessageStageMode);
|
|
324
|
+
if (nextStopMessageMode === 'on') {
|
|
325
|
+
const templateCheck = validateStopMessageStageTemplatesCompleteness();
|
|
326
|
+
if (!templateCheck.ok) {
|
|
327
|
+
const detail = templateCheck.missing
|
|
328
|
+
.map((entry) => `${entry.stage}:${entry.ref || '(no-ref)'}:${entry.error || 'invalid'}`)
|
|
329
|
+
.join('; ');
|
|
330
|
+
throw new VirtualRouterError(`[stopMessage] mode=on requires complete stage message files (${detail}).`, VirtualRouterErrorCode.CONFIG_ERROR, { requestId: metadata.requestId, entryEndpoint: metadata.entryEndpoint });
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (hasRoutingInstructionMarker(request.messages)) {
|
|
217
335
|
request.messages = cleanMessagesFromRoutingInstructions(request.messages);
|
|
218
336
|
}
|
|
219
337
|
if (instructions.length > 0) {
|
|
@@ -226,8 +344,9 @@ export class VirtualRouterEngine {
|
|
|
226
344
|
// so servertool triggers always match the setting sessionId.
|
|
227
345
|
if (stopMessageScope) {
|
|
228
346
|
const hasStopMessageSet = instructions.some((entry) => entry.type === 'stopMessageSet');
|
|
347
|
+
const hasStopMessageMode = instructions.some((entry) => entry.type === 'stopMessageMode');
|
|
229
348
|
const hasStopMessageClear = instructions.some((entry) => entry.type === 'stopMessageClear');
|
|
230
|
-
if (hasStopMessageSet || hasStopMessageClear) {
|
|
349
|
+
if (hasStopMessageSet || hasStopMessageMode || hasStopMessageClear) {
|
|
231
350
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
232
351
|
let nextSessionState = {
|
|
233
352
|
...sessionState
|
|
@@ -241,6 +360,11 @@ export class VirtualRouterEngine {
|
|
|
241
360
|
nextSessionState.stopMessageUpdatedAt = clearedAt;
|
|
242
361
|
nextSessionState.stopMessageLastUsedAt = clearedAt;
|
|
243
362
|
nextSessionState.stopMessageSource = undefined;
|
|
363
|
+
nextSessionState.stopMessageStage = undefined;
|
|
364
|
+
nextSessionState.stopMessageStageMode = undefined;
|
|
365
|
+
nextSessionState.stopMessageObservationHash = undefined;
|
|
366
|
+
nextSessionState.stopMessageObservationStableCount = undefined;
|
|
367
|
+
nextSessionState.stopMessageBdWorkState = undefined;
|
|
244
368
|
shouldPersistSessionState = true;
|
|
245
369
|
}
|
|
246
370
|
else if (hasStopMessageSet) {
|
|
@@ -251,7 +375,10 @@ export class VirtualRouterEngine {
|
|
|
251
375
|
const sameMax = typeof sessionState.stopMessageMaxRepeats === 'number' &&
|
|
252
376
|
typeof maxRepeats === 'number' &&
|
|
253
377
|
Math.floor(sessionState.stopMessageMaxRepeats) === Math.floor(maxRepeats);
|
|
254
|
-
const
|
|
378
|
+
const currentMode = normalizeStopMessageStageMode(sessionState.stopMessageStageMode);
|
|
379
|
+
const nextMode = normalizeStopMessageStageMode(routingState.stopMessageStageMode) ?? currentMode;
|
|
380
|
+
const isSameMode = currentMode === nextMode;
|
|
381
|
+
const isSameInstruction = Boolean(text) && sameText && sameMax && isSameMode;
|
|
255
382
|
const used = typeof sessionState.stopMessageUsed === 'number' && Number.isFinite(sessionState.stopMessageUsed)
|
|
256
383
|
? Math.max(0, Math.floor(sessionState.stopMessageUsed))
|
|
257
384
|
: 0;
|
|
@@ -261,16 +388,62 @@ export class VirtualRouterEngine {
|
|
|
261
388
|
nextSessionState.stopMessageText = text || undefined;
|
|
262
389
|
nextSessionState.stopMessageMaxRepeats = maxRepeats;
|
|
263
390
|
nextSessionState.stopMessageSource = 'explicit';
|
|
391
|
+
nextSessionState.stopMessageStageMode = nextMode;
|
|
264
392
|
if (shouldRearm) {
|
|
265
393
|
nextSessionState.stopMessageUsed = 0;
|
|
266
394
|
nextSessionState.stopMessageUpdatedAt =
|
|
267
395
|
typeof routingState.stopMessageUpdatedAt === 'number'
|
|
268
396
|
? routingState.stopMessageUpdatedAt
|
|
269
397
|
: Date.now();
|
|
398
|
+
nextSessionState.stopMessageStage = undefined;
|
|
399
|
+
nextSessionState.stopMessageObservationHash = undefined;
|
|
400
|
+
nextSessionState.stopMessageObservationStableCount = 0;
|
|
401
|
+
nextSessionState.stopMessageBdWorkState = undefined;
|
|
270
402
|
nextSessionState.stopMessageLastUsedAt = undefined;
|
|
271
403
|
shouldPersistSessionState = true;
|
|
272
404
|
}
|
|
273
405
|
}
|
|
406
|
+
else if (hasStopMessageMode) {
|
|
407
|
+
const mode = normalizeStopMessageStageMode(routingState.stopMessageStageMode);
|
|
408
|
+
const modeMaxRepeats = typeof routingState.stopMessageMaxRepeats === 'number' && Number.isFinite(routingState.stopMessageMaxRepeats)
|
|
409
|
+
? Math.floor(routingState.stopMessageMaxRepeats)
|
|
410
|
+
: 0;
|
|
411
|
+
if (mode === 'off') {
|
|
412
|
+
const changed = typeof nextSessionState.stopMessageText === 'string' ||
|
|
413
|
+
typeof nextSessionState.stopMessageMaxRepeats === 'number' ||
|
|
414
|
+
typeof nextSessionState.stopMessageUsed === 'number' ||
|
|
415
|
+
typeof nextSessionState.stopMessageSource === 'string' ||
|
|
416
|
+
typeof nextSessionState.stopMessageStage === 'string' ||
|
|
417
|
+
typeof nextSessionState.stopMessageObservationHash === 'string' ||
|
|
418
|
+
typeof nextSessionState.stopMessageObservationStableCount === 'number' ||
|
|
419
|
+
typeof nextSessionState.stopMessageBdWorkState === 'string' ||
|
|
420
|
+
normalizeStopMessageStageMode(nextSessionState.stopMessageStageMode) !== 'off';
|
|
421
|
+
nextSessionState.stopMessageText = undefined;
|
|
422
|
+
nextSessionState.stopMessageMaxRepeats = undefined;
|
|
423
|
+
nextSessionState.stopMessageUsed = undefined;
|
|
424
|
+
nextSessionState.stopMessageSource = undefined;
|
|
425
|
+
nextSessionState.stopMessageStage = undefined;
|
|
426
|
+
nextSessionState.stopMessageStageMode = 'off';
|
|
427
|
+
nextSessionState.stopMessageObservationHash = undefined;
|
|
428
|
+
nextSessionState.stopMessageObservationStableCount = undefined;
|
|
429
|
+
nextSessionState.stopMessageBdWorkState = undefined;
|
|
430
|
+
nextSessionState.stopMessageUpdatedAt = Date.now();
|
|
431
|
+
nextSessionState.stopMessageLastUsedAt = undefined;
|
|
432
|
+
shouldPersistSessionState = changed;
|
|
433
|
+
}
|
|
434
|
+
else if (mode) {
|
|
435
|
+
if (normalizeStopMessageStageMode(nextSessionState.stopMessageStageMode) !== mode) {
|
|
436
|
+
nextSessionState.stopMessageStageMode = mode;
|
|
437
|
+
shouldPersistSessionState = true;
|
|
438
|
+
}
|
|
439
|
+
if (modeMaxRepeats > 0 &&
|
|
440
|
+
(typeof nextSessionState.stopMessageMaxRepeats !== 'number' ||
|
|
441
|
+
Math.floor(nextSessionState.stopMessageMaxRepeats) !== modeMaxRepeats)) {
|
|
442
|
+
nextSessionState.stopMessageMaxRepeats = modeMaxRepeats;
|
|
443
|
+
shouldPersistSessionState = true;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
274
447
|
if (shouldPersistSessionState) {
|
|
275
448
|
this.routingInstructionState.set(stopMessageScope, nextSessionState);
|
|
276
449
|
persistRoutingInstructionState(stopMessageScope, nextSessionState, this.routingStateStore);
|
|
@@ -280,25 +453,83 @@ export class VirtualRouterEngine {
|
|
|
280
453
|
}
|
|
281
454
|
// 日志展示使用 session scope 的 stopMessage 状态,避免每次解析重复刷新时间/次数。
|
|
282
455
|
if (typeof nextSessionState.stopMessageText === 'string' ||
|
|
283
|
-
typeof nextSessionState.stopMessageMaxRepeats === 'number'
|
|
456
|
+
typeof nextSessionState.stopMessageMaxRepeats === 'number' ||
|
|
457
|
+
typeof nextSessionState.stopMessageStageMode === 'string') {
|
|
284
458
|
routingState.stopMessageText = nextSessionState.stopMessageText;
|
|
285
459
|
routingState.stopMessageMaxRepeats = nextSessionState.stopMessageMaxRepeats;
|
|
286
460
|
routingState.stopMessageUsed = nextSessionState.stopMessageUsed;
|
|
287
461
|
routingState.stopMessageUpdatedAt = nextSessionState.stopMessageUpdatedAt;
|
|
288
462
|
routingState.stopMessageLastUsedAt = nextSessionState.stopMessageLastUsedAt;
|
|
463
|
+
routingState.stopMessageStage = nextSessionState.stopMessageStage;
|
|
464
|
+
routingState.stopMessageStageMode = nextSessionState.stopMessageStageMode;
|
|
465
|
+
routingState.stopMessageObservationHash = nextSessionState.stopMessageObservationHash;
|
|
466
|
+
routingState.stopMessageObservationStableCount = nextSessionState.stopMessageObservationStableCount;
|
|
467
|
+
routingState.stopMessageBdWorkState = nextSessionState.stopMessageBdWorkState;
|
|
289
468
|
}
|
|
290
469
|
}
|
|
291
470
|
}
|
|
292
471
|
}
|
|
472
|
+
if (instructions.length > 0 && stopMessageScope) {
|
|
473
|
+
const hasPreCommandSet = instructions.some((entry) => entry.type === 'preCommandSet');
|
|
474
|
+
const hasPreCommandClear = instructions.some((entry) => entry.type === 'preCommandClear');
|
|
475
|
+
if (hasPreCommandSet || hasPreCommandClear) {
|
|
476
|
+
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
477
|
+
const nextSessionState = {
|
|
478
|
+
...sessionState
|
|
479
|
+
};
|
|
480
|
+
let changed = false;
|
|
481
|
+
if (hasPreCommandClear) {
|
|
482
|
+
changed =
|
|
483
|
+
typeof sessionState.preCommandScriptPath === 'string' ||
|
|
484
|
+
typeof sessionState.preCommandSource === 'string' ||
|
|
485
|
+
typeof sessionState.preCommandUpdatedAt === 'number';
|
|
486
|
+
nextSessionState.preCommandScriptPath = undefined;
|
|
487
|
+
nextSessionState.preCommandSource = undefined;
|
|
488
|
+
nextSessionState.preCommandUpdatedAt = Date.now();
|
|
489
|
+
}
|
|
490
|
+
if (hasPreCommandSet) {
|
|
491
|
+
const scriptPath = typeof routingState.preCommandScriptPath === 'string' ? routingState.preCommandScriptPath.trim() : '';
|
|
492
|
+
if (scriptPath) {
|
|
493
|
+
if (sessionState.preCommandScriptPath !== scriptPath) {
|
|
494
|
+
changed = true;
|
|
495
|
+
}
|
|
496
|
+
nextSessionState.preCommandScriptPath = scriptPath;
|
|
497
|
+
nextSessionState.preCommandSource = 'explicit';
|
|
498
|
+
nextSessionState.preCommandUpdatedAt =
|
|
499
|
+
typeof routingState.preCommandUpdatedAt === 'number' && Number.isFinite(routingState.preCommandUpdatedAt)
|
|
500
|
+
? routingState.preCommandUpdatedAt
|
|
501
|
+
: Date.now();
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
if (changed) {
|
|
505
|
+
this.routingInstructionState.set(stopMessageScope, nextSessionState);
|
|
506
|
+
persistRoutingInstructionState(stopMessageScope, nextSessionState, this.routingStateStore);
|
|
507
|
+
routingState.preCommandScriptPath = nextSessionState.preCommandScriptPath;
|
|
508
|
+
routingState.preCommandSource = nextSessionState.preCommandSource;
|
|
509
|
+
routingState.preCommandUpdatedAt = nextSessionState.preCommandUpdatedAt;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
293
513
|
if (instructions.length === 0 && stopMessageScope) {
|
|
294
514
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
295
515
|
if (typeof sessionState.stopMessageText === 'string' ||
|
|
296
|
-
typeof sessionState.stopMessageMaxRepeats === 'number'
|
|
516
|
+
typeof sessionState.stopMessageMaxRepeats === 'number' ||
|
|
517
|
+
typeof sessionState.stopMessageStageMode === 'string') {
|
|
297
518
|
routingState.stopMessageText = sessionState.stopMessageText;
|
|
298
519
|
routingState.stopMessageMaxRepeats = sessionState.stopMessageMaxRepeats;
|
|
299
520
|
routingState.stopMessageUsed = sessionState.stopMessageUsed;
|
|
300
521
|
routingState.stopMessageUpdatedAt = sessionState.stopMessageUpdatedAt;
|
|
301
522
|
routingState.stopMessageLastUsedAt = sessionState.stopMessageLastUsedAt;
|
|
523
|
+
routingState.stopMessageStage = sessionState.stopMessageStage;
|
|
524
|
+
routingState.stopMessageStageMode = sessionState.stopMessageStageMode;
|
|
525
|
+
routingState.stopMessageObservationHash = sessionState.stopMessageObservationHash;
|
|
526
|
+
routingState.stopMessageObservationStableCount = sessionState.stopMessageObservationStableCount;
|
|
527
|
+
routingState.stopMessageBdWorkState = sessionState.stopMessageBdWorkState;
|
|
528
|
+
}
|
|
529
|
+
if (typeof sessionState.preCommandScriptPath === 'string' && sessionState.preCommandScriptPath.trim()) {
|
|
530
|
+
routingState.preCommandScriptPath = sessionState.preCommandScriptPath;
|
|
531
|
+
routingState.preCommandSource = sessionState.preCommandSource;
|
|
532
|
+
routingState.preCommandUpdatedAt = sessionState.preCommandUpdatedAt;
|
|
302
533
|
}
|
|
303
534
|
}
|
|
304
535
|
// Guardrail: if a session is restricted to providers that do not exist in any routing pools,
|
|
@@ -517,12 +748,16 @@ export class VirtualRouterEngine {
|
|
|
517
748
|
}
|
|
518
749
|
}
|
|
519
750
|
const baseTarget = this.providerRegistry.buildTarget(selection.providerKey);
|
|
520
|
-
const forceVision = this.routeHasForceFlag('vision');
|
|
751
|
+
const forceVision = selection.routeUsed === 'vision' && this.routeHasForceFlag('vision');
|
|
521
752
|
const target = {
|
|
522
753
|
...baseTarget,
|
|
523
754
|
...(this.webSearchForce ? { forceWebSearch: true } : {}),
|
|
524
755
|
...(forceVision ? { forceVision: true } : {})
|
|
525
756
|
};
|
|
757
|
+
const instructionProcessMode = this.resolveInstructionProcessModeForSelection(selection.providerKey, routingState);
|
|
758
|
+
if (instructionProcessMode) {
|
|
759
|
+
target.processMode = instructionProcessMode;
|
|
760
|
+
}
|
|
526
761
|
recordAntigravitySessionLease({
|
|
527
762
|
metadata: features.metadata,
|
|
528
763
|
providerKey: selection.providerKey,
|
|
@@ -585,6 +820,20 @@ export class VirtualRouterEngine {
|
|
|
585
820
|
};
|
|
586
821
|
}
|
|
587
822
|
getStopMessageState(metadata) {
|
|
823
|
+
const hasArmedStopState = (candidate) => {
|
|
824
|
+
if (!candidate) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
const text = typeof candidate.stopMessageText === 'string' ? candidate.stopMessageText.trim() : '';
|
|
828
|
+
const maxRepeats = typeof candidate.stopMessageMaxRepeats === 'number' && Number.isFinite(candidate.stopMessageMaxRepeats)
|
|
829
|
+
? Math.max(1, Math.floor(candidate.stopMessageMaxRepeats))
|
|
830
|
+
: 0;
|
|
831
|
+
const mode = typeof candidate.stopMessageStageMode === 'string'
|
|
832
|
+
? candidate.stopMessageStageMode.trim().toLowerCase()
|
|
833
|
+
: '';
|
|
834
|
+
const allowModeOnly = !text && maxRepeats > 0 && (mode === 'on' || mode === 'auto');
|
|
835
|
+
return (Boolean(text) || allowModeOnly) && maxRepeats > 0;
|
|
836
|
+
};
|
|
588
837
|
const sessionScope = this.resolveSessionScope(metadata);
|
|
589
838
|
const sessionState = sessionScope
|
|
590
839
|
? getRoutingInstructionState(sessionScope, this.routingInstructionState, this.routingStateStore)
|
|
@@ -593,9 +842,11 @@ export class VirtualRouterEngine {
|
|
|
593
842
|
const stickyState = stickyKey
|
|
594
843
|
? getRoutingInstructionState(stickyKey, this.routingInstructionState, this.routingStateStore)
|
|
595
844
|
: null;
|
|
596
|
-
const effectiveState = sessionState
|
|
845
|
+
const effectiveState = hasArmedStopState(sessionState)
|
|
597
846
|
? sessionState
|
|
598
|
-
: stickyState
|
|
847
|
+
: hasArmedStopState(stickyState)
|
|
848
|
+
? stickyState
|
|
849
|
+
: null;
|
|
599
850
|
if (!effectiveState) {
|
|
600
851
|
return null;
|
|
601
852
|
}
|
|
@@ -604,11 +855,18 @@ export class VirtualRouterEngine {
|
|
|
604
855
|
Number.isFinite(effectiveState.stopMessageMaxRepeats)
|
|
605
856
|
? Math.max(1, Math.floor(effectiveState.stopMessageMaxRepeats))
|
|
606
857
|
: 0;
|
|
607
|
-
|
|
858
|
+
const stageModeRaw = typeof effectiveState.stopMessageStageMode === 'string'
|
|
859
|
+
? effectiveState.stopMessageStageMode.trim().toLowerCase()
|
|
860
|
+
: '';
|
|
861
|
+
const stageModeNormalized = stageModeRaw === 'on' || stageModeRaw === 'off' || stageModeRaw === 'auto'
|
|
862
|
+
? stageModeRaw
|
|
863
|
+
: undefined;
|
|
864
|
+
const allowModeOnly = !text && maxRepeats > 0 && (stageModeNormalized === 'on' || stageModeNormalized === 'auto');
|
|
865
|
+
if ((!text && !allowModeOnly) || maxRepeats <= 0) {
|
|
608
866
|
return null;
|
|
609
867
|
}
|
|
610
868
|
return {
|
|
611
|
-
stopMessageText: text,
|
|
869
|
+
...(text ? { stopMessageText: text } : {}),
|
|
612
870
|
stopMessageMaxRepeats: maxRepeats,
|
|
613
871
|
...(typeof effectiveState.stopMessageSource === 'string' && effectiveState.stopMessageSource.trim()
|
|
614
872
|
? { stopMessageSource: effectiveState.stopMessageSource.trim() }
|
|
@@ -623,6 +881,49 @@ export class VirtualRouterEngine {
|
|
|
623
881
|
...(typeof effectiveState.stopMessageLastUsedAt === 'number' &&
|
|
624
882
|
Number.isFinite(effectiveState.stopMessageLastUsedAt)
|
|
625
883
|
? { stopMessageLastUsedAt: effectiveState.stopMessageLastUsedAt }
|
|
884
|
+
: {}),
|
|
885
|
+
...(typeof effectiveState.stopMessageStage === 'string' && effectiveState.stopMessageStage.trim()
|
|
886
|
+
? { stopMessageStage: effectiveState.stopMessageStage.trim() }
|
|
887
|
+
: {}),
|
|
888
|
+
...(stageModeNormalized ? { stopMessageStageMode: stageModeNormalized } : {}),
|
|
889
|
+
...(typeof effectiveState.stopMessageObservationHash === 'string' && effectiveState.stopMessageObservationHash.trim()
|
|
890
|
+
? { stopMessageObservationHash: effectiveState.stopMessageObservationHash.trim() }
|
|
891
|
+
: {}),
|
|
892
|
+
...(typeof effectiveState.stopMessageObservationStableCount === 'number' &&
|
|
893
|
+
Number.isFinite(effectiveState.stopMessageObservationStableCount)
|
|
894
|
+
? { stopMessageObservationStableCount: Math.max(0, Math.floor(effectiveState.stopMessageObservationStableCount)) }
|
|
895
|
+
: {}),
|
|
896
|
+
...(typeof effectiveState.stopMessageBdWorkState === 'string' && effectiveState.stopMessageBdWorkState.trim()
|
|
897
|
+
? { stopMessageBdWorkState: effectiveState.stopMessageBdWorkState.trim() }
|
|
898
|
+
: {})
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
getPreCommandState(metadata) {
|
|
902
|
+
const sessionScope = this.resolveSessionScope(metadata);
|
|
903
|
+
const sessionState = sessionScope
|
|
904
|
+
? getRoutingInstructionState(sessionScope, this.routingInstructionState, this.routingStateStore)
|
|
905
|
+
: null;
|
|
906
|
+
const stickyKey = this.resolveStickyKey(metadata);
|
|
907
|
+
const stickyState = stickyKey
|
|
908
|
+
? getRoutingInstructionState(stickyKey, this.routingInstructionState, this.routingStateStore)
|
|
909
|
+
: null;
|
|
910
|
+
const effectiveState = sessionState && typeof sessionState.preCommandScriptPath === 'string' && sessionState.preCommandScriptPath.trim()
|
|
911
|
+
? sessionState
|
|
912
|
+
: stickyState;
|
|
913
|
+
if (!effectiveState) {
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
const scriptPath = typeof effectiveState.preCommandScriptPath === 'string' ? effectiveState.preCommandScriptPath.trim() : '';
|
|
917
|
+
if (!scriptPath) {
|
|
918
|
+
return null;
|
|
919
|
+
}
|
|
920
|
+
return {
|
|
921
|
+
preCommandScriptPath: scriptPath,
|
|
922
|
+
...(typeof effectiveState.preCommandSource === 'string' && effectiveState.preCommandSource.trim()
|
|
923
|
+
? { preCommandSource: effectiveState.preCommandSource.trim() }
|
|
924
|
+
: {}),
|
|
925
|
+
...(typeof effectiveState.preCommandUpdatedAt === 'number' && Number.isFinite(effectiveState.preCommandUpdatedAt)
|
|
926
|
+
? { preCommandUpdatedAt: effectiveState.preCommandUpdatedAt }
|
|
626
927
|
: {})
|
|
627
928
|
};
|
|
628
929
|
}
|
|
@@ -787,6 +1088,27 @@ export class VirtualRouterEngine {
|
|
|
787
1088
|
resolveSessionScope(metadata) {
|
|
788
1089
|
return resolveSessionScopeImpl(metadata);
|
|
789
1090
|
}
|
|
1091
|
+
resolveInstructionProcessModeForSelection(providerKey, routingState) {
|
|
1092
|
+
const candidates = [
|
|
1093
|
+
routingState.forcedTarget,
|
|
1094
|
+
routingState.stickyTarget,
|
|
1095
|
+
routingState.preferTarget
|
|
1096
|
+
];
|
|
1097
|
+
for (const candidate of candidates) {
|
|
1098
|
+
const processMode = candidate?.processMode;
|
|
1099
|
+
if (!processMode) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
const resolved = this.resolveInstructionTarget(candidate);
|
|
1103
|
+
if (!resolved) {
|
|
1104
|
+
continue;
|
|
1105
|
+
}
|
|
1106
|
+
if (resolved.keys.includes(providerKey)) {
|
|
1107
|
+
return processMode;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return undefined;
|
|
1111
|
+
}
|
|
790
1112
|
resolveInstructionTarget(target) {
|
|
791
1113
|
if (!target || !target.provider) {
|
|
792
1114
|
return null;
|
|
@@ -1034,6 +1356,8 @@ export class VirtualRouterEngine {
|
|
|
1034
1356
|
}
|
|
1035
1357
|
buildRouteCandidates(requestedRoute, classificationCandidates, features) {
|
|
1036
1358
|
const forceVision = this.routeHasForceFlag('vision');
|
|
1359
|
+
const hasMultimodalTargets = this.routeHasTargets(this.routing.multimodal);
|
|
1360
|
+
const hasVisionTargets = this.routeHasTargets(this.routing.vision);
|
|
1037
1361
|
const normalized = this.normalizeRouteAlias(requestedRoute || DEFAULT_ROUTE);
|
|
1038
1362
|
const baseList = [];
|
|
1039
1363
|
if (classificationCandidates && classificationCandidates.length) {
|
|
@@ -1044,35 +1368,31 @@ export class VirtualRouterEngine {
|
|
|
1044
1368
|
else if (normalized) {
|
|
1045
1369
|
baseList.push(normalized);
|
|
1046
1370
|
}
|
|
1047
|
-
// 当检测到当前请求包含图片时,确保 default/thinking 也参与候选集,
|
|
1048
|
-
// 以便优先尝试内建多模态模型(Responses/Gemini),再回落到 vision 路由池。
|
|
1049
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
1050
|
-
const visionAwareRoutes = [DEFAULT_ROUTE, 'thinking'];
|
|
1051
|
-
for (const routeName of visionAwareRoutes) {
|
|
1052
|
-
if (this.routeHasTargets(this.routing[routeName]) && !baseList.includes(routeName)) {
|
|
1053
|
-
baseList.push(routeName);
|
|
1054
|
-
}
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
1371
|
if (features.hasImageAttachment) {
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
continue;
|
|
1372
|
+
if (hasMultimodalTargets) {
|
|
1373
|
+
if (!baseList.includes('multimodal')) {
|
|
1374
|
+
baseList.unshift('multimodal');
|
|
1062
1375
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1376
|
+
}
|
|
1377
|
+
else if (hasVisionTargets) {
|
|
1378
|
+
if (!baseList.includes('vision')) {
|
|
1379
|
+
baseList.unshift('vision');
|
|
1065
1380
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1381
|
+
}
|
|
1382
|
+
if (!forceVision && hasMultimodalTargets) {
|
|
1383
|
+
const visionAwareRoutes = [DEFAULT_ROUTE, 'thinking'];
|
|
1384
|
+
for (const routeName of visionAwareRoutes) {
|
|
1385
|
+
if (this.routeHasTargets(this.routing[routeName]) && !baseList.includes(routeName)) {
|
|
1386
|
+
baseList.push(routeName);
|
|
1387
|
+
}
|
|
1068
1388
|
}
|
|
1069
1389
|
}
|
|
1070
1390
|
}
|
|
1071
1391
|
let ordered = this.sortByPriority(baseList);
|
|
1072
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
1392
|
+
if (features.hasImageAttachment && !forceVision && hasMultimodalTargets) {
|
|
1073
1393
|
ordered = this.reorderForInlineVision(ordered);
|
|
1074
1394
|
}
|
|
1075
|
-
if (features.hasImageAttachment) {
|
|
1395
|
+
if (features.hasImageAttachment && hasMultimodalTargets) {
|
|
1076
1396
|
ordered = this.reorderForPreferredModel(ordered, 'kimi-k2.5');
|
|
1077
1397
|
}
|
|
1078
1398
|
const deduped = [];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { detectExtendedThinkingKeyword, detectImageAttachment, detectKeyword, extractMessageText, getLatestMessageRole, getLatestUserMessage } from './message-utils.js';
|
|
2
2
|
import { extractAntigravityGeminiSessionId } from '../../conversion/compat/antigravity-session-signature.js';
|
|
3
|
-
import { detectCodingTool, detectLastAssistantToolCategory, detectVisionTool, detectWebTool, extractMeaningfulDeclaredToolNames } from './tool-signals.js';
|
|
3
|
+
import { detectCodingTool, detectLastAssistantToolCategory, detectVisionTool, detectWebSearchToolDeclared, detectWebTool, extractMeaningfulDeclaredToolNames } from './tool-signals.js';
|
|
4
4
|
import { computeRequestTokens } from './token-estimator.js';
|
|
5
5
|
const THINKING_KEYWORDS = ['let me think', 'chain of thought', 'cot', 'reason step', 'deliberate'];
|
|
6
6
|
export function buildRoutingFeatures(request, metadata) {
|
|
@@ -59,6 +59,7 @@ export function buildRoutingFeatures(request, metadata) {
|
|
|
59
59
|
hasVisionTool,
|
|
60
60
|
hasImageAttachment,
|
|
61
61
|
hasWebTool,
|
|
62
|
+
hasWebSearchToolDeclared: detectWebSearchToolDeclared(request),
|
|
62
63
|
hasCodingTool,
|
|
63
64
|
hasThinkingKeyword,
|
|
64
65
|
estimatedTokens,
|