@jsonstudio/llms 0.6.1749 → 0.6.1892
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 +325 -38
- 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 +11 -1
- package/dist/router/virtual-router/routing-instructions.js +101 -183
- 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 +1 -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 +15 -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 +352 -257
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +22 -1
- package/dist/servertool/handlers/stop-message-stage-policy.js +472 -60
- 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,7 +232,8 @@ 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,
|
|
@@ -174,29 +242,58 @@ export class VirtualRouterEngine {
|
|
|
174
242
|
stopMessageUpdatedAt: sessionState.stopMessageUpdatedAt,
|
|
175
243
|
stopMessageLastUsedAt: sessionState.stopMessageLastUsedAt,
|
|
176
244
|
stopMessageStage: sessionState.stopMessageStage,
|
|
245
|
+
stopMessageStageMode: sessionState.stopMessageStageMode,
|
|
177
246
|
stopMessageObservationHash: sessionState.stopMessageObservationHash,
|
|
178
247
|
stopMessageObservationStableCount: sessionState.stopMessageObservationStableCount,
|
|
179
248
|
stopMessageBdWorkState: sessionState.stopMessageBdWorkState
|
|
180
249
|
};
|
|
181
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
|
|
257
|
+
};
|
|
258
|
+
}
|
|
182
259
|
}
|
|
183
260
|
const parsedInstructions = parseRoutingInstructions(request.messages);
|
|
261
|
+
const latestUserHasMarker = hasLatestUserRoutingInstructionMarker(request.messages);
|
|
184
262
|
let instructions = parsedInstructions;
|
|
185
263
|
if (stopMessageScope && parsedInstructions.length > 0) {
|
|
186
264
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
187
|
-
const
|
|
188
|
-
|
|
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');
|
|
189
283
|
if (!hasStopMessageClear && stopMessageSets.length > 0) {
|
|
190
284
|
const sessionText = typeof sessionState.stopMessageText === 'string' ? sessionState.stopMessageText.trim() : '';
|
|
191
285
|
const sessionMax = typeof sessionState.stopMessageMaxRepeats === 'number' && Number.isFinite(sessionState.stopMessageMaxRepeats)
|
|
192
286
|
? Math.floor(sessionState.stopMessageMaxRepeats)
|
|
193
287
|
: undefined;
|
|
288
|
+
const sessionMode = normalizeStopMessageStageMode(sessionState.stopMessageStageMode);
|
|
194
289
|
const allSame = stopMessageSets.every((entry) => {
|
|
195
290
|
const entryText = typeof entry.stopMessageText === 'string' ? entry.stopMessageText.trim() : '';
|
|
196
291
|
const entryMax = typeof entry.stopMessageMaxRepeats === 'number' && Number.isFinite(entry.stopMessageMaxRepeats)
|
|
197
292
|
? Math.floor(entry.stopMessageMaxRepeats)
|
|
198
293
|
: undefined;
|
|
199
|
-
|
|
294
|
+
const incomingMode = normalizeStopMessageStageMode(entry.stopMessageStageMode);
|
|
295
|
+
const entryMode = incomingMode ?? (sessionMode === 'off' ? 'on' : sessionMode);
|
|
296
|
+
return Boolean(entryText) && entryText === sessionText && entryMax === sessionMax && entryMode === sessionMode;
|
|
200
297
|
});
|
|
201
298
|
const used = typeof sessionState.stopMessageUsed === 'number' && Number.isFinite(sessionState.stopMessageUsed)
|
|
202
299
|
? Math.max(0, Math.floor(sessionState.stopMessageUsed))
|
|
@@ -205,19 +302,36 @@ export class VirtualRouterEngine {
|
|
|
205
302
|
Number.isFinite(sessionState.stopMessageLastUsedAt);
|
|
206
303
|
const alreadyArmed = used === 0 && !hasLastUsedAt;
|
|
207
304
|
if (allSame && alreadyArmed) {
|
|
208
|
-
instructions =
|
|
305
|
+
instructions = instructions.filter((entry) => entry.type !== 'stopMessageSet');
|
|
209
306
|
}
|
|
210
307
|
}
|
|
211
308
|
}
|
|
212
309
|
// stopMessage must be session-scoped: require explicit sessionId in metadata.
|
|
213
310
|
// This prevents global/default persistence and ensures the trigger matches the setting sessionId.
|
|
214
311
|
if (parsedInstructions.length > 0) {
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
|
|
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 });
|
|
218
319
|
}
|
|
219
320
|
}
|
|
220
|
-
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)) {
|
|
221
335
|
request.messages = cleanMessagesFromRoutingInstructions(request.messages);
|
|
222
336
|
}
|
|
223
337
|
if (instructions.length > 0) {
|
|
@@ -230,8 +344,9 @@ export class VirtualRouterEngine {
|
|
|
230
344
|
// so servertool triggers always match the setting sessionId.
|
|
231
345
|
if (stopMessageScope) {
|
|
232
346
|
const hasStopMessageSet = instructions.some((entry) => entry.type === 'stopMessageSet');
|
|
347
|
+
const hasStopMessageMode = instructions.some((entry) => entry.type === 'stopMessageMode');
|
|
233
348
|
const hasStopMessageClear = instructions.some((entry) => entry.type === 'stopMessageClear');
|
|
234
|
-
if (hasStopMessageSet || hasStopMessageClear) {
|
|
349
|
+
if (hasStopMessageSet || hasStopMessageMode || hasStopMessageClear) {
|
|
235
350
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
236
351
|
let nextSessionState = {
|
|
237
352
|
...sessionState
|
|
@@ -246,6 +361,7 @@ export class VirtualRouterEngine {
|
|
|
246
361
|
nextSessionState.stopMessageLastUsedAt = clearedAt;
|
|
247
362
|
nextSessionState.stopMessageSource = undefined;
|
|
248
363
|
nextSessionState.stopMessageStage = undefined;
|
|
364
|
+
nextSessionState.stopMessageStageMode = undefined;
|
|
249
365
|
nextSessionState.stopMessageObservationHash = undefined;
|
|
250
366
|
nextSessionState.stopMessageObservationStableCount = undefined;
|
|
251
367
|
nextSessionState.stopMessageBdWorkState = undefined;
|
|
@@ -259,7 +375,10 @@ export class VirtualRouterEngine {
|
|
|
259
375
|
const sameMax = typeof sessionState.stopMessageMaxRepeats === 'number' &&
|
|
260
376
|
typeof maxRepeats === 'number' &&
|
|
261
377
|
Math.floor(sessionState.stopMessageMaxRepeats) === Math.floor(maxRepeats);
|
|
262
|
-
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;
|
|
263
382
|
const used = typeof sessionState.stopMessageUsed === 'number' && Number.isFinite(sessionState.stopMessageUsed)
|
|
264
383
|
? Math.max(0, Math.floor(sessionState.stopMessageUsed))
|
|
265
384
|
: 0;
|
|
@@ -269,6 +388,7 @@ export class VirtualRouterEngine {
|
|
|
269
388
|
nextSessionState.stopMessageText = text || undefined;
|
|
270
389
|
nextSessionState.stopMessageMaxRepeats = maxRepeats;
|
|
271
390
|
nextSessionState.stopMessageSource = 'explicit';
|
|
391
|
+
nextSessionState.stopMessageStageMode = nextMode;
|
|
272
392
|
if (shouldRearm) {
|
|
273
393
|
nextSessionState.stopMessageUsed = 0;
|
|
274
394
|
nextSessionState.stopMessageUpdatedAt =
|
|
@@ -283,6 +403,47 @@ export class VirtualRouterEngine {
|
|
|
283
403
|
shouldPersistSessionState = true;
|
|
284
404
|
}
|
|
285
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
|
+
}
|
|
286
447
|
if (shouldPersistSessionState) {
|
|
287
448
|
this.routingInstructionState.set(stopMessageScope, nextSessionState);
|
|
288
449
|
persistRoutingInstructionState(stopMessageScope, nextSessionState, this.routingStateStore);
|
|
@@ -292,13 +453,15 @@ export class VirtualRouterEngine {
|
|
|
292
453
|
}
|
|
293
454
|
// 日志展示使用 session scope 的 stopMessage 状态,避免每次解析重复刷新时间/次数。
|
|
294
455
|
if (typeof nextSessionState.stopMessageText === 'string' ||
|
|
295
|
-
typeof nextSessionState.stopMessageMaxRepeats === 'number'
|
|
456
|
+
typeof nextSessionState.stopMessageMaxRepeats === 'number' ||
|
|
457
|
+
typeof nextSessionState.stopMessageStageMode === 'string') {
|
|
296
458
|
routingState.stopMessageText = nextSessionState.stopMessageText;
|
|
297
459
|
routingState.stopMessageMaxRepeats = nextSessionState.stopMessageMaxRepeats;
|
|
298
460
|
routingState.stopMessageUsed = nextSessionState.stopMessageUsed;
|
|
299
461
|
routingState.stopMessageUpdatedAt = nextSessionState.stopMessageUpdatedAt;
|
|
300
462
|
routingState.stopMessageLastUsedAt = nextSessionState.stopMessageLastUsedAt;
|
|
301
463
|
routingState.stopMessageStage = nextSessionState.stopMessageStage;
|
|
464
|
+
routingState.stopMessageStageMode = nextSessionState.stopMessageStageMode;
|
|
302
465
|
routingState.stopMessageObservationHash = nextSessionState.stopMessageObservationHash;
|
|
303
466
|
routingState.stopMessageObservationStableCount = nextSessionState.stopMessageObservationStableCount;
|
|
304
467
|
routingState.stopMessageBdWorkState = nextSessionState.stopMessageBdWorkState;
|
|
@@ -306,20 +469,68 @@ export class VirtualRouterEngine {
|
|
|
306
469
|
}
|
|
307
470
|
}
|
|
308
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
|
+
}
|
|
309
513
|
if (instructions.length === 0 && stopMessageScope) {
|
|
310
514
|
const sessionState = getRoutingInstructionState(stopMessageScope, this.routingInstructionState, this.routingStateStore);
|
|
311
515
|
if (typeof sessionState.stopMessageText === 'string' ||
|
|
312
|
-
typeof sessionState.stopMessageMaxRepeats === 'number'
|
|
516
|
+
typeof sessionState.stopMessageMaxRepeats === 'number' ||
|
|
517
|
+
typeof sessionState.stopMessageStageMode === 'string') {
|
|
313
518
|
routingState.stopMessageText = sessionState.stopMessageText;
|
|
314
519
|
routingState.stopMessageMaxRepeats = sessionState.stopMessageMaxRepeats;
|
|
315
520
|
routingState.stopMessageUsed = sessionState.stopMessageUsed;
|
|
316
521
|
routingState.stopMessageUpdatedAt = sessionState.stopMessageUpdatedAt;
|
|
317
522
|
routingState.stopMessageLastUsedAt = sessionState.stopMessageLastUsedAt;
|
|
318
523
|
routingState.stopMessageStage = sessionState.stopMessageStage;
|
|
524
|
+
routingState.stopMessageStageMode = sessionState.stopMessageStageMode;
|
|
319
525
|
routingState.stopMessageObservationHash = sessionState.stopMessageObservationHash;
|
|
320
526
|
routingState.stopMessageObservationStableCount = sessionState.stopMessageObservationStableCount;
|
|
321
527
|
routingState.stopMessageBdWorkState = sessionState.stopMessageBdWorkState;
|
|
322
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;
|
|
533
|
+
}
|
|
323
534
|
}
|
|
324
535
|
// Guardrail: if a session is restricted to providers that do not exist in any routing pools,
|
|
325
536
|
// we must not hard-fail the request loop. Auto-clear the allowlist and fall back to normal routing.
|
|
@@ -537,12 +748,16 @@ export class VirtualRouterEngine {
|
|
|
537
748
|
}
|
|
538
749
|
}
|
|
539
750
|
const baseTarget = this.providerRegistry.buildTarget(selection.providerKey);
|
|
540
|
-
const forceVision = this.routeHasForceFlag('vision');
|
|
751
|
+
const forceVision = selection.routeUsed === 'vision' && this.routeHasForceFlag('vision');
|
|
541
752
|
const target = {
|
|
542
753
|
...baseTarget,
|
|
543
754
|
...(this.webSearchForce ? { forceWebSearch: true } : {}),
|
|
544
755
|
...(forceVision ? { forceVision: true } : {})
|
|
545
756
|
};
|
|
757
|
+
const instructionProcessMode = this.resolveInstructionProcessModeForSelection(selection.providerKey, routingState);
|
|
758
|
+
if (instructionProcessMode) {
|
|
759
|
+
target.processMode = instructionProcessMode;
|
|
760
|
+
}
|
|
546
761
|
recordAntigravitySessionLease({
|
|
547
762
|
metadata: features.metadata,
|
|
548
763
|
providerKey: selection.providerKey,
|
|
@@ -605,6 +820,20 @@ export class VirtualRouterEngine {
|
|
|
605
820
|
};
|
|
606
821
|
}
|
|
607
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
|
+
};
|
|
608
837
|
const sessionScope = this.resolveSessionScope(metadata);
|
|
609
838
|
const sessionState = sessionScope
|
|
610
839
|
? getRoutingInstructionState(sessionScope, this.routingInstructionState, this.routingStateStore)
|
|
@@ -613,9 +842,11 @@ export class VirtualRouterEngine {
|
|
|
613
842
|
const stickyState = stickyKey
|
|
614
843
|
? getRoutingInstructionState(stickyKey, this.routingInstructionState, this.routingStateStore)
|
|
615
844
|
: null;
|
|
616
|
-
const effectiveState = sessionState
|
|
845
|
+
const effectiveState = hasArmedStopState(sessionState)
|
|
617
846
|
? sessionState
|
|
618
|
-
: stickyState
|
|
847
|
+
: hasArmedStopState(stickyState)
|
|
848
|
+
? stickyState
|
|
849
|
+
: null;
|
|
619
850
|
if (!effectiveState) {
|
|
620
851
|
return null;
|
|
621
852
|
}
|
|
@@ -624,11 +855,18 @@ export class VirtualRouterEngine {
|
|
|
624
855
|
Number.isFinite(effectiveState.stopMessageMaxRepeats)
|
|
625
856
|
? Math.max(1, Math.floor(effectiveState.stopMessageMaxRepeats))
|
|
626
857
|
: 0;
|
|
627
|
-
|
|
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) {
|
|
628
866
|
return null;
|
|
629
867
|
}
|
|
630
868
|
return {
|
|
631
|
-
stopMessageText: text,
|
|
869
|
+
...(text ? { stopMessageText: text } : {}),
|
|
632
870
|
stopMessageMaxRepeats: maxRepeats,
|
|
633
871
|
...(typeof effectiveState.stopMessageSource === 'string' && effectiveState.stopMessageSource.trim()
|
|
634
872
|
? { stopMessageSource: effectiveState.stopMessageSource.trim() }
|
|
@@ -647,6 +885,7 @@ export class VirtualRouterEngine {
|
|
|
647
885
|
...(typeof effectiveState.stopMessageStage === 'string' && effectiveState.stopMessageStage.trim()
|
|
648
886
|
? { stopMessageStage: effectiveState.stopMessageStage.trim() }
|
|
649
887
|
: {}),
|
|
888
|
+
...(stageModeNormalized ? { stopMessageStageMode: stageModeNormalized } : {}),
|
|
650
889
|
...(typeof effectiveState.stopMessageObservationHash === 'string' && effectiveState.stopMessageObservationHash.trim()
|
|
651
890
|
? { stopMessageObservationHash: effectiveState.stopMessageObservationHash.trim() }
|
|
652
891
|
: {}),
|
|
@@ -659,6 +898,35 @@ export class VirtualRouterEngine {
|
|
|
659
898
|
: {})
|
|
660
899
|
};
|
|
661
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 }
|
|
927
|
+
: {})
|
|
928
|
+
};
|
|
929
|
+
}
|
|
662
930
|
handleProviderFailure(event) {
|
|
663
931
|
handleProviderFailureImpl(event, this.healthManager, this.providerHealthConfig(), (key, ttl) => this.markProviderCooldown(key, ttl));
|
|
664
932
|
}
|
|
@@ -820,6 +1088,27 @@ export class VirtualRouterEngine {
|
|
|
820
1088
|
resolveSessionScope(metadata) {
|
|
821
1089
|
return resolveSessionScopeImpl(metadata);
|
|
822
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
|
+
}
|
|
823
1112
|
resolveInstructionTarget(target) {
|
|
824
1113
|
if (!target || !target.provider) {
|
|
825
1114
|
return null;
|
|
@@ -1067,6 +1356,8 @@ export class VirtualRouterEngine {
|
|
|
1067
1356
|
}
|
|
1068
1357
|
buildRouteCandidates(requestedRoute, classificationCandidates, features) {
|
|
1069
1358
|
const forceVision = this.routeHasForceFlag('vision');
|
|
1359
|
+
const hasMultimodalTargets = this.routeHasTargets(this.routing.multimodal);
|
|
1360
|
+
const hasVisionTargets = this.routeHasTargets(this.routing.vision);
|
|
1070
1361
|
const normalized = this.normalizeRouteAlias(requestedRoute || DEFAULT_ROUTE);
|
|
1071
1362
|
const baseList = [];
|
|
1072
1363
|
if (classificationCandidates && classificationCandidates.length) {
|
|
@@ -1077,35 +1368,31 @@ export class VirtualRouterEngine {
|
|
|
1077
1368
|
else if (normalized) {
|
|
1078
1369
|
baseList.push(normalized);
|
|
1079
1370
|
}
|
|
1080
|
-
// 当检测到当前请求包含图片时,确保 default/thinking 也参与候选集,
|
|
1081
|
-
// 以便优先尝试内建多模态模型(Responses/Gemini),再回落到 vision 路由池。
|
|
1082
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
1083
|
-
const visionAwareRoutes = [DEFAULT_ROUTE, 'thinking'];
|
|
1084
|
-
for (const routeName of visionAwareRoutes) {
|
|
1085
|
-
if (this.routeHasTargets(this.routing[routeName]) && !baseList.includes(routeName)) {
|
|
1086
|
-
baseList.push(routeName);
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
1371
|
if (features.hasImageAttachment) {
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
continue;
|
|
1372
|
+
if (hasMultimodalTargets) {
|
|
1373
|
+
if (!baseList.includes('multimodal')) {
|
|
1374
|
+
baseList.unshift('multimodal');
|
|
1095
1375
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1376
|
+
}
|
|
1377
|
+
else if (hasVisionTargets) {
|
|
1378
|
+
if (!baseList.includes('vision')) {
|
|
1379
|
+
baseList.unshift('vision');
|
|
1098
1380
|
}
|
|
1099
|
-
|
|
1100
|
-
|
|
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
|
+
}
|
|
1101
1388
|
}
|
|
1102
1389
|
}
|
|
1103
1390
|
}
|
|
1104
1391
|
let ordered = this.sortByPriority(baseList);
|
|
1105
|
-
if (features.hasImageAttachment && !forceVision) {
|
|
1392
|
+
if (features.hasImageAttachment && !forceVision && hasMultimodalTargets) {
|
|
1106
1393
|
ordered = this.reorderForInlineVision(ordered);
|
|
1107
1394
|
}
|
|
1108
|
-
if (features.hasImageAttachment) {
|
|
1395
|
+
if (features.hasImageAttachment && hasMultimodalTargets) {
|
|
1109
1396
|
ordered = this.reorderForPreferredModel(ordered, 'kimi-k2.5');
|
|
1110
1397
|
}
|
|
1111
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,
|