@jsonstudio/llms 0.6.1643 → 0.6.1739

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.
Files changed (96) hide show
  1. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +10 -0
  2. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +121 -0
  3. package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.d.ts +10 -0
  4. package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +80 -0
  5. package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.d.ts +7 -0
  6. package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +161 -0
  7. package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.d.ts +12 -0
  8. package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +67 -0
  9. package/dist/conversion/compat/actions/iflow-response-body-unwrap.d.ts +9 -0
  10. package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +140 -0
  11. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +10 -0
  12. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +59 -0
  13. package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.d.ts +14 -0
  14. package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.js +125 -0
  15. package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +11 -0
  16. package/dist/conversion/compat/actions/normalize-tool-call-ids.js +140 -0
  17. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.d.ts +2 -0
  18. package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +152 -0
  19. package/dist/conversion/compat/antigravity-session-signature.d.ts +1 -1
  20. package/dist/conversion/compat/antigravity-session-signature.js +5 -4
  21. package/dist/conversion/compat/profiles/chat-iflow.json +6 -0
  22. package/dist/conversion/compat/profiles/chat-lmstudio.json +7 -1
  23. package/dist/conversion/hub/operation-table/operation-table-runner.js +1 -1
  24. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +19 -2
  25. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +101 -5
  26. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.d.ts +2 -0
  27. package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +63 -0
  28. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +18 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline.js +1 -1
  30. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +8 -5
  31. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +5 -1
  32. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +113 -0
  33. package/dist/conversion/hub/pipeline/target-utils.js +3 -0
  34. package/dist/conversion/hub/response/provider-response.js +27 -1
  35. package/dist/conversion/responses/responses-openai-bridge.js +32 -6
  36. package/dist/conversion/shared/anthropic-message-utils.js +20 -5
  37. package/dist/conversion/shared/bridge-id-utils.d.ts +2 -0
  38. package/dist/conversion/shared/bridge-id-utils.js +52 -15
  39. package/dist/conversion/shared/responses-conversation-store.js +40 -5
  40. package/dist/conversion/shared/responses-output-builder.js +23 -7
  41. package/dist/conversion/shared/responses-tool-utils.d.ts +1 -0
  42. package/dist/conversion/shared/responses-tool-utils.js +30 -13
  43. package/dist/conversion/shared/text-markup-normalizer.d.ts +1 -0
  44. package/dist/conversion/shared/text-markup-normalizer.js +269 -1
  45. package/dist/router/virtual-router/bootstrap.js +31 -7
  46. package/dist/router/virtual-router/classifier.js +1 -1
  47. package/dist/router/virtual-router/engine/antigravity/alias-lease.d.ts +33 -0
  48. package/dist/router/virtual-router/engine/antigravity/alias-lease.js +247 -0
  49. package/dist/router/virtual-router/engine/health/index.d.ts +23 -0
  50. package/dist/router/virtual-router/engine/health/index.js +720 -0
  51. package/dist/router/virtual-router/engine/provider-key/parse.d.ts +6 -0
  52. package/dist/router/virtual-router/engine/provider-key/parse.js +43 -0
  53. package/dist/router/virtual-router/engine/routing-pools/index.d.ts +13 -0
  54. package/dist/router/virtual-router/engine/routing-pools/index.js +225 -0
  55. package/dist/router/virtual-router/engine/routing-state/keys.d.ts +3 -0
  56. package/dist/router/virtual-router/engine/routing-state/keys.js +30 -0
  57. package/dist/router/virtual-router/engine/routing-state/metadata.d.ts +6 -0
  58. package/dist/router/virtual-router/engine/routing-state/metadata.js +132 -0
  59. package/dist/router/virtual-router/engine/routing-state/store.d.ts +11 -0
  60. package/dist/router/virtual-router/engine/routing-state/store.js +107 -0
  61. package/dist/router/virtual-router/engine-health.d.ts +1 -23
  62. package/dist/router/virtual-router/engine-health.js +1 -720
  63. package/dist/router/virtual-router/engine-selection/route-utils.js +57 -0
  64. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +8 -48
  65. package/dist/router/virtual-router/engine-selection/tier-selection.js +34 -17
  66. package/dist/router/virtual-router/engine-selection.d.ts +1 -13
  67. package/dist/router/virtual-router/engine-selection.js +1 -225
  68. package/dist/router/virtual-router/engine.d.ts +2 -23
  69. package/dist/router/virtual-router/engine.js +130 -603
  70. package/dist/router/virtual-router/message-utils.js +15 -5
  71. package/dist/servertool/engine.js +4 -4
  72. package/dist/servertool/handlers/followup-request-builder.js +46 -0
  73. package/dist/servertool/handlers/gemini-empty-reply-continue.js +48 -47
  74. package/dist/servertool/handlers/stop-message-auto.js +64 -7
  75. package/dist/servertool/handlers/vision.js +10 -0
  76. package/dist/servertool/types.d.ts +3 -0
  77. package/dist/sse/sse-to-json/builders/response-builder.js +6 -0
  78. package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +32 -2
  79. package/dist/sse/sse-to-json/parsers/sse-parser.js +34 -0
  80. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -0
  81. package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +33 -1
  82. package/dist/tools/apply-patch/args-normalizer/default-actions.d.ts +2 -0
  83. package/dist/tools/apply-patch/args-normalizer/default-actions.js +12 -0
  84. package/dist/tools/apply-patch/args-normalizer/extract-patch.d.ts +2 -0
  85. package/dist/tools/apply-patch/args-normalizer/extract-patch.js +15 -0
  86. package/dist/tools/apply-patch/args-normalizer/index.d.ts +2 -0
  87. package/dist/tools/apply-patch/args-normalizer/index.js +164 -0
  88. package/dist/tools/apply-patch/args-normalizer/structured-builders.d.ts +7 -0
  89. package/dist/tools/apply-patch/args-normalizer/structured-builders.js +85 -0
  90. package/dist/tools/apply-patch/args-normalizer/types.d.ts +54 -0
  91. package/dist/tools/apply-patch/args-normalizer/types.js +1 -0
  92. package/dist/tools/apply-patch/patch-text/looks-like-patch.js +1 -0
  93. package/dist/tools/apply-patch/patch-text/normalize.js +104 -5
  94. package/dist/tools/apply-patch/structured/coercion.js +28 -4
  95. package/dist/tools/apply-patch/validator.js +7 -146
  96. package/package.json +1 -1
@@ -61,10 +61,27 @@ export function buildRouteCandidates(requestedRoute, classificationCandidates, f
61
61
  }
62
62
  }
63
63
  }
64
+ if (features.hasImageAttachment) {
65
+ const allRouteNames = Object.keys(routing);
66
+ for (const routeName of allRouteNames) {
67
+ if (!routeHasTargets(routing[routeName])) {
68
+ continue;
69
+ }
70
+ if (!routeSupportsModel(routeName, 'kimi-k2.5', routing, providerRegistry)) {
71
+ continue;
72
+ }
73
+ if (!baseList.includes(routeName)) {
74
+ baseList.push(routeName);
75
+ }
76
+ }
77
+ }
64
78
  let ordered = sortByPriority(baseList);
65
79
  if (features.hasImageAttachment && !forceVision) {
66
80
  ordered = reorderForInlineVision(ordered, routing, providerRegistry);
67
81
  }
82
+ if (features.hasImageAttachment) {
83
+ ordered = reorderForPreferredModel(ordered, 'kimi-k2.5', routing, providerRegistry);
84
+ }
68
85
  const deduped = [];
69
86
  for (const routeName of ordered) {
70
87
  if (routeName && !deduped.includes(routeName)) {
@@ -142,3 +159,43 @@ function routeSupportsInlineVision(routeName, routing, providerRegistry) {
142
159
  }
143
160
  return false;
144
161
  }
162
+ function reorderForPreferredModel(routeNames, modelId, routing, providerRegistry) {
163
+ const unique = Array.from(new Set(routeNames.filter(Boolean)));
164
+ if (!unique.length) {
165
+ return unique;
166
+ }
167
+ const preferred = unique.filter((routeName) => routeSupportsModel(routeName, modelId, routing, providerRegistry));
168
+ if (!preferred.length) {
169
+ return unique;
170
+ }
171
+ const remaining = unique.filter((routeName) => !preferred.includes(routeName));
172
+ return [...preferred, ...remaining];
173
+ }
174
+ function routeSupportsModel(routeName, modelId, routing, providerRegistry) {
175
+ const normalizedModel = modelId.trim().toLowerCase();
176
+ if (!normalizedModel) {
177
+ return false;
178
+ }
179
+ const pools = routing[routeName];
180
+ if (!Array.isArray(pools)) {
181
+ return false;
182
+ }
183
+ for (const pool of pools) {
184
+ if (!Array.isArray(pool.targets)) {
185
+ continue;
186
+ }
187
+ for (const providerKey of pool.targets) {
188
+ try {
189
+ const profile = providerRegistry.get(providerKey);
190
+ const candidate = typeof profile.modelId === 'string' ? profile.modelId.trim().toLowerCase() : '';
191
+ if (candidate === normalizedModel) {
192
+ return true;
193
+ }
194
+ }
195
+ catch {
196
+ // ignore unknown providers when probing capabilities
197
+ }
198
+ }
199
+ }
200
+ return false;
201
+ }
@@ -282,7 +282,8 @@ export function selectProviderKeyFromCandidatePool(opts) {
282
282
  if (!bucket.length) {
283
283
  continue;
284
284
  }
285
- bucket.sort((a, b) => (a.penalty - b.penalty) || (a.order - b.order));
285
+ // Keep candidate ordering stable (config order). Availability/blacklist/cooldown still apply.
286
+ bucket.sort((a, b) => a.order - b.order);
286
287
  let bucketCandidates = bucket.map((item) => item.key);
287
288
  // Single-provider pool should never be "emptied" by health/cooldown.
288
289
  if (bucketCandidates.length === 1) {
@@ -299,10 +300,6 @@ export function selectProviderKeyFromCandidatePool(opts) {
299
300
  deps,
300
301
  excludedKeys
301
302
  });
302
- const bucketPenaltyMap = {};
303
- for (const item of bucket) {
304
- bucketPenaltyMap[item.key] = item.penalty;
305
- }
306
303
  const bucketWeights = {};
307
304
  const bucketMultipliers = {};
308
305
  for (const item of bucket) {
@@ -337,8 +334,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
337
334
  candidates: bucketCandidates,
338
335
  orderedTargets: tier.targets,
339
336
  providerRegistry: deps.providerRegistry,
340
- availabilityCheck: isAvailable,
341
- penalties: bucketPenaltyMap
337
+ availabilityCheck: isAvailable
342
338
  });
343
339
  if (!group) {
344
340
  continue;
@@ -359,50 +355,14 @@ export function selectProviderKeyFromCandidatePool(opts) {
359
355
  }
360
356
  continue;
361
357
  }
362
- if (isRecoveryAttempt && healthWeightedCfg.enabled && healthWeightedCfg.recoverToBestOnRetry) {
363
- let best = null;
364
- let bestM = Number.NEGATIVE_INFINITY;
365
- for (const key of bucketCandidates) {
366
- if (!isAvailable(key))
367
- continue;
368
- const m = bucketMultipliers[key] ?? 1;
369
- if (m > bestM) {
370
- bestM = m;
371
- best = key;
372
- }
373
- }
374
- if (best) {
375
- return best;
376
- }
377
- continue;
378
- }
379
- else if (isRecoveryAttempt) {
380
- const recovered = selectFirstAvailable(bucketCandidates);
381
- if (recovered)
382
- return recovered;
383
- continue;
384
- }
358
+ const recovered = selectFirstAvailable(bucketCandidates);
359
+ if (recovered)
360
+ return recovered;
361
+ continue;
385
362
  // (unreachable) recovery handled above
386
363
  }
387
364
  else {
388
- if (isRecoveryAttempt && healthWeightedCfg.enabled && healthWeightedCfg.recoverToBestOnRetry) {
389
- let best = null;
390
- let bestM = Number.NEGATIVE_INFINITY;
391
- for (const key of bucketCandidates) {
392
- if (!isAvailable(key))
393
- continue;
394
- const m = bucketMultipliers[key] ?? 1;
395
- if (m > bestM) {
396
- bestM = m;
397
- best = key;
398
- }
399
- }
400
- if (best) {
401
- return best;
402
- }
403
- continue;
404
- }
405
- else if (isRecoveryAttempt) {
365
+ if (isRecoveryAttempt) {
406
366
  const recovered = selectFirstAvailable(bucketCandidates);
407
367
  if (recovered)
408
368
  return recovered;
@@ -108,6 +108,14 @@ function resolveSessionScopeKey(metadata) {
108
108
  if (conversationId) {
109
109
  return `conversation:${conversationId}`;
110
110
  }
111
+ // Antigravity-Manager alignment: when the client does not provide session_id/conversation_id,
112
+ // fall back to the derived antigravitySessionId so alias/session binding still works.
113
+ const antigravitySessionId = typeof record.antigravitySessionId === 'string'
114
+ ? String(record.antigravitySessionId).trim()
115
+ : '';
116
+ if (antigravitySessionId) {
117
+ return `session:${antigravitySessionId}`;
118
+ }
111
119
  return null;
112
120
  }
113
121
  function buildScopedSessionKey(sessionKey) {
@@ -376,29 +384,38 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
376
384
  }
377
385
  targets = filtered;
378
386
  }
379
- if (features.hasImageAttachment && (routeName === DEFAULT_ROUTE || routeName === 'thinking')) {
380
- const prioritized = [];
381
- const fallthrough = [];
382
- for (const key of targets) {
383
- try {
384
- const profile = deps.providerRegistry.get(key);
385
- if (profile.providerType === 'responses') {
386
- prioritized.push(key);
387
- }
388
- else if (profile.providerType === 'gemini') {
389
- prioritized.push(key);
387
+ if (features.hasImageAttachment) {
388
+ const kimiTargets = targets.filter((key) => {
389
+ const modelId = getProviderModelId(key, deps.providerRegistry) ?? '';
390
+ return modelId.trim().toLowerCase() === 'kimi-k2.5';
391
+ });
392
+ if (kimiTargets.length) {
393
+ targets = kimiTargets;
394
+ }
395
+ else if (routeName === DEFAULT_ROUTE || routeName === 'thinking') {
396
+ const prioritized = [];
397
+ const fallthrough = [];
398
+ for (const key of targets) {
399
+ try {
400
+ const profile = deps.providerRegistry.get(key);
401
+ if (profile.providerType === 'responses') {
402
+ prioritized.push(key);
403
+ }
404
+ else if (profile.providerType === 'gemini') {
405
+ prioritized.push(key);
406
+ }
407
+ else {
408
+ fallthrough.push(key);
409
+ }
390
410
  }
391
- else {
411
+ catch {
392
412
  fallthrough.push(key);
393
413
  }
394
414
  }
395
- catch {
396
- fallthrough.push(key);
415
+ if (prioritized.length) {
416
+ targets = prioritized;
397
417
  }
398
418
  }
399
- if (prioritized.length) {
400
- targets = prioritized;
401
- }
402
419
  }
403
420
  if (!targets.length) {
404
421
  const leaseHint = leaseResult.blocked > 0
@@ -1,13 +1 @@
1
- import type { ClassificationResult, RouterMetadataInput, RoutingFeatures } from './types.js';
2
- import type { RoutingInstructionState } from './routing-instructions.js';
3
- import type { SelectionDeps } from './engine-selection/selection-deps.js';
4
- export { selectDirectProviderModel } from './engine-selection/direct-provider-model.js';
5
- export { selectFromStickyPool } from './engine-selection/sticky-pool.js';
6
- export declare function selectProviderImpl(requestedRoute: string, metadata: RouterMetadataInput, classification: ClassificationResult, features: RoutingFeatures, activeState: RoutingInstructionState, deps: SelectionDeps, options?: {
7
- routingState?: RoutingInstructionState;
8
- }): {
9
- providerKey: string;
10
- routeUsed: string;
11
- pool: string[];
12
- poolId?: string;
13
- };
1
+ export { selectProviderImpl, selectDirectProviderModel, selectFromStickyPool } from './engine/routing-pools/index.js';
@@ -1,225 +1 @@
1
- import { DEFAULT_ROUTE, VirtualRouterError, VirtualRouterErrorCode } from './types.js';
2
- import { extractExcludedProviderKeySet, extractProviderId } from './engine-selection/key-parsing.js';
3
- import { trySelectFromTier } from './engine-selection/tier-selection.js';
4
- import { resolveInstructionTarget } from './engine-selection/instruction-target.js';
5
- import { filterCandidatesByRoutingState } from './engine-selection/routing-state-filter.js';
6
- import { selectFromStickyPool as selectFromStickyPoolImpl } from './engine-selection/sticky-pool.js';
7
- export { selectDirectProviderModel } from './engine-selection/direct-provider-model.js';
8
- export { selectFromStickyPool } from './engine-selection/sticky-pool.js';
9
- import { buildRouteCandidates, extendRouteCandidatesForState, initializeRouteQueue, normalizeRouteAlias, routeHasTargets, sortRoutePools } from './engine-selection/route-utils.js';
10
- export function selectProviderImpl(requestedRoute, metadata, classification, features, activeState, deps, options = {}) {
11
- const state = options.routingState ?? activeState;
12
- const quotaView = deps.quotaView;
13
- const quotaNow = quotaView ? Date.now() : 0;
14
- const isAllowedByQuota = (key) => {
15
- if (!quotaView) {
16
- return true;
17
- }
18
- const entry = quotaView(key);
19
- if (!entry) {
20
- return true;
21
- }
22
- if (!entry.inPool) {
23
- return false;
24
- }
25
- if (entry.cooldownUntil && entry.cooldownUntil > quotaNow) {
26
- return false;
27
- }
28
- if (entry.blacklistUntil && entry.blacklistUntil > quotaNow) {
29
- return false;
30
- }
31
- return true;
32
- };
33
- const excludedProviderKeys = extractExcludedProviderKeySet(features.metadata);
34
- const forcedResolution = state.forcedTarget ? resolveInstructionTarget(state.forcedTarget, deps.providerRegistry) : null;
35
- if (forcedResolution && forcedResolution.mode === 'exact') {
36
- const forcedKey = forcedResolution.keys[0];
37
- if (!excludedProviderKeys.has(forcedKey) && !deps.isProviderCoolingDown(forcedKey) && isAllowedByQuota(forcedKey)) {
38
- return {
39
- providerKey: forcedKey,
40
- routeUsed: requestedRoute,
41
- pool: [forcedKey],
42
- poolId: 'forced'
43
- };
44
- }
45
- }
46
- let stickyResolution = null;
47
- let stickyKeySet;
48
- if (!forcedResolution && state.stickyTarget) {
49
- stickyResolution = resolveInstructionTarget(state.stickyTarget, deps.providerRegistry);
50
- if (stickyResolution && stickyResolution.mode === 'exact') {
51
- const stickyKey = stickyResolution.keys[0];
52
- if ((deps.quotaView ? true : deps.healthManager.isAvailable(stickyKey)) &&
53
- !excludedProviderKeys.has(stickyKey) &&
54
- !deps.isProviderCoolingDown(stickyKey) &&
55
- isAllowedByQuota(stickyKey)) {
56
- return {
57
- providerKey: stickyKey,
58
- routeUsed: requestedRoute,
59
- pool: [stickyKey],
60
- poolId: 'sticky'
61
- };
62
- }
63
- }
64
- if (stickyResolution && stickyResolution.mode === 'filter' && stickyResolution.keys.length > 0) {
65
- const liveKeys = stickyResolution.keys.filter((key) => (deps.quotaView ? true : deps.healthManager.isAvailable(key)) &&
66
- !excludedProviderKeys.has(key) &&
67
- !deps.isProviderCoolingDown(key) &&
68
- isAllowedByQuota(key));
69
- if (liveKeys.length > 0) {
70
- stickyKeySet = new Set(liveKeys);
71
- }
72
- }
73
- }
74
- const allowAliasRotation = Boolean(state.stickyTarget) &&
75
- !state.stickyTarget?.keyAlias &&
76
- state.stickyTarget?.keyIndex === undefined;
77
- if (forcedResolution && forcedResolution.mode === 'filter') {
78
- const forcedKeySet = new Set(forcedResolution.keys);
79
- if (forcedKeySet.size > 0) {
80
- for (const key of Array.from(forcedKeySet)) {
81
- if (excludedProviderKeys.has(key) || deps.isProviderCoolingDown(key)) {
82
- forcedKeySet.delete(key);
83
- }
84
- }
85
- }
86
- if (forcedKeySet.size > 0) {
87
- const candidates = extendRouteCandidatesForState(buildRouteCandidates(requestedRoute, classification.candidates, features, deps.routing, deps.providerRegistry), state, deps.routing);
88
- const filteredCandidates = filterCandidatesByRoutingState(candidates, state, deps.routing, deps.providerRegistry);
89
- if (filteredCandidates.length === 0) {
90
- const allowedProviders = Array.from(state.allowedProviders);
91
- const disabledProviders = Array.from(state.disabledProviders);
92
- const providersInRouting = new Set();
93
- for (const pools of Object.values(deps.routing)) {
94
- if (!Array.isArray(pools))
95
- continue;
96
- for (const pool of pools) {
97
- if (!pool || !Array.isArray(pool.targets))
98
- continue;
99
- for (const key of pool.targets) {
100
- if (typeof key !== 'string' || !key)
101
- continue;
102
- const providerId = extractProviderId(key);
103
- if (providerId) {
104
- providersInRouting.add(providerId);
105
- }
106
- }
107
- }
108
- }
109
- const missingAllowedProviders = allowedProviders.length > 0 ? allowedProviders.filter((provider) => !providersInRouting.has(provider)) : [];
110
- const hint = (() => {
111
- if (missingAllowedProviders.length > 0) {
112
- return `Allowed providers not present in routing pools: ${missingAllowedProviders.join(', ')}`;
113
- }
114
- return 'Routing instructions excluded all route candidates';
115
- })();
116
- throw new VirtualRouterError(`No available providers after applying routing instructions (${hint}). ` +
117
- `Tip: remove/adjust <**...**> routing instructions (or use <**clear**>), or add providers/models to routing.`, VirtualRouterErrorCode.PROVIDER_NOT_AVAILABLE, {
118
- requestedRoute,
119
- allowedProviders,
120
- disabledProviders,
121
- missingAllowedProviders
122
- });
123
- }
124
- return selectFromCandidates(filteredCandidates, metadata, classification, features, state, deps, {
125
- requiredProviderKeys: forcedKeySet,
126
- allowAliasRotation
127
- });
128
- }
129
- }
130
- if (stickyKeySet && stickyKeySet.size > 0) {
131
- const stickySelection = selectFromStickyPoolImpl(stickyKeySet, metadata, features, state, deps, { allowAliasRotation });
132
- if (stickySelection) {
133
- return stickySelection;
134
- }
135
- }
136
- const candidates = buildRouteCandidates(requestedRoute, classification.candidates, features, deps.routing, deps.providerRegistry);
137
- const expandedCandidates = extendRouteCandidatesForState(candidates, state, deps.routing);
138
- const filteredCandidates = filterCandidatesByRoutingState(expandedCandidates, state, deps.routing, deps.providerRegistry);
139
- if (filteredCandidates.length === 0) {
140
- const allowedProviders = Array.from(state.allowedProviders);
141
- const disabledProviders = Array.from(state.disabledProviders);
142
- const providersInRouting = new Set();
143
- for (const pools of Object.values(deps.routing)) {
144
- if (!Array.isArray(pools))
145
- continue;
146
- for (const pool of pools) {
147
- if (!pool || !Array.isArray(pool.targets))
148
- continue;
149
- for (const key of pool.targets) {
150
- if (typeof key !== 'string' || !key)
151
- continue;
152
- const providerId = extractProviderId(key);
153
- if (providerId) {
154
- providersInRouting.add(providerId);
155
- }
156
- }
157
- }
158
- }
159
- const missingAllowedProviders = allowedProviders.length > 0 ? allowedProviders.filter((provider) => !providersInRouting.has(provider)) : [];
160
- const hint = (() => {
161
- if (missingAllowedProviders.length > 0) {
162
- return `Allowed providers not present in routing pools: ${missingAllowedProviders.join(', ')}`;
163
- }
164
- return 'Routing instructions excluded all route candidates';
165
- })();
166
- throw new VirtualRouterError(`No available providers after applying routing instructions (${hint}). ` +
167
- `Tip: remove/adjust <**...**> routing instructions (or use <**clear**>), or add providers/models to routing.`, VirtualRouterErrorCode.PROVIDER_NOT_AVAILABLE, {
168
- requestedRoute,
169
- allowedProviders,
170
- disabledProviders,
171
- missingAllowedProviders
172
- });
173
- }
174
- return selectFromCandidates(filteredCandidates, metadata, classification, features, state, deps, {
175
- allowAliasRotation
176
- });
177
- }
178
- function selectFromCandidates(routes, metadata, classification, features, state, deps, options) {
179
- const allowedProviders = new Set(state.allowedProviders);
180
- const disabledProviders = new Set(state.disabledProviders);
181
- const disabledKeysMap = new Map(Array.from(state.disabledKeys.entries()).map(([provider, keys]) => [
182
- provider,
183
- new Set(Array.from(keys).map((k) => (typeof k === 'string' ? k : k + 1)))
184
- ]));
185
- const disabledModels = new Map(Array.from(state.disabledModels.entries()).map(([provider, models]) => [provider, new Set(models)]));
186
- const stickyKey = options.allowAliasRotation ? undefined : deps.resolveStickyKey(metadata);
187
- const attempted = [];
188
- const visitedRoutes = new Set();
189
- const routeQueue = initializeRouteQueue(routes);
190
- const estimatedTokens = typeof features.estimatedTokens === 'number' && Number.isFinite(features.estimatedTokens)
191
- ? Math.max(0, features.estimatedTokens)
192
- : 0;
193
- while (routeQueue.length) {
194
- const routeName = routeQueue.shift();
195
- if (visitedRoutes.has(routeName)) {
196
- continue;
197
- }
198
- const routePools = deps.routing[routeName];
199
- if (!routeHasTargets(routePools)) {
200
- visitedRoutes.add(routeName);
201
- attempted.push(`${routeName}:empty`);
202
- continue;
203
- }
204
- visitedRoutes.add(routeName);
205
- const orderedPools = sortRoutePools(routePools);
206
- for (const poolTier of orderedPools) {
207
- const { providerKey, poolTargets, tierId, failureHint } = trySelectFromTier(routeName, poolTier, stickyKey, estimatedTokens, features, deps, {
208
- disabledProviders,
209
- disabledKeysMap,
210
- allowedProviders,
211
- disabledModels,
212
- requiredProviderKeys: options.requiredProviderKeys,
213
- allowAliasRotation: options.allowAliasRotation
214
- });
215
- if (providerKey) {
216
- return { providerKey, routeUsed: routeName, pool: poolTargets, poolId: tierId };
217
- }
218
- if (failureHint) {
219
- attempted.push(failureHint);
220
- }
221
- }
222
- }
223
- const requestedRoute = normalizeRouteAlias(classification.routeName || DEFAULT_ROUTE);
224
- throw new VirtualRouterError(`All providers unavailable for route ${requestedRoute}`, VirtualRouterErrorCode.PROVIDER_NOT_AVAILABLE, { routeName: requestedRoute, attempted });
225
- }
1
+ export { selectProviderImpl, selectDirectProviderModel, selectFromStickyPool } from './engine/routing-pools/index.js';
@@ -70,26 +70,9 @@ export declare class VirtualRouterEngine {
70
70
  private providerHealthConfig;
71
71
  private resolveStickyKey;
72
72
  private resolveSessionScope;
73
- private resolveAntigravityAliasReuseCooldownMs;
74
- private resolveAntigravityLeaseScope;
75
- private buildScopedSessionKey;
76
- private buildAntigravityLeaseRuntimeKey;
77
- private extractRuntimeKey;
78
- private recordAntigravitySessionLease;
79
- private resolveAntigravityAliasLeasePersistPath;
80
- private hydrateAntigravityAliasLeaseStoreIfNeeded;
81
- private scheduleAntigravityAliasLeaseStoreFlush;
82
- private flushAntigravityAliasLeaseStoreSync;
83
- private resolveStopMessageScope;
84
- private getRoutingInstructionState;
85
- private buildMetadataInstructions;
86
- private parseMetadataDisableDescriptor;
87
- private parseMetadataForceProviderKey;
88
- private resolveRoutingMode;
89
73
  private resolveInstructionTarget;
90
74
  private filterCandidatesByRoutingState;
91
75
  private selectFromCandidates;
92
- private extractProviderId;
93
76
  /**
94
77
  * 在已有候选路由集合上,筛选出真正挂载了 sticky 池内 providerKey 的路由,
95
78
  * 并按 ROUTE_PRIORITY 进行排序;同时显式排除 tools 路由,保证一旦进入
@@ -104,13 +87,11 @@ export declare class VirtualRouterEngine {
104
87
  * - 仍然尊重 allowed/disabledProviders、disabledKeys、disabledModels 以及上下文长度。
105
88
  */
106
89
  private selectFromStickyPool;
107
- private extractKeyAlias;
108
- private normalizeAliasDescriptor;
109
- private extractKeyIndex;
110
- private getProviderModelId;
111
90
  private extractExcludedProviderKeySet;
112
91
  private buildRouteCandidates;
113
92
  private reorderForInlineVision;
93
+ private reorderForPreferredModel;
94
+ private routeSupportsModel;
114
95
  private routeSupportsInlineVision;
115
96
  private sortByPriority;
116
97
  private routeWeight;
@@ -119,8 +100,6 @@ export declare class VirtualRouterEngine {
119
100
  private hasPrimaryPool;
120
101
  private sortRoutePools;
121
102
  private flattenPoolTargets;
122
- private isRoutingStateEmpty;
123
- private persistRoutingInstructionState;
124
103
  private markProviderCooldown;
125
104
  private clearProviderCooldown;
126
105
  private isProviderCoolingDown;