@jsonstudio/llms 0.6.1462 → 0.6.1733
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/codecs/gemini-openai-codec.js +6 -1
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +4 -7
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +140 -21
- package/dist/conversion/compat/actions/antigravity-thought-signature-cache.js +68 -10
- package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +151 -23
- package/dist/conversion/compat/actions/gemini-cli-request.js +72 -13
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +10 -0
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +121 -0
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.d.ts +10 -0
- package/dist/conversion/compat/actions/iflow-kimi-cli-defaults.js +80 -0
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.d.ts +7 -0
- package/dist/conversion/compat/actions/iflow-kimi-history-media-placeholder.js +161 -0
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.d.ts +12 -0
- package/dist/conversion/compat/actions/iflow-kimi-thinking-reasoning-fill.js +67 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.d.ts +9 -0
- package/dist/conversion/compat/actions/iflow-response-body-unwrap.js +140 -0
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.d.ts +10 -0
- package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +59 -0
- package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.d.ts +14 -0
- package/dist/conversion/compat/actions/lmstudio-responses-input-stringify.js +125 -0
- package/dist/conversion/compat/actions/normalize-tool-call-ids.d.ts +11 -0
- package/dist/conversion/compat/actions/normalize-tool-call-ids.js +140 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.d.ts +2 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +152 -0
- package/dist/conversion/compat/antigravity-session-signature.d.ts +57 -3
- package/dist/conversion/compat/antigravity-session-signature.js +821 -27
- package/dist/conversion/compat/profiles/anthropic-claude-code.json +0 -9
- package/dist/conversion/compat/profiles/chat-gemini-cli.json +1 -0
- package/dist/conversion/compat/profiles/chat-iflow.json +6 -0
- package/dist/conversion/compat/profiles/chat-lmstudio.json +7 -1
- package/dist/conversion/hub/operation-table/operation-table-runner.js +1 -1
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +52 -10
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +102 -6
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.d.ts +2 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +63 -0
- package/dist/conversion/hub/pipeline/compat/compat-profile-store.js +12 -3
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +18 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +25 -1
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +20 -0
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +8 -5
- package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +5 -1
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +113 -0
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +26 -1
- package/dist/conversion/hub/pipeline/target-utils.js +3 -0
- package/dist/conversion/hub/process/chat-process.js +300 -67
- package/dist/conversion/hub/response/provider-response.js +31 -4
- package/dist/conversion/responses/responses-openai-bridge.js +32 -6
- package/dist/conversion/shared/anthropic-message-utils.js +20 -5
- package/dist/conversion/shared/bridge-id-utils.d.ts +2 -0
- package/dist/conversion/shared/bridge-id-utils.js +52 -15
- package/dist/conversion/shared/gemini-tool-utils.js +134 -9
- package/dist/conversion/shared/responses-conversation-store.js +40 -5
- package/dist/conversion/shared/responses-output-builder.js +23 -7
- package/dist/conversion/shared/responses-tool-utils.d.ts +1 -0
- package/dist/conversion/shared/responses-tool-utils.js +30 -13
- package/dist/conversion/shared/text-markup-normalizer.d.ts +1 -0
- package/dist/conversion/shared/text-markup-normalizer.js +359 -2
- package/dist/conversion/shared/thought-signature-validator.d.ts +1 -1
- package/dist/conversion/shared/thought-signature-validator.js +2 -1
- package/dist/quota/apikey-reset.d.ts +17 -0
- package/dist/quota/apikey-reset.js +43 -0
- package/dist/quota/index.d.ts +2 -0
- package/dist/quota/index.js +1 -0
- package/dist/quota/quota-manager.d.ts +44 -0
- package/dist/quota/quota-manager.js +491 -0
- package/dist/quota/quota-state.d.ts +6 -0
- package/dist/quota/quota-state.js +167 -0
- package/dist/quota/types.d.ts +61 -0
- package/dist/quota/types.js +1 -0
- package/dist/router/virtual-router/bootstrap.js +134 -13
- package/dist/router/virtual-router/classifier.js +1 -1
- package/dist/router/virtual-router/engine/antigravity/alias-lease.d.ts +33 -0
- package/dist/router/virtual-router/engine/antigravity/alias-lease.js +247 -0
- package/dist/router/virtual-router/engine/health/index.d.ts +23 -0
- package/dist/router/virtual-router/engine/health/index.js +720 -0
- package/dist/router/virtual-router/engine/provider-key/parse.d.ts +6 -0
- package/dist/router/virtual-router/engine/provider-key/parse.js +43 -0
- package/dist/router/virtual-router/engine/routing-pools/index.d.ts +13 -0
- package/dist/router/virtual-router/engine/routing-pools/index.js +225 -0
- package/dist/router/virtual-router/engine/routing-state/keys.d.ts +3 -0
- package/dist/router/virtual-router/engine/routing-state/keys.js +30 -0
- package/dist/router/virtual-router/engine/routing-state/metadata.d.ts +6 -0
- package/dist/router/virtual-router/engine/routing-state/metadata.js +132 -0
- package/dist/router/virtual-router/engine/routing-state/store.d.ts +11 -0
- package/dist/router/virtual-router/engine/routing-state/store.js +107 -0
- package/dist/router/virtual-router/engine-health.d.ts +1 -23
- package/dist/router/virtual-router/engine-health.js +1 -616
- package/dist/router/virtual-router/engine-selection/route-utils.js +57 -0
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +18 -0
- package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +1 -2
- package/dist/router/virtual-router/engine-selection/tier-priority.js +2 -2
- package/dist/router/virtual-router/engine-selection/tier-selection-select.js +39 -55
- package/dist/router/virtual-router/engine-selection/tier-selection.js +284 -23
- package/dist/router/virtual-router/engine-selection.d.ts +1 -13
- package/dist/router/virtual-router/engine-selection.js +1 -225
- package/dist/router/virtual-router/engine.d.ts +8 -14
- package/dist/router/virtual-router/engine.js +187 -382
- package/dist/router/virtual-router/features.js +20 -2
- package/dist/router/virtual-router/message-utils.js +15 -5
- package/dist/router/virtual-router/success-center.d.ts +10 -0
- package/dist/router/virtual-router/success-center.js +32 -0
- package/dist/router/virtual-router/types.d.ts +48 -0
- package/dist/servertool/clock/config.d.ts +2 -0
- package/dist/servertool/clock/config.js +10 -2
- package/dist/servertool/clock/daemon.js +3 -0
- package/dist/servertool/clock/ntp.d.ts +18 -0
- package/dist/servertool/clock/ntp.js +318 -0
- package/dist/servertool/clock/paths.d.ts +1 -0
- package/dist/servertool/clock/paths.js +3 -0
- package/dist/servertool/clock/state.d.ts +2 -0
- package/dist/servertool/clock/state.js +15 -2
- package/dist/servertool/clock/tasks.d.ts +1 -0
- package/dist/servertool/clock/tasks.js +24 -1
- package/dist/servertool/clock/types.d.ts +21 -0
- package/dist/servertool/engine.js +109 -5
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.d.ts +1 -0
- package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +201 -0
- package/dist/servertool/handlers/clock-auto.js +39 -4
- package/dist/servertool/handlers/clock.js +145 -16
- package/dist/servertool/handlers/followup-request-builder.js +84 -0
- package/dist/servertool/handlers/gemini-empty-reply-continue.js +48 -47
- package/dist/servertool/handlers/stop-message-auto.js +3 -3
- package/dist/servertool/handlers/vision.js +10 -0
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +1 -0
- package/dist/servertool/types.d.ts +2 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +6 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +32 -2
- package/dist/sse/sse-to-json/parsers/sse-parser.js +34 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +33 -1
- package/dist/tools/apply-patch/args-normalizer/default-actions.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/default-actions.js +12 -0
- package/dist/tools/apply-patch/args-normalizer/extract-patch.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/extract-patch.js +15 -0
- package/dist/tools/apply-patch/args-normalizer/index.d.ts +2 -0
- package/dist/tools/apply-patch/args-normalizer/index.js +164 -0
- package/dist/tools/apply-patch/args-normalizer/structured-builders.d.ts +7 -0
- package/dist/tools/apply-patch/args-normalizer/structured-builders.js +85 -0
- package/dist/tools/apply-patch/args-normalizer/types.d.ts +54 -0
- package/dist/tools/apply-patch/args-normalizer/types.js +1 -0
- package/dist/tools/apply-patch/execution-capturer.js +24 -3
- package/dist/tools/apply-patch/patch-text/looks-like-patch.js +1 -0
- package/dist/tools/apply-patch/patch-text/normalize.js +104 -5
- package/dist/tools/apply-patch/structured/coercion.js +28 -4
- package/dist/tools/apply-patch/validator.js +7 -146
- package/package.json +3 -2
|
@@ -13,6 +13,24 @@ export type SelectionDeps = {
|
|
|
13
13
|
resolveStickyKey: (metadata: RouterMetadataInput) => string | undefined;
|
|
14
14
|
quotaView?: ProviderQuotaView;
|
|
15
15
|
aliasQueueStore?: Map<string, string[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Antigravity alias session lease (session isolation) store.
|
|
18
|
+
* Key: runtimeKey (providerId.keyAlias), e.g. "antigravity.aliasA".
|
|
19
|
+
*/
|
|
20
|
+
antigravityAliasLeaseStore?: Map<string, {
|
|
21
|
+
sessionKey: string;
|
|
22
|
+
lastSeenAt: number;
|
|
23
|
+
}>;
|
|
24
|
+
/**
|
|
25
|
+
* Session → runtimeKey mapping for Antigravity alias leases.
|
|
26
|
+
* Key: session scope key, e.g. "session:abc" / "conversation:xyz".
|
|
27
|
+
* Value: runtimeKey (providerId.keyAlias)
|
|
28
|
+
*/
|
|
29
|
+
antigravitySessionAliasStore?: Map<string, string>;
|
|
30
|
+
/**
|
|
31
|
+
* Cooldown window (ms) before an Antigravity alias can be reused by a different session.
|
|
32
|
+
*/
|
|
33
|
+
antigravityAliasReuseCooldownMs?: number;
|
|
16
34
|
};
|
|
17
35
|
export type TrySelectFromTierOptions = {
|
|
18
36
|
disabledProviders?: Set<string>;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { ProviderHealthManager } from '../health-manager.js';
|
|
2
1
|
import type { ProviderRegistry } from '../provider-registry.js';
|
|
3
2
|
export declare function pickPriorityGroup(opts: {
|
|
4
3
|
candidates: string[];
|
|
5
4
|
orderedTargets: string[];
|
|
6
5
|
providerRegistry: ProviderRegistry;
|
|
7
|
-
|
|
6
|
+
availabilityCheck: (key: string) => boolean;
|
|
8
7
|
penalties?: Record<string, number>;
|
|
9
8
|
}): {
|
|
10
9
|
groupId: string;
|
|
@@ -31,12 +31,12 @@ function resolvePriorityMeta(orderedTargets, providerRegistry) {
|
|
|
31
31
|
return meta;
|
|
32
32
|
}
|
|
33
33
|
export function pickPriorityGroup(opts) {
|
|
34
|
-
const { candidates, orderedTargets, providerRegistry,
|
|
34
|
+
const { candidates, orderedTargets, providerRegistry, availabilityCheck, penalties } = opts;
|
|
35
35
|
const meta = resolvePriorityMeta(orderedTargets, providerRegistry);
|
|
36
36
|
let bestGroupId = null;
|
|
37
37
|
let bestScore = Number.NEGATIVE_INFINITY;
|
|
38
38
|
for (const key of candidates) {
|
|
39
|
-
if (!
|
|
39
|
+
if (!availabilityCheck(key))
|
|
40
40
|
continue;
|
|
41
41
|
const m = meta.get(key);
|
|
42
42
|
if (!m)
|
|
@@ -67,7 +67,7 @@ function applyAliasStickyQueuePinning(opts) {
|
|
|
67
67
|
excludedProviderKeys: excludedKeys,
|
|
68
68
|
aliasOfKey: extractKeyAlias,
|
|
69
69
|
modelIdOfKey: (key) => getProviderModelId(key, deps.providerRegistry),
|
|
70
|
-
availabilityCheck: (key) => deps.healthManager.isAvailable(key)
|
|
70
|
+
availabilityCheck: (key) => deps.healthManager.isAvailable(key) || Boolean(deps.quotaView?.(key))
|
|
71
71
|
});
|
|
72
72
|
if (pinned && pinned.length) {
|
|
73
73
|
pinnedByGroup.set(groupId, new Set(pinned));
|
|
@@ -137,9 +137,33 @@ function preferAntigravityAliasesOnRetry(opts) {
|
|
|
137
137
|
export function selectProviderKeyFromCandidatePool(opts) {
|
|
138
138
|
const { routeName, tier, stickyKey, candidates, isSafePool, deps, options, contextResult, warnRatio, excludedKeys, isRecoveryAttempt, now, nowForWeights, healthWeightedCfg, contextWeightedCfg } = opts;
|
|
139
139
|
const quotaView = deps.quotaView;
|
|
140
|
+
const isAvailable = (key) => {
|
|
141
|
+
if (!quotaView) {
|
|
142
|
+
return deps.healthManager.isAvailable(key);
|
|
143
|
+
}
|
|
144
|
+
const entry = quotaView(key);
|
|
145
|
+
if (!entry) {
|
|
146
|
+
// When quotaView is present, quota is the source of truth for availability.
|
|
147
|
+
// Treat unknown entries as "in pool" so routing does not depend on router-local health.
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
if (entry.inPool === false) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
if (entry.cooldownUntil && entry.cooldownUntil > now) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (entry.blacklistUntil && entry.blacklistUntil > now) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
// When quotaView is injected, quota is the source of truth for availability.
|
|
160
|
+
// Do not let router-local health snapshots (which may be stale or intentionally disabled)
|
|
161
|
+
// prevent selection for in-pool targets.
|
|
162
|
+
return true;
|
|
163
|
+
};
|
|
140
164
|
const selectFirstAvailable = (keys) => {
|
|
141
165
|
for (const key of keys) {
|
|
142
|
-
if (
|
|
166
|
+
if (isAvailable(key)) {
|
|
143
167
|
return key;
|
|
144
168
|
}
|
|
145
169
|
}
|
|
@@ -171,7 +195,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
171
195
|
candidates: pinnedCandidates,
|
|
172
196
|
orderedTargets: tier.targets,
|
|
173
197
|
providerRegistry: deps.providerRegistry,
|
|
174
|
-
|
|
198
|
+
availabilityCheck: isAvailable
|
|
175
199
|
});
|
|
176
200
|
if (!group) {
|
|
177
201
|
return null;
|
|
@@ -198,7 +222,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
198
222
|
candidates: group.groupCandidates,
|
|
199
223
|
stickyKey: options.allowAliasRotation ? undefined : stickyKey,
|
|
200
224
|
weights,
|
|
201
|
-
availabilityCheck:
|
|
225
|
+
availabilityCheck: isAvailable
|
|
202
226
|
}, 'round-robin');
|
|
203
227
|
}
|
|
204
228
|
const weights = (() => {
|
|
@@ -223,7 +247,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
223
247
|
candidates: pinnedCandidates,
|
|
224
248
|
stickyKey: options.allowAliasRotation ? undefined : stickyKey,
|
|
225
249
|
weights,
|
|
226
|
-
availabilityCheck:
|
|
250
|
+
availabilityCheck: isAvailable
|
|
227
251
|
}, tier.mode === 'round-robin' ? 'round-robin' : undefined);
|
|
228
252
|
}
|
|
229
253
|
const buckets = new Map();
|
|
@@ -258,7 +282,8 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
258
282
|
if (!bucket.length) {
|
|
259
283
|
continue;
|
|
260
284
|
}
|
|
261
|
-
|
|
285
|
+
// Keep candidate ordering stable (config order). Availability/blacklist/cooldown still apply.
|
|
286
|
+
bucket.sort((a, b) => a.order - b.order);
|
|
262
287
|
let bucketCandidates = bucket.map((item) => item.key);
|
|
263
288
|
// Single-provider pool should never be "emptied" by health/cooldown.
|
|
264
289
|
if (bucketCandidates.length === 1) {
|
|
@@ -275,10 +300,6 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
275
300
|
deps,
|
|
276
301
|
excludedKeys
|
|
277
302
|
});
|
|
278
|
-
const bucketPenaltyMap = {};
|
|
279
|
-
for (const item of bucket) {
|
|
280
|
-
bucketPenaltyMap[item.key] = item.penalty;
|
|
281
|
-
}
|
|
282
303
|
const bucketWeights = {};
|
|
283
304
|
const bucketMultipliers = {};
|
|
284
305
|
for (const item of bucket) {
|
|
@@ -313,8 +334,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
313
334
|
candidates: bucketCandidates,
|
|
314
335
|
orderedTargets: tier.targets,
|
|
315
336
|
providerRegistry: deps.providerRegistry,
|
|
316
|
-
|
|
317
|
-
penalties: bucketPenaltyMap
|
|
337
|
+
availabilityCheck: isAvailable
|
|
318
338
|
});
|
|
319
339
|
if (!group) {
|
|
320
340
|
continue;
|
|
@@ -328,57 +348,21 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
328
348
|
candidates: group.groupCandidates,
|
|
329
349
|
stickyKey: options.allowAliasRotation ? undefined : stickyKey,
|
|
330
350
|
weights: groupWeights,
|
|
331
|
-
availabilityCheck:
|
|
351
|
+
availabilityCheck: isAvailable
|
|
332
352
|
}, 'round-robin');
|
|
333
353
|
if (selected) {
|
|
334
354
|
return selected;
|
|
335
355
|
}
|
|
336
356
|
continue;
|
|
337
357
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if (!deps.healthManager.isAvailable(key))
|
|
343
|
-
continue;
|
|
344
|
-
const m = bucketMultipliers[key] ?? 1;
|
|
345
|
-
if (m > bestM) {
|
|
346
|
-
bestM = m;
|
|
347
|
-
best = key;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (best) {
|
|
351
|
-
return best;
|
|
352
|
-
}
|
|
353
|
-
continue;
|
|
354
|
-
}
|
|
355
|
-
else if (isRecoveryAttempt) {
|
|
356
|
-
const recovered = selectFirstAvailable(bucketCandidates);
|
|
357
|
-
if (recovered)
|
|
358
|
-
return recovered;
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
358
|
+
const recovered = selectFirstAvailable(bucketCandidates);
|
|
359
|
+
if (recovered)
|
|
360
|
+
return recovered;
|
|
361
|
+
continue;
|
|
361
362
|
// (unreachable) recovery handled above
|
|
362
363
|
}
|
|
363
364
|
else {
|
|
364
|
-
if (isRecoveryAttempt
|
|
365
|
-
let best = null;
|
|
366
|
-
let bestM = Number.NEGATIVE_INFINITY;
|
|
367
|
-
for (const key of bucketCandidates) {
|
|
368
|
-
if (!deps.healthManager.isAvailable(key))
|
|
369
|
-
continue;
|
|
370
|
-
const m = bucketMultipliers[key] ?? 1;
|
|
371
|
-
if (m > bestM) {
|
|
372
|
-
bestM = m;
|
|
373
|
-
best = key;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
if (best) {
|
|
377
|
-
return best;
|
|
378
|
-
}
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
else if (isRecoveryAttempt) {
|
|
365
|
+
if (isRecoveryAttempt) {
|
|
382
366
|
const recovered = selectFirstAvailable(bucketCandidates);
|
|
383
367
|
if (recovered)
|
|
384
368
|
return recovered;
|
|
@@ -389,7 +373,7 @@ export function selectProviderKeyFromCandidatePool(opts) {
|
|
|
389
373
|
candidates: bucketCandidates,
|
|
390
374
|
stickyKey: options.allowAliasRotation ? undefined : stickyKey,
|
|
391
375
|
weights: bucketWeights,
|
|
392
|
-
availabilityCheck:
|
|
376
|
+
availabilityCheck: isAvailable
|
|
393
377
|
}, tier.mode === 'round-robin' ? 'round-robin' : undefined);
|
|
394
378
|
if (selected) {
|
|
395
379
|
return selected;
|
|
@@ -4,6 +4,34 @@ import { resolveHealthWeightedConfig } from '../health-weighted.js';
|
|
|
4
4
|
import { pinCandidatesByAliasQueue, resolveAliasSelectionStrategy } from './alias-selection.js';
|
|
5
5
|
import { extractKeyAlias, extractKeyIndex, extractProviderId, getProviderModelId } from './key-parsing.js';
|
|
6
6
|
import { selectProviderKeyFromCandidatePool } from './tier-selection-select.js';
|
|
7
|
+
import { lookupAntigravityPinnedAliasForSessionId, unpinAntigravitySessionAliasForSessionId } from '../../../conversion/compat/antigravity-session-signature.js';
|
|
8
|
+
const DEFAULT_ANTIGRAVITY_ALIAS_SESSION_COOLDOWN_MS = 5 * 60_000;
|
|
9
|
+
function isAntigravityGeminiModelKey(providerKey, deps) {
|
|
10
|
+
if ((extractProviderId(providerKey) ?? '') !== 'antigravity') {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const modelId = getProviderModelId(providerKey, deps.providerRegistry) ?? '';
|
|
14
|
+
return modelId.trim().toLowerCase().startsWith('gemini-');
|
|
15
|
+
}
|
|
16
|
+
function extractAntigravityRuntimeBase(providerKey) {
|
|
17
|
+
const value = typeof providerKey === 'string' ? providerKey.trim() : '';
|
|
18
|
+
if (!value)
|
|
19
|
+
return null;
|
|
20
|
+
const firstDot = value.indexOf('.');
|
|
21
|
+
if (firstDot <= 0 || firstDot === value.length - 1)
|
|
22
|
+
return null;
|
|
23
|
+
const secondDot = value.indexOf('.', firstDot + 1);
|
|
24
|
+
if (secondDot <= firstDot + 1)
|
|
25
|
+
return null;
|
|
26
|
+
const providerId = value.slice(0, firstDot);
|
|
27
|
+
const alias = value.slice(firstDot + 1, secondDot);
|
|
28
|
+
if (!providerId || !alias)
|
|
29
|
+
return null;
|
|
30
|
+
return `${providerId}.${alias}`;
|
|
31
|
+
}
|
|
32
|
+
function buildAntigravityLeaseRuntimeKey(runtimeBase) {
|
|
33
|
+
return `${runtimeBase}::gemini`;
|
|
34
|
+
}
|
|
7
35
|
function shouldAvoidAllAntigravityOnRetry(metadata) {
|
|
8
36
|
if (!metadata || typeof metadata !== 'object') {
|
|
9
37
|
return false;
|
|
@@ -15,6 +43,27 @@ function shouldAvoidAllAntigravityOnRetry(metadata) {
|
|
|
15
43
|
const rt = rtRaw;
|
|
16
44
|
return rt.antigravityAvoidAllOnRetry === true;
|
|
17
45
|
}
|
|
46
|
+
function isAntigravityGeminiSessionBindingDisabled(metadata) {
|
|
47
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const rtRaw = metadata.__rt;
|
|
51
|
+
if (!rtRaw || typeof rtRaw !== 'object' || Array.isArray(rtRaw)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const rt = rtRaw;
|
|
55
|
+
if (rt.disableAntigravitySessionBinding === true) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
const mode = rt.antigravitySessionBinding;
|
|
59
|
+
if (mode === false) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
if (typeof mode === 'string' && ['0', 'false', 'off', 'disabled', 'none'].includes(mode.trim().toLowerCase())) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
18
67
|
function shouldAvoidAntigravityAfterRepeatedError(metadata) {
|
|
19
68
|
if (!metadata || typeof metadata !== 'object') {
|
|
20
69
|
return false;
|
|
@@ -46,9 +95,181 @@ function extractNonAntigravityTargets(targets) {
|
|
|
46
95
|
}
|
|
47
96
|
return targets.filter((key) => (extractProviderId(key) ?? '') !== 'antigravity');
|
|
48
97
|
}
|
|
98
|
+
function resolveSessionScopeKey(metadata) {
|
|
99
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
const record = metadata;
|
|
103
|
+
const sessionId = typeof record.sessionId === 'string' ? record.sessionId.trim() : '';
|
|
104
|
+
if (sessionId) {
|
|
105
|
+
return `session:${sessionId}`;
|
|
106
|
+
}
|
|
107
|
+
const conversationId = typeof record.conversationId === 'string' ? record.conversationId.trim() : '';
|
|
108
|
+
if (conversationId) {
|
|
109
|
+
return `conversation:${conversationId}`;
|
|
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
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
function buildScopedSessionKey(sessionKey) {
|
|
122
|
+
// Policy: antigravity alias/session binding applies only to Gemini models.
|
|
123
|
+
return `${sessionKey}::gemini`;
|
|
124
|
+
}
|
|
125
|
+
function extractLeaseRuntimeKey(providerKey, deps) {
|
|
126
|
+
const base = extractAntigravityRuntimeBase(providerKey);
|
|
127
|
+
if (!base)
|
|
128
|
+
return null;
|
|
129
|
+
if ((extractProviderId(providerKey) ?? '') !== 'antigravity')
|
|
130
|
+
return base;
|
|
131
|
+
if (!isAntigravityGeminiModelKey(providerKey, deps)) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return buildAntigravityLeaseRuntimeKey(base);
|
|
135
|
+
}
|
|
136
|
+
function applyAntigravityAliasSessionLeases(targets, deps, metadata) {
|
|
137
|
+
if (!Array.isArray(targets) || targets.length === 0) {
|
|
138
|
+
return { targets, blocked: 0, preferredPinned: false, pinnedStrict: false };
|
|
139
|
+
}
|
|
140
|
+
if (isAntigravityGeminiSessionBindingDisabled(metadata)) {
|
|
141
|
+
return { targets, blocked: 0, preferredPinned: false, pinnedStrict: false };
|
|
142
|
+
}
|
|
143
|
+
const leaseStore = deps.antigravityAliasLeaseStore;
|
|
144
|
+
const sessionAliasStore = deps.antigravitySessionAliasStore;
|
|
145
|
+
if (!leaseStore || !sessionAliasStore) {
|
|
146
|
+
return { targets, blocked: 0, preferredPinned: false, pinnedStrict: false };
|
|
147
|
+
}
|
|
148
|
+
const sessionKey = resolveSessionScopeKey(metadata);
|
|
149
|
+
if (!sessionKey) {
|
|
150
|
+
return { targets, blocked: 0, preferredPinned: false, pinnedStrict: false };
|
|
151
|
+
}
|
|
152
|
+
const cooldownMs = typeof deps.antigravityAliasReuseCooldownMs === 'number' && Number.isFinite(deps.antigravityAliasReuseCooldownMs)
|
|
153
|
+
? Math.max(0, Math.floor(deps.antigravityAliasReuseCooldownMs))
|
|
154
|
+
: DEFAULT_ANTIGRAVITY_ALIAS_SESSION_COOLDOWN_MS;
|
|
155
|
+
const now = Date.now();
|
|
156
|
+
const bindingModeRaw = deps.loadBalancer.getPolicy().aliasSelection
|
|
157
|
+
?.antigravitySessionBinding;
|
|
158
|
+
const strictRequested = typeof bindingModeRaw === 'string' && bindingModeRaw.trim().toLowerCase() === 'strict';
|
|
159
|
+
const agSessionId = metadata && typeof metadata === 'object' && typeof metadata.antigravitySessionId === 'string'
|
|
160
|
+
? String(metadata.antigravitySessionId).trim()
|
|
161
|
+
: '';
|
|
162
|
+
const hasAntigravityGeminiTargets = targets.some((key) => isAntigravityGeminiModelKey(key, deps));
|
|
163
|
+
let pinnedRuntimeKey = strictRequested && agSessionId && hasAntigravityGeminiTargets
|
|
164
|
+
? lookupAntigravityPinnedAliasForSessionId(agSessionId, { hydrate: true })
|
|
165
|
+
: undefined;
|
|
166
|
+
const pinnedLeaseKey = pinnedRuntimeKey ? buildAntigravityLeaseRuntimeKey(pinnedRuntimeKey) : undefined;
|
|
167
|
+
// If the pinned alias is completely out of pool (quota exhausted), release the pin so we can rotate.
|
|
168
|
+
if (pinnedRuntimeKey && deps.quotaView && agSessionId) {
|
|
169
|
+
const pinnedKeys = targets.filter((key) => isAntigravityGeminiModelKey(key, deps) && extractLeaseRuntimeKey(key, deps) === pinnedLeaseKey);
|
|
170
|
+
if (pinnedKeys.length > 0) {
|
|
171
|
+
const allOutOfPool = pinnedKeys.every((key) => deps.quotaView?.(key)?.inPool === false);
|
|
172
|
+
if (allOutOfPool) {
|
|
173
|
+
const releasedRuntimeKey = pinnedRuntimeKey;
|
|
174
|
+
unpinAntigravitySessionAliasForSessionId(agSessionId);
|
|
175
|
+
pinnedRuntimeKey = undefined;
|
|
176
|
+
sessionAliasStore.delete(buildScopedSessionKey(sessionKey));
|
|
177
|
+
try {
|
|
178
|
+
const raw = String(process.env.ROUTECODEX_STAGE_LOG || process.env.RCC_STAGE_LOG || '').trim().toLowerCase();
|
|
179
|
+
const enabled = raw !== '' && raw !== '0' && raw !== 'false' && raw !== 'no';
|
|
180
|
+
if (enabled) {
|
|
181
|
+
console.log('[virtual-router][antigravity-session-binding] unpin', JSON.stringify({ agSessionId, runtimeKey: releasedRuntimeKey }));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// ignore
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
const strictBinding = strictRequested && Boolean(pinnedLeaseKey);
|
|
191
|
+
const geminiSessionKey = buildScopedSessionKey(sessionKey);
|
|
192
|
+
let preferredGeminiRuntimeKey = pinnedLeaseKey || sessionAliasStore.get(geminiSessionKey);
|
|
193
|
+
if (preferredGeminiRuntimeKey && !pinnedLeaseKey) {
|
|
194
|
+
const lease = leaseStore.get(preferredGeminiRuntimeKey);
|
|
195
|
+
if (lease && lease.sessionKey !== geminiSessionKey && now - lease.lastSeenAt < cooldownMs) {
|
|
196
|
+
preferredGeminiRuntimeKey = undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// If a previously bound alias is completely out of pool (quota exhausted / blacklisted),
|
|
200
|
+
// release the binding so the session can rebind to a different alias on the next successful call.
|
|
201
|
+
if (deps.quotaView && !pinnedLeaseKey && preferredGeminiRuntimeKey) {
|
|
202
|
+
const pinnedKeys = targets.filter((key) => isAntigravityGeminiModelKey(key, deps) && extractLeaseRuntimeKey(key, deps) === preferredGeminiRuntimeKey);
|
|
203
|
+
if (pinnedKeys.length > 0) {
|
|
204
|
+
const allOutOfPool = pinnedKeys.every((key) => deps.quotaView?.(key)?.inPool === false);
|
|
205
|
+
if (allOutOfPool) {
|
|
206
|
+
const releasedRuntimeKey = preferredGeminiRuntimeKey;
|
|
207
|
+
sessionAliasStore.delete(geminiSessionKey);
|
|
208
|
+
preferredGeminiRuntimeKey = undefined;
|
|
209
|
+
try {
|
|
210
|
+
const raw = String(process.env.ROUTECODEX_STAGE_LOG || process.env.RCC_STAGE_LOG || '').trim().toLowerCase();
|
|
211
|
+
const enabled = raw !== '' && raw !== '0' && raw !== 'false' && raw !== 'no';
|
|
212
|
+
if (enabled) {
|
|
213
|
+
console.log('[virtual-router][antigravity-session-binding] release', JSON.stringify({ sessionKey: geminiSessionKey, runtimeKey: releasedRuntimeKey }));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// ignore
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const pinnedGemini = preferredGeminiRuntimeKey
|
|
223
|
+
? targets.filter((key) => isAntigravityGeminiModelKey(key, deps) && extractLeaseRuntimeKey(key, deps) === preferredGeminiRuntimeKey)
|
|
224
|
+
: [];
|
|
225
|
+
const preferredPinned = pinnedGemini.length > 0;
|
|
226
|
+
const pinnedSet = preferredPinned ? new Set([...pinnedGemini]) : null;
|
|
227
|
+
const candidates = preferredPinned
|
|
228
|
+
? [...pinnedGemini, ...targets.filter((key) => !pinnedSet.has(key))]
|
|
229
|
+
: targets;
|
|
230
|
+
const pinnedStrict = strictBinding && Boolean(preferredGeminiRuntimeKey);
|
|
231
|
+
let blocked = 0;
|
|
232
|
+
const filtered = candidates.filter((key) => {
|
|
233
|
+
const providerId = extractProviderId(key);
|
|
234
|
+
if (providerId !== 'antigravity') {
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
// Policy: antigravity alias/session binding applies only to Gemini models.
|
|
238
|
+
if (!isAntigravityGeminiModelKey(key, deps)) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
const scopedSessionKey = geminiSessionKey;
|
|
242
|
+
const runtimeKey = extractLeaseRuntimeKey(key, deps);
|
|
243
|
+
if (!runtimeKey) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
if (pinnedStrict) {
|
|
247
|
+
if (!isAntigravityGeminiModelKey(key, deps)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
if (preferredGeminiRuntimeKey && runtimeKey !== preferredGeminiRuntimeKey) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const lease = leaseStore.get(runtimeKey);
|
|
255
|
+
if (!lease) {
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
if (lease.sessionKey === scopedSessionKey) {
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
if (now - lease.lastSeenAt >= cooldownMs) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
blocked += 1;
|
|
265
|
+
return false;
|
|
266
|
+
});
|
|
267
|
+
return { targets: filtered, blocked, preferredPinned, pinnedStrict };
|
|
268
|
+
}
|
|
49
269
|
export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, features, deps, options) {
|
|
50
270
|
const { disabledProviders, disabledKeysMap, allowedProviders, disabledModels, requiredProviderKeys } = options;
|
|
51
271
|
let targets = Array.isArray(tier.targets) ? tier.targets : [];
|
|
272
|
+
let preLeaseTargets = null;
|
|
52
273
|
const excludedRaw = Array.isArray(features.metadata?.excludedProviderKeys)
|
|
53
274
|
? features.metadata.excludedProviderKeys
|
|
54
275
|
.filter((val) => typeof val === 'string')
|
|
@@ -70,10 +291,14 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
|
|
|
70
291
|
}
|
|
71
292
|
const singleCandidateFallback = targets.length === 1 ? targets[0] : undefined;
|
|
72
293
|
if (targets.length > 0) {
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
294
|
+
// When quotaView is present, cooldown is expressed via quotaView.{cooldownUntil,blacklistUntil,inPool}.
|
|
295
|
+
// Do not apply router-local cooldown filters in that mode.
|
|
296
|
+
if (!deps.quotaView) {
|
|
297
|
+
// Always respect cooldown signals. If a route/tier is depleted due to cooldown,
|
|
298
|
+
// routing is expected to fall back to other tiers/routes (e.g. longcontext → default),
|
|
299
|
+
// rather than repeatedly selecting the cooled-down provider.
|
|
300
|
+
targets = targets.filter((key) => !deps.isProviderCoolingDown(key));
|
|
301
|
+
}
|
|
77
302
|
}
|
|
78
303
|
if (allowedProviders && allowedProviders.size > 0) {
|
|
79
304
|
targets = targets.filter((key) => {
|
|
@@ -126,6 +351,23 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
|
|
|
126
351
|
if (requiredProviderKeys && requiredProviderKeys.size > 0) {
|
|
127
352
|
targets = targets.filter((key) => requiredProviderKeys.has(key));
|
|
128
353
|
}
|
|
354
|
+
// Antigravity session isolation:
|
|
355
|
+
// - One alias (auth key) must not be shared across different sessions within the cooldown window,
|
|
356
|
+
// otherwise upstream may respond with 429 due to cross-session contamination.
|
|
357
|
+
// - If the current session already has a leased alias, pin it when possible.
|
|
358
|
+
preLeaseTargets = targets;
|
|
359
|
+
const leaseResult = applyAntigravityAliasSessionLeases(targets, deps, features.metadata);
|
|
360
|
+
targets = leaseResult.targets;
|
|
361
|
+
// Default route must not fail purely due to Antigravity alias leasing.
|
|
362
|
+
// If *all* candidates are blocked by lease, fall back to the pre-lease pool and let upstream decide.
|
|
363
|
+
if (!targets.length &&
|
|
364
|
+
routeName === DEFAULT_ROUTE &&
|
|
365
|
+
preLeaseTargets &&
|
|
366
|
+
preLeaseTargets.length > 0 &&
|
|
367
|
+
leaseResult.blocked > 0 &&
|
|
368
|
+
!leaseResult.pinnedStrict) {
|
|
369
|
+
targets = preLeaseTargets;
|
|
370
|
+
}
|
|
129
371
|
const serverToolRequired = features.metadata?.serverToolRequired === true;
|
|
130
372
|
if (serverToolRequired) {
|
|
131
373
|
const filtered = [];
|
|
@@ -142,35 +384,54 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
|
|
|
142
384
|
}
|
|
143
385
|
targets = filtered;
|
|
144
386
|
}
|
|
145
|
-
if (features.hasImageAttachment
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
+
}
|
|
156
410
|
}
|
|
157
|
-
|
|
411
|
+
catch {
|
|
158
412
|
fallthrough.push(key);
|
|
159
413
|
}
|
|
160
414
|
}
|
|
161
|
-
|
|
162
|
-
|
|
415
|
+
if (prioritized.length) {
|
|
416
|
+
targets = prioritized;
|
|
163
417
|
}
|
|
164
418
|
}
|
|
165
|
-
if (prioritized.length) {
|
|
166
|
-
targets = prioritized;
|
|
167
|
-
}
|
|
168
419
|
}
|
|
169
420
|
if (!targets.length) {
|
|
170
|
-
|
|
421
|
+
const leaseHint = leaseResult.blocked > 0
|
|
422
|
+
? `${routeName}:${tier.id}:antigravity_alias_session_busy(${leaseResult.blocked})`
|
|
423
|
+
: `${routeName}:${tier.id}:empty`;
|
|
424
|
+
return { providerKey: null, poolTargets: [], tierId: tier.id, failureHint: leaseHint };
|
|
171
425
|
}
|
|
172
426
|
const contextResult = deps.contextAdvisor.classify(targets, estimatedTokens, (key) => deps.providerRegistry.get(key));
|
|
173
|
-
|
|
427
|
+
let prioritizedPools = buildContextCandidatePools(contextResult);
|
|
428
|
+
// ContextAdvisor overflow (ratio >= 1) is not always a hard stop: token estimation is approximate.
|
|
429
|
+
// For the default route, treat overflow as a last-resort candidate pool when hardLimit=false,
|
|
430
|
+
// to avoid route exhaustion when no other providers are available.
|
|
431
|
+
const hardLimit = deps.contextAdvisor.getConfig().hardLimit;
|
|
432
|
+
if (!hardLimit && routeName === DEFAULT_ROUTE && contextResult.overflow.length > 0) {
|
|
433
|
+
prioritizedPools = [...prioritizedPools, contextResult.overflow];
|
|
434
|
+
}
|
|
174
435
|
const avoidAntigravityOnRetry = isRecoveryAttempt && shouldAvoidAntigravityAfterRepeatedError(features.metadata);
|
|
175
436
|
const nonAntigravityTargets = avoidAntigravityOnRetry ? extractNonAntigravityTargets(targets) : [];
|
|
176
437
|
const poolsToTry = avoidAntigravityOnRetry && nonAntigravityTargets.length > 0
|
|
@@ -1,13 +1 @@
|
|
|
1
|
-
|
|
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';
|