@jsonstudio/llms 0.6.2979 → 0.6.3238

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 (246) hide show
  1. package/dist/conversion/args-mapping.js +8 -0
  2. package/dist/conversion/{shared/bridge-actions.js → bridge-actions.js} +2 -1
  3. package/dist/conversion/{shared/bridge-id-utils.js → bridge-id-utils.js} +1 -1
  4. package/dist/conversion/{shared/bridge-instructions.js → bridge-instructions.js} +1 -1
  5. package/dist/conversion/{shared/bridge-message-utils.d.ts → bridge-message-utils.d.ts} +1 -1
  6. package/dist/conversion/{shared/bridge-message-utils.js → bridge-message-utils.js} +5 -149
  7. package/dist/conversion/{shared/bridge-metadata.js → bridge-metadata.js} +1 -1
  8. package/dist/conversion/{shared/bridge-policies.js → bridge-policies.js} +1 -1
  9. package/dist/conversion/codecs/gemini-openai-codec.js +27 -8
  10. package/dist/conversion/codecs/responses-openai-codec.js +1 -1
  11. package/dist/conversion/{shared/compaction-detect.d.ts → compaction-detect.d.ts} +1 -1
  12. package/dist/conversion/compaction-detect.js +4 -0
  13. package/dist/conversion/compat/actions/apply-patch-fixer.js +2 -2
  14. package/dist/conversion/compat/actions/deepseek-web-response.d.ts +0 -1
  15. package/dist/conversion/compat/actions/deepseek-web-response.js +15 -405
  16. package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +1 -1
  17. package/dist/conversion/compat/actions/lmstudio-responses-fc-ids.js +1 -1
  18. package/dist/conversion/compat/actions/qwen-transform.js +74 -2
  19. package/dist/conversion/compat/actions/snapshot.js +1 -1
  20. package/dist/conversion/compat/antigravity-session-signature.js +36 -0
  21. package/dist/conversion/compat/profiles/chat-deepseek-web.json +0 -22
  22. package/dist/conversion/compat/profiles/chat-glm.json +251 -72
  23. package/dist/conversion/compat/profiles/chat-iflow.json +174 -39
  24. package/dist/conversion/compat/profiles/chat-lmstudio.json +43 -14
  25. package/dist/conversion/hub/operation-table/operation-table-runner.js +2 -2
  26. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +1 -1
  27. package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.d.ts +8 -0
  28. package/dist/conversion/hub/operation-table/semantic-mappers/archive/chat-mapper.archive.js +404 -0
  29. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +5 -381
  30. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +2 -2
  31. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +2 -8
  32. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +1 -0
  33. package/dist/conversion/hub/pipeline/hub-pipeline.js +50 -3
  34. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +1 -1
  35. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +62 -0
  36. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +3 -1
  37. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +1 -1
  38. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/chat-process-semantics-bridge.d.ts +1 -1
  39. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +42 -29
  40. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +12 -0
  41. package/dist/conversion/hub/policy/protocol-spec.js +1 -1
  42. package/dist/conversion/hub/process/chat-process-clock-reminders.js +1 -1
  43. package/dist/conversion/hub/process/chat-process-clock-tools.js +1 -1
  44. package/dist/conversion/hub/process/chat-process-continue-execution.js +1 -1
  45. package/dist/conversion/hub/process/chat-process-servertool-orchestration.js +1 -1
  46. package/dist/conversion/hub/process/chat-process-web-search.js +1 -1
  47. package/dist/conversion/hub/response/provider-response.js +14 -5
  48. package/dist/conversion/hub/response/response-mappers.js +23 -1
  49. package/dist/conversion/hub/response/response-runtime.js +28 -5
  50. package/dist/conversion/hub/snapshot-recorder.js +3 -92
  51. package/dist/conversion/hub/tool-governance/engine.d.ts +8 -0
  52. package/dist/conversion/hub/tool-governance/engine.js +40 -193
  53. package/dist/conversion/hub/tool-governance/rules.js +73 -69
  54. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +1 -1
  55. package/dist/conversion/index.d.ts +1 -2
  56. package/dist/conversion/index.js +1 -2
  57. package/dist/conversion/{shared/jsonish.js → jsonish.js} +1 -1
  58. package/dist/conversion/{shared/mcp-injection.js → mcp-injection.js} +1 -1
  59. package/dist/conversion/media.js +4 -0
  60. package/dist/conversion/{shared/metadata-passthrough.d.ts → metadata-passthrough.d.ts} +1 -1
  61. package/dist/conversion/{shared/metadata-passthrough.js → metadata-passthrough.js} +2 -2
  62. package/dist/conversion/payload-budget.js +47 -0
  63. package/dist/conversion/protocol-field-allowlists.d.ts +7 -0
  64. package/dist/conversion/protocol-field-allowlists.js +9 -0
  65. package/dist/conversion/{shared/protocol-state.d.ts → protocol-state.d.ts} +2 -2
  66. package/dist/conversion/{shared/protocol-state.js → protocol-state.js} +2 -2
  67. package/dist/conversion/{shared/errors.d.ts → provider-protocol-error.d.ts} +0 -3
  68. package/dist/conversion/provider-protocol-error.js +25 -0
  69. package/dist/conversion/responses/responses-openai-bridge/response-payload.js +8 -5
  70. package/dist/conversion/responses/responses-openai-bridge/types.d.ts +1 -1
  71. package/dist/conversion/responses/responses-openai-bridge.d.ts +1 -1
  72. package/dist/conversion/responses/responses-openai-bridge.js +43 -10
  73. package/dist/conversion/{shared/runtime-metadata.d.ts → runtime-metadata.d.ts} +1 -1
  74. package/dist/conversion/{shared/runtime-metadata.js → runtime-metadata.js} +2 -2
  75. package/dist/conversion/shared/anthropic-message-utils.js +19 -8
  76. package/dist/conversion/shared/chat-request-filters.d.ts +3 -4
  77. package/dist/conversion/shared/chat-request-filters.js +22 -78
  78. package/dist/conversion/shared/gemini-tool-utils.d.ts +1 -1
  79. package/dist/conversion/shared/openai-finalizer.js +1 -0
  80. package/dist/conversion/shared/openai-message-normalize.js +2 -2
  81. package/dist/conversion/shared/reasoning-normalizer.js +6 -0
  82. package/dist/conversion/shared/reasoning-utils.js +5 -2
  83. package/dist/conversion/shared/responses-conversation-store.js +1 -1
  84. package/dist/conversion/shared/responses-output-builder.js +55 -11
  85. package/dist/conversion/shared/responses-reasoning-registry.d.ts +14 -2
  86. package/dist/conversion/shared/responses-reasoning-registry.js +34 -6
  87. package/dist/conversion/shared/responses-response-utils.js +99 -9
  88. package/dist/conversion/shared/responses-tool-utils.js +1 -1
  89. package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +1 -1
  90. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -2
  91. package/dist/conversion/shared/text-markup-normalizer.js +1 -1
  92. package/dist/conversion/shared/tool-filter-pipeline.js +1 -1
  93. package/dist/conversion/shared/tool-governor.js +3 -3
  94. package/dist/conversion/shared/tool-mapping.d.ts +1 -1
  95. package/dist/conversion/{shared/snapshot-utils.d.ts → snapshot-utils.d.ts} +11 -0
  96. package/dist/conversion/{shared/snapshot-utils.js → snapshot-utils.js} +14 -23
  97. package/dist/conversion/types/text-markup-normalizer.d.ts +13 -0
  98. package/dist/conversion/types/text-markup-normalizer.js +1 -0
  99. package/dist/filters/special/request-tools-normalize.js +1 -1
  100. package/dist/filters/special/response-tool-text-canonicalize.js +2 -2
  101. package/dist/native/router_hotpath_napi.node +0 -0
  102. package/dist/quota/quota-manager.js +31 -59
  103. package/dist/quota/quota-state.js +14 -7
  104. package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +1 -0
  105. package/dist/router/virtual-router/bootstrap/profile-builder.js +13 -0
  106. package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +2 -0
  107. package/dist/router/virtual-router/bootstrap/provider-normalization.js +4 -1
  108. package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +7 -0
  109. package/dist/router/virtual-router/bootstrap/streaming-helpers.js +44 -0
  110. package/dist/router/virtual-router/bootstrap.js +2 -0
  111. package/dist/router/virtual-router/engine/routing-state/store.d.ts +1 -2
  112. package/dist/router/virtual-router/engine/routing-state/store.js +2 -2
  113. package/dist/router/virtual-router/engine-legacy/config.d.ts +11 -0
  114. package/dist/router/virtual-router/engine-legacy/config.js +108 -0
  115. package/dist/router/virtual-router/engine-legacy/direct-model.d.ts +10 -0
  116. package/dist/router/virtual-router/engine-legacy/direct-model.js +38 -0
  117. package/dist/router/virtual-router/engine-legacy/health.d.ts +13 -0
  118. package/dist/router/virtual-router/engine-legacy/health.js +104 -0
  119. package/dist/router/virtual-router/engine-legacy/helpers.d.ts +16 -0
  120. package/dist/router/virtual-router/engine-legacy/helpers.js +226 -0
  121. package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +9 -0
  122. package/dist/router/virtual-router/engine-legacy/route-finalize.js +84 -0
  123. package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +17 -0
  124. package/dist/router/virtual-router/engine-legacy/route-selection.js +205 -0
  125. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +3 -0
  126. package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +36 -0
  127. package/dist/router/virtual-router/engine-legacy/route-state.d.ts +12 -0
  128. package/dist/router/virtual-router/engine-legacy/route-state.js +386 -0
  129. package/dist/router/virtual-router/engine-legacy/route-utils.d.ts +19 -0
  130. package/dist/router/virtual-router/engine-legacy/route-utils.js +212 -0
  131. package/dist/router/virtual-router/engine-legacy/routing.d.ts +8 -0
  132. package/dist/router/virtual-router/engine-legacy/routing.js +8 -0
  133. package/dist/router/virtual-router/engine-legacy/selection-core.d.ts +28 -0
  134. package/dist/router/virtual-router/engine-legacy/selection-core.js +112 -0
  135. package/dist/router/virtual-router/engine-legacy/selection-state.d.ts +16 -0
  136. package/dist/router/virtual-router/engine-legacy/selection-state.js +187 -0
  137. package/dist/router/virtual-router/engine-legacy/state-accessors.d.ts +21 -0
  138. package/dist/router/virtual-router/engine-legacy/state-accessors.js +118 -0
  139. package/dist/router/virtual-router/engine-legacy.d.ts +123 -0
  140. package/dist/router/virtual-router/engine-legacy.js +194 -0
  141. package/dist/router/virtual-router/engine-logging.d.ts +2 -0
  142. package/dist/router/virtual-router/engine-logging.js +7 -2
  143. package/dist/router/virtual-router/engine-selection/key-parsing.js +0 -3
  144. package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.d.ts +1 -0
  145. package/dist/router/virtual-router/engine-selection/native-chat-request-filter-semantics.js +54 -0
  146. package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.d.ts +10 -0
  147. package/dist/router/virtual-router/engine-selection/native-hub-bridge-policy-semantics.js +67 -0
  148. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.d.ts +30 -0
  149. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-governance-semantics.js +202 -0
  150. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.d.ts +2 -0
  151. package/dist/router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js +83 -0
  152. package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +43 -2
  153. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +75 -0
  154. package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +205 -0
  155. package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.d.ts +3 -0
  156. package/dist/router/virtual-router/engine-selection/native-snapshot-hooks.js +109 -0
  157. package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.d.ts +16 -0
  158. package/dist/router/virtual-router/engine-selection/native-virtual-router-engine-proxy.js +14 -0
  159. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +2 -0
  160. package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +86 -0
  161. package/dist/router/virtual-router/engine-selection/tier-selection-quota-integration.js +100 -0
  162. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +99 -0
  163. package/dist/router/virtual-router/engine.d.ts +22 -105
  164. package/dist/router/virtual-router/engine.js +274 -1641
  165. package/dist/router/virtual-router/load-balancer.d.ts +8 -0
  166. package/dist/router/virtual-router/load-balancer.js +65 -2
  167. package/dist/router/virtual-router/provider-registry.js +2 -0
  168. package/dist/router/virtual-router/routing-instructions/clean.d.ts +3 -0
  169. package/dist/router/virtual-router/routing-instructions/clean.js +34 -0
  170. package/dist/router/virtual-router/routing-instructions/parse.d.ts +18 -0
  171. package/dist/router/virtual-router/routing-instructions/parse.js +377 -0
  172. package/dist/router/virtual-router/routing-instructions/state.d.ts +4 -0
  173. package/dist/router/virtual-router/routing-instructions/state.js +245 -0
  174. package/dist/router/virtual-router/routing-instructions/types.d.ts +70 -0
  175. package/dist/router/virtual-router/routing-instructions/types.js +2 -0
  176. package/dist/router/virtual-router/routing-instructions.d.ts +5 -89
  177. package/dist/router/virtual-router/routing-instructions.js +4 -655
  178. package/dist/router/virtual-router/sticky-session-store.d.ts +4 -0
  179. package/dist/router/virtual-router/sticky-session-store.js +19 -81
  180. package/dist/router/virtual-router/tool-signals.js +21 -3
  181. package/dist/router/virtual-router/types.d.ts +4 -0
  182. package/dist/servertool/clock/session-scope.js +32 -1
  183. package/dist/servertool/engine.js +79 -8
  184. package/dist/servertool/handlers/antigravity-thought-signature-bootstrap.js +1 -1
  185. package/dist/servertool/handlers/clock-auto.js +1 -1
  186. package/dist/servertool/handlers/clock.js +1 -1
  187. package/dist/servertool/handlers/compaction-detect.d.ts +1 -1
  188. package/dist/servertool/handlers/compaction-detect.js +1 -1
  189. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -1
  190. package/dist/servertool/handlers/iflow-model-error-retry.js +1 -1
  191. package/dist/servertool/handlers/recursive-detection-guard.js +1 -1
  192. package/dist/servertool/handlers/review.js +1 -1
  193. package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +1 -1
  194. package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +1 -1
  195. package/dist/servertool/handlers/stop-message-auto.js +1 -1
  196. package/dist/servertool/handlers/vision.js +1 -1
  197. package/dist/servertool/handlers/web-search.js +1 -1
  198. package/dist/servertool/reenter-backend.js +1 -1
  199. package/dist/servertool/server-side-tools.js +2 -2
  200. package/dist/servertool/stop-gateway-context.js +1 -1
  201. package/dist/servertool/stop-message-compare-context.js +1 -1
  202. package/dist/sse/json-to-sse/event-generators/responses.d.ts +4 -0
  203. package/dist/sse/json-to-sse/event-generators/responses.js +95 -1
  204. package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +6 -4
  205. package/dist/sse/sse-to-json/builders/response-builder.d.ts +8 -0
  206. package/dist/sse/sse-to-json/builders/response-builder.js +162 -4
  207. package/dist/sse/sse-to-json/responses-sse-to-json-converter.js +2 -0
  208. package/dist/sse/types/responses-types.d.ts +6 -2
  209. package/dist/tools/apply-patch/structured/coercion.js +5 -0
  210. package/dist/tools/args-json.js +29 -0
  211. package/package.json +8 -5
  212. package/dist/conversion/shared/args-mapping.js +0 -77
  213. package/dist/conversion/shared/compaction-detect.js +0 -4
  214. package/dist/conversion/shared/errors.js +0 -31
  215. package/dist/conversion/shared/media.js +0 -4
  216. package/dist/conversion/shared/payload-budget.js +0 -165
  217. package/dist/conversion/shared/protocol-field-allowlists.d.ts +0 -7
  218. package/dist/conversion/shared/protocol-field-allowlists.js +0 -149
  219. package/dist/conversion/shared/snapshot-hooks.d.ts +0 -11
  220. package/dist/conversion/shared/snapshot-hooks.js +0 -503
  221. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +0 -2
  222. package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +0 -129
  223. package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +0 -4
  224. package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +0 -637
  225. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +0 -21
  226. package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +0 -177
  227. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +0 -5
  228. package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +0 -385
  229. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +0 -10
  230. package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +0 -602
  231. package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +0 -5
  232. package/dist/conversion/shared/text-markup-normalizer/extractors.js +0 -4
  233. package/dist/conversion/shared/tool-canonicalizer.d.ts +0 -2
  234. package/dist/conversion/shared/tool-canonicalizer.js +0 -38
  235. /package/dist/conversion/{shared/args-mapping.d.ts → args-mapping.d.ts} +0 -0
  236. /package/dist/conversion/{shared/bridge-actions.d.ts → bridge-actions.d.ts} +0 -0
  237. /package/dist/conversion/{shared/bridge-id-utils.d.ts → bridge-id-utils.d.ts} +0 -0
  238. /package/dist/conversion/{shared/bridge-instructions.d.ts → bridge-instructions.d.ts} +0 -0
  239. /package/dist/conversion/{shared/bridge-metadata.d.ts → bridge-metadata.d.ts} +0 -0
  240. /package/dist/conversion/{shared/bridge-policies.d.ts → bridge-policies.d.ts} +0 -0
  241. /package/dist/conversion/{shared/jsonish.d.ts → jsonish.d.ts} +0 -0
  242. /package/dist/conversion/{shared/mcp-injection.d.ts → mcp-injection.d.ts} +0 -0
  243. /package/dist/conversion/{shared/media.d.ts → media.d.ts} +0 -0
  244. /package/dist/conversion/{shared/payload-budget.d.ts → payload-budget.d.ts} +0 -0
  245. /package/dist/conversion/{shared → types}/bridge-message-types.d.ts +0 -0
  246. /package/dist/conversion/{shared → types}/bridge-message-types.js +0 -0
@@ -0,0 +1,86 @@
1
+ import { failNativeRequired } from './native-router-hotpath-policy.js';
2
+ import { loadNativeRouterHotpathBindingForInternalUse } from './native-router-hotpath.js';
3
+ function readNativeFunction(name) {
4
+ const binding = loadNativeRouterHotpathBindingForInternalUse();
5
+ const fn = binding?.[name];
6
+ return typeof fn === 'function' ? fn : null;
7
+ }
8
+ function safeStringify(value) {
9
+ try {
10
+ return JSON.stringify(value);
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ function parseArrayPayload(raw) {
17
+ try {
18
+ const parsed = JSON.parse(raw);
19
+ if (!Array.isArray(parsed))
20
+ return null;
21
+ return parsed.filter((entry) => typeof entry === 'string');
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ function parseRecordPayload(raw) {
28
+ try {
29
+ const parsed = JSON.parse(raw);
30
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
31
+ return null;
32
+ }
33
+ return parsed;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ export function parseRoutingInstructionKindsWithNative(request) {
40
+ const capability = 'parseRoutingInstructionKindsJson';
41
+ const fail = (reason) => failNativeRequired(capability, reason);
42
+ const fn = readNativeFunction(capability);
43
+ if (!fn) {
44
+ return fail();
45
+ }
46
+ const requestJson = safeStringify(request);
47
+ if (!requestJson) {
48
+ return fail('json stringify failed');
49
+ }
50
+ try {
51
+ const result = fn(requestJson);
52
+ if (typeof result !== 'string' || !result) {
53
+ return fail('empty result');
54
+ }
55
+ const parsed = parseArrayPayload(result);
56
+ return parsed ?? fail('invalid payload');
57
+ }
58
+ catch (error) {
59
+ const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
60
+ return fail(reason);
61
+ }
62
+ }
63
+ export function cleanRoutingInstructionMarkersWithNative(request) {
64
+ const capability = 'cleanRoutingInstructionMarkersJson';
65
+ const fail = (reason) => failNativeRequired(capability, reason);
66
+ const fn = readNativeFunction(capability);
67
+ if (!fn) {
68
+ return fail();
69
+ }
70
+ const requestJson = safeStringify(request);
71
+ if (!requestJson) {
72
+ return fail('json stringify failed');
73
+ }
74
+ try {
75
+ const result = fn(requestJson);
76
+ if (typeof result !== 'string' || !result) {
77
+ return fail('empty result');
78
+ }
79
+ const parsed = parseRecordPayload(result);
80
+ return parsed ?? fail('invalid payload');
81
+ }
82
+ catch (error) {
83
+ const reason = error instanceof Error ? error.message : String(error ?? 'unknown');
84
+ return fail(reason);
85
+ }
86
+ }
@@ -3,6 +3,72 @@ import { computeHealthWeight } from '../health-weighted.js';
3
3
  import { buildQuotaBuckets } from './native-router-hotpath.js';
4
4
  import { computeContextWeightMultipliers } from './context-weight-multipliers.js';
5
5
  import { pickPriorityGroup } from './tier-priority.js';
6
+ import { extractProviderId, getProviderModelId } from './key-parsing.js';
7
+ function buildPrimaryTargetGroups(candidates, deps) {
8
+ const groups = new Map();
9
+ for (const key of candidates) {
10
+ const providerId = extractProviderId(key) ?? '';
11
+ if (!providerId) {
12
+ continue;
13
+ }
14
+ let modelId;
15
+ try {
16
+ modelId = getProviderModelId(key, deps.providerRegistry) ?? undefined;
17
+ }
18
+ catch {
19
+ modelId = undefined;
20
+ }
21
+ const groupId = modelId ? `${providerId}.${modelId}` : providerId;
22
+ const entry = groups.get(groupId) ?? [];
23
+ entry.push(key);
24
+ groups.set(groupId, entry);
25
+ }
26
+ return groups;
27
+ }
28
+ function resolveGroupWeight(groupId, weights) {
29
+ if (!weights) {
30
+ return 1;
31
+ }
32
+ const direct = weights[groupId];
33
+ if (typeof direct === 'number' && Number.isFinite(direct) && direct > 0) {
34
+ return direct;
35
+ }
36
+ const providerId = groupId.split('.')[0] ?? groupId;
37
+ const providerOnly = weights[providerId];
38
+ if (typeof providerOnly === 'number' && Number.isFinite(providerOnly) && providerOnly > 0) {
39
+ return providerOnly;
40
+ }
41
+ return 1;
42
+ }
43
+ function buildGroupWeights(groups, weights) {
44
+ if (!groups.size) {
45
+ return undefined;
46
+ }
47
+ const out = {};
48
+ for (const [groupId] of groups.entries()) {
49
+ out[groupId] = resolveGroupWeight(groupId, weights);
50
+ }
51
+ return out;
52
+ }
53
+ function hasNonUniformWeights(candidates, weights) {
54
+ if (!weights || candidates.length < 2) {
55
+ return false;
56
+ }
57
+ let ref;
58
+ for (const key of candidates) {
59
+ const raw = weights[key];
60
+ if (typeof raw !== 'number' || !Number.isFinite(raw)) {
61
+ continue;
62
+ }
63
+ if (ref === undefined) {
64
+ ref = raw;
65
+ }
66
+ else if (Math.abs(raw - ref) > 1e-6) {
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
6
72
  export function selectProviderKeyWithQuotaBuckets(opts) {
7
73
  const { routeName, tier, stickyKey, candidates, isSafePool, deps, options, contextResult, warnRatio, isRecoveryAttempt, now, nowForWeights, healthWeightedCfg, contextWeightedCfg, quotaView, isAvailable, selectFirstAvailable, applyAliasStickyQueuePinning, preferAntigravityAliasesOnRetry } = opts;
8
74
  const bucketInputs = candidates.map((key, order) => {
@@ -78,6 +144,23 @@ export function selectProviderKeyWithQuotaBuckets(opts) {
78
144
  for (const key of group.groupCandidates) {
79
145
  groupWeights[key] = bucketWeights[key] ?? 1;
80
146
  }
147
+ const allowGrouped = !hasNonUniformWeights(group.groupCandidates, bucketWeights);
148
+ if (allowGrouped && deps.loadBalancer.getPolicy().strategy !== 'sticky') {
149
+ const groups = buildPrimaryTargetGroups(group.groupCandidates, deps);
150
+ if (groups.size > 0) {
151
+ const groupWeightMap = buildGroupWeights(groups, deps.loadBalancer.getPolicy().weights);
152
+ const selected = deps.loadBalancer.selectGrouped({
153
+ routeName: `${routeName}:${tier.id}:priority:${priority}:group:${group.groupId}`,
154
+ groups,
155
+ stickyKey: options.allowAliasRotation ? undefined : stickyKey,
156
+ weights: groupWeightMap,
157
+ availabilityCheck: isAvailable
158
+ }, 'round-robin');
159
+ if (selected) {
160
+ return selected;
161
+ }
162
+ }
163
+ }
81
164
  const selected = deps.loadBalancer.select({
82
165
  routeName: `${routeName}:${tier.id}:priority:${priority}:group:${group.groupId}`,
83
166
  candidates: group.groupCandidates,
@@ -101,6 +184,23 @@ export function selectProviderKeyWithQuotaBuckets(opts) {
101
184
  return recovered;
102
185
  continue;
103
186
  }
187
+ const allowGrouped = !hasNonUniformWeights(bucketCandidates, bucketWeights);
188
+ if (allowGrouped && deps.loadBalancer.getPolicy().strategy !== 'sticky') {
189
+ const groups = buildPrimaryTargetGroups(bucketCandidates, deps);
190
+ if (groups.size > 0) {
191
+ const groupWeightMap = buildGroupWeights(groups, deps.loadBalancer.getPolicy().weights);
192
+ const selected = deps.loadBalancer.selectGrouped({
193
+ routeName: `${routeName}:${tier.id}:${priority}`,
194
+ groups,
195
+ stickyKey: options.allowAliasRotation ? undefined : stickyKey,
196
+ weights: groupWeightMap,
197
+ availabilityCheck: isAvailable
198
+ }, tier.mode === 'round-robin' ? 'round-robin' : undefined);
199
+ if (selected) {
200
+ return selected;
201
+ }
202
+ }
203
+ }
104
204
  const selected = deps.loadBalancer.select({
105
205
  routeName: `${routeName}:${tier.id}`,
106
206
  candidates: bucketCandidates,
@@ -4,6 +4,71 @@ import { computeContextWeightMultipliers } from './context-weight-multipliers.js
4
4
  import { extractKeyAlias, extractProviderId, getProviderModelId } from './key-parsing.js';
5
5
  import { pickPriorityGroup } from './tier-priority.js';
6
6
  import { selectProviderKeyWithQuotaBuckets } from './tier-selection-quota-integration.js';
7
+ function buildPrimaryTargetGroups(candidates, deps) {
8
+ const groups = new Map();
9
+ for (const key of candidates) {
10
+ const providerId = extractProviderId(key) ?? '';
11
+ if (!providerId) {
12
+ continue;
13
+ }
14
+ let modelId;
15
+ try {
16
+ modelId = getProviderModelId(key, deps.providerRegistry) ?? undefined;
17
+ }
18
+ catch {
19
+ modelId = undefined;
20
+ }
21
+ const groupId = modelId ? `${providerId}.${modelId}` : providerId;
22
+ const entry = groups.get(groupId) ?? [];
23
+ entry.push(key);
24
+ groups.set(groupId, entry);
25
+ }
26
+ return groups;
27
+ }
28
+ function resolveGroupWeight(groupId, weights) {
29
+ if (!weights) {
30
+ return 1;
31
+ }
32
+ const direct = weights[groupId];
33
+ if (typeof direct === 'number' && Number.isFinite(direct) && direct > 0) {
34
+ return direct;
35
+ }
36
+ const providerId = groupId.split('.')[0] ?? groupId;
37
+ const providerOnly = weights[providerId];
38
+ if (typeof providerOnly === 'number' && Number.isFinite(providerOnly) && providerOnly > 0) {
39
+ return providerOnly;
40
+ }
41
+ return 1;
42
+ }
43
+ function buildGroupWeights(groups, weights) {
44
+ if (!groups.size) {
45
+ return undefined;
46
+ }
47
+ const out = {};
48
+ for (const [groupId] of groups.entries()) {
49
+ out[groupId] = resolveGroupWeight(groupId, weights);
50
+ }
51
+ return out;
52
+ }
53
+ function hasNonUniformWeights(candidates, weights) {
54
+ if (!weights || candidates.length < 2) {
55
+ return false;
56
+ }
57
+ let ref;
58
+ for (const key of candidates) {
59
+ const raw = weights[key];
60
+ if (typeof raw !== 'number' || !Number.isFinite(raw)) {
61
+ continue;
62
+ }
63
+ if (ref === undefined) {
64
+ ref = raw;
65
+ }
66
+ else if (Math.abs(raw - ref) > 1e-6) {
67
+ return true;
68
+ }
69
+ }
70
+ return false;
71
+ }
7
72
  function applyAliasStickyQueuePinning(opts) {
8
73
  const { candidates, orderedTargets, deps, excludedKeys } = opts;
9
74
  if (!Array.isArray(candidates) || candidates.length < 2) {
@@ -217,6 +282,23 @@ export function selectProviderKeyFromCandidatePool(opts) {
217
282
  }
218
283
  return out;
219
284
  })();
285
+ const allowGrouped = !hasNonUniformWeights(group.groupCandidates, weights);
286
+ if (allowGrouped && deps.loadBalancer.getPolicy().strategy !== 'sticky') {
287
+ const groups = buildPrimaryTargetGroups(group.groupCandidates, deps);
288
+ if (groups.size > 0) {
289
+ const groupWeights = buildGroupWeights(groups, deps.loadBalancer.getPolicy().weights);
290
+ const selected = deps.loadBalancer.selectGrouped({
291
+ routeName: `${routeName}:${tier.id}:priority:group:${group.groupId}`,
292
+ groups,
293
+ stickyKey: options.allowAliasRotation ? undefined : stickyKey,
294
+ weights: groupWeights,
295
+ availabilityCheck: isAvailable
296
+ }, 'round-robin');
297
+ if (selected) {
298
+ return selected;
299
+ }
300
+ }
301
+ }
220
302
  return deps.loadBalancer.select({
221
303
  routeName: `${routeName}:${tier.id}:priority:group:${group.groupId}`,
222
304
  candidates: group.groupCandidates,
@@ -242,6 +324,23 @@ export function selectProviderKeyFromCandidatePool(opts) {
242
324
  }
243
325
  return out;
244
326
  })();
327
+ const allowGrouped = !hasNonUniformWeights(pinnedCandidates, weights);
328
+ if (allowGrouped && deps.loadBalancer.getPolicy().strategy !== 'sticky') {
329
+ const groups = buildPrimaryTargetGroups(pinnedCandidates, deps);
330
+ if (groups.size > 0) {
331
+ const groupWeights = buildGroupWeights(groups, deps.loadBalancer.getPolicy().weights);
332
+ const selected = deps.loadBalancer.selectGrouped({
333
+ routeName: `${routeName}:${tier.id}`,
334
+ groups,
335
+ stickyKey: options.allowAliasRotation ? undefined : stickyKey,
336
+ weights: groupWeights,
337
+ availabilityCheck: isAvailable
338
+ }, tier.mode === 'round-robin' ? 'round-robin' : undefined);
339
+ if (selected) {
340
+ return selected;
341
+ }
342
+ }
343
+ }
245
344
  return deps.loadBalancer.select({
246
345
  routeName: `${routeName}:${tier.id}`,
247
346
  candidates: pinnedCandidates,
@@ -1,51 +1,34 @@
1
- import { type RoutingDecision, type RoutingDiagnostics, type StopMessageStateSnapshot, type PreCommandStateSnapshot, type RouterMetadataInput, type VirtualRouterConfig, type TargetMetadata, type ProviderFailureEvent, type ProviderErrorEvent, type ProviderSuccessEvent, type VirtualRouterHealthStore } from './types.js';
2
1
  import type { ProcessedRequest, StandardizedRequest } from '../../conversion/hub/types/standardized.js';
3
- import { type RoutingInstructionState } from './routing-instructions.js';
4
- import type { ProviderQuotaView } from './types.js';
5
- interface RoutingInstructionStateStore {
6
- loadSync(key: string): RoutingInstructionState | null;
7
- saveAsync(key: string, state: RoutingInstructionState | null): void;
8
- saveSync?: (key: string, state: RoutingInstructionState | null) => void;
9
- }
2
+ import type { ProviderErrorEvent, ProviderFailureEvent, ProviderQuotaView, ProviderSuccessEvent, RouterMetadataInput, RoutingDecision, RoutingDiagnostics, RoutingStatusSnapshot, StopMessageStateSnapshot, PreCommandStateSnapshot, TargetMetadata, VirtualRouterConfig, VirtualRouterHealthStore } from './types.js';
10
3
  export declare class VirtualRouterEngine {
11
- private routing;
12
- private readonly providerRegistry;
13
- private readonly healthManager;
14
- private get providerCooldowns();
15
- private loadBalancer;
16
- private classifier;
17
- private readonly contextAdvisor;
18
- private contextRouting;
19
- private readonly routeAnalytics;
20
- private stickySessionManager;
21
- private cooldownManager;
22
- private antigravityLeasePersistence;
23
- private readonly debug;
24
- private healthConfig;
25
- private readonly statsCenter;
26
- private webSearchForce;
27
- private healthStore?;
28
- private routingStateStore;
29
- private routingInstructionState;
30
- private quotaView?;
31
- /**
32
- * Backward-compatible test/debug surface used by existing regression scripts.
33
- * Keep this as a read-only view over StickySessionManager storage.
34
- */
35
- get antigravitySessionAliasStore(): Map<string, string>;
4
+ private readonly nativeProxy;
5
+ private readonly registry;
6
+ private readonly routingInstructionStateStore;
36
7
  constructor(deps?: {
37
8
  healthStore?: VirtualRouterHealthStore;
38
- routingStateStore?: RoutingInstructionStateStore;
9
+ routingStateStore?: {
10
+ loadSync: (key: string) => unknown;
11
+ saveAsync: (key: string, state: unknown) => void;
12
+ saveSync?: (key: string, state: unknown) => void;
13
+ };
39
14
  quotaView?: ProviderQuotaView;
40
15
  });
16
+ get antigravitySessionAliasStore(): Map<string, string>;
17
+ get routingInstructionState(): Map<string, unknown>;
18
+ get providerRegistry(): unknown;
19
+ initialize(config: VirtualRouterConfig): void;
41
20
  updateDeps(deps: {
42
21
  healthStore?: VirtualRouterHealthStore | null;
43
- routingStateStore?: RoutingInstructionStateStore | null;
22
+ routingStateStore?: {
23
+ loadSync: (key: string) => unknown;
24
+ saveAsync: (key: string, state: unknown) => void;
25
+ saveSync?: (key: string, state: unknown) => void;
26
+ } | null;
44
27
  quotaView?: ProviderQuotaView | null;
45
28
  }): void;
46
- private parseDirectProviderModel;
47
- private shouldFallbackDirectModelForMedia;
48
- initialize(config: VirtualRouterConfig): void;
29
+ updateVirtualRouterConfig(config: VirtualRouterConfig): void;
30
+ markProviderCooldown(providerKey: string, cooldownMs: number | undefined): void;
31
+ clearProviderCooldown(providerKey: string): void;
49
32
  route(request: StandardizedRequest | ProcessedRequest, metadata: RouterMetadataInput): {
50
33
  target: TargetMetadata;
51
34
  decision: RoutingDecision;
@@ -56,71 +39,5 @@ export declare class VirtualRouterEngine {
56
39
  handleProviderFailure(event: ProviderFailureEvent): void;
57
40
  handleProviderError(event: ProviderErrorEvent): void;
58
41
  handleProviderSuccess(event: ProviderSuccessEvent): void;
59
- getStatus(): {
60
- routes: Record<string, {
61
- providers: string[];
62
- hits: number;
63
- lastUsedProvider?: string;
64
- lastHit?: {
65
- timestampMs: number;
66
- reason?: string;
67
- requestTokens?: number;
68
- selectionPenalty?: number;
69
- stopMessageActive: boolean;
70
- stopMessageMode?: "on" | "off" | "auto";
71
- stopMessageRemaining?: number;
72
- };
73
- }>;
74
- health: import("./types.js").ProviderHealthState[];
75
- };
76
- /**
77
- * 将分类器产生的逻辑路由名直接归一化为配置中的路由键。
78
- * 不再维护 "websearch" 之类的别名,调用方应显式使用 "web_search" 或 "search" 等实际路由名。
79
- */
80
- private normalizeRouteAlias;
81
- private validateConfig;
82
- private selectProvider;
83
- private resolveSelectionPenalty;
84
- private providerHealthConfig;
85
- private resolveStickyKey;
86
- private resolveSessionScope;
87
- private resolveInstructionProcessModeForSelection;
88
- private resolveInstructionTarget;
89
- private filterCandidatesByRoutingState;
90
- private selectFromCandidates;
91
- /**
92
- * 在已有候选路由集合上,筛选出真正挂载了 sticky 池内 providerKey 的路由,
93
- * 并按 ROUTE_PRIORITY 进行排序;同时显式排除 tools 路由,保证一旦进入
94
- * sticky 模式,就不会再命中独立的 tools 池(例如 glm/qwen 工具模型)。
95
- * 若候选集合中完全没有挂载 sticky key 的路由,则尝试在 default 路由上兜底。
96
- */
97
- private buildStickyRouteCandidatesFromFiltered;
98
- /**
99
- * 在 sticky 模式下,仅在 sticky 池内选择 Provider:
100
- * - stickyKeySet 表示已经解析并通过健康检查的 providerKey 集合;
101
- * - 不再依赖 routing[*].targets 中是否挂载这些 key,避免「未初始化路由池」导致 sticky 池为空;
102
- * - 仍然尊重 allowed/disabledProviders、disabledKeys、disabledModels 以及上下文长度。
103
- */
104
- private selectFromStickyPool;
105
- private extractExcludedProviderKeySet;
106
- private buildRouteCandidates;
107
- private reorderForInlineVision;
108
- private reorderForPreferredModel;
109
- private routeSupportsModel;
110
- private routeSupportsInlineVision;
111
- private sortByPriority;
112
- private routeWeight;
113
- private routeHasForceFlag;
114
- private routeHasTargets;
115
- private hasPrimaryPool;
116
- private sortRoutePools;
117
- private flattenPoolTargets;
118
- private markProviderCooldown;
119
- private clearProviderCooldown;
120
- private isProviderCoolingDown;
121
- private getProviderCooldownRemainingMs;
122
- private restoreHealthFromStore;
123
- private buildHealthSnapshot;
124
- private persistHealthSnapshot;
42
+ getStatus(): RoutingStatusSnapshot;
125
43
  }
126
- export {};