@jsonstudio/llms 0.6.1399 → 0.6.1403

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 (72) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.d.ts +1 -3
  2. package/dist/conversion/codecs/gemini-openai-codec.js +4 -10
  3. package/dist/conversion/compat/actions/gemini-cli-request.d.ts +2 -0
  4. package/dist/conversion/compat/actions/gemini-cli-request.js +490 -0
  5. package/dist/conversion/compat/profiles/chat-gemini-cli.json +27 -0
  6. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +76 -348
  7. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  8. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +2 -0
  9. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +95 -3
  10. package/dist/conversion/hub/pipeline/hub-pipeline.js +1365 -19
  11. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +22 -0
  12. package/dist/conversion/hub/policy/policy-engine.js +50 -3
  13. package/dist/conversion/hub/process/chat-process.js +5 -146
  14. package/dist/conversion/hub/response/provider-response.js +11 -10
  15. package/dist/conversion/hub/response/response-mappers.d.ts +1 -3
  16. package/dist/conversion/hub/response/response-mappers.js +2 -20
  17. package/dist/conversion/hub/tool-surface/tool-surface-engine.js +2 -1
  18. package/dist/conversion/responses/responses-openai-bridge.js +4 -3
  19. package/dist/conversion/shared/gemini-tool-utils.d.ts +1 -6
  20. package/dist/conversion/shared/gemini-tool-utils.js +164 -542
  21. package/dist/conversion/shared/mcp-injection.js +29 -0
  22. package/dist/conversion/shared/openai-message-normalize.js +3 -17
  23. package/dist/filters/special/request-tool-list-filter.js +21 -13
  24. package/dist/filters/special/tool-filter-hooks.js +60 -3
  25. package/dist/router/virtual-router/bootstrap.js +8 -6
  26. package/dist/router/virtual-router/engine-health.d.ts +1 -1
  27. package/dist/router/virtual-router/engine-health.js +110 -11
  28. package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +0 -15
  29. package/dist/router/virtual-router/engine-selection/alias-selection.js +4 -85
  30. package/dist/router/virtual-router/engine-selection/route-utils.js +6 -12
  31. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +17 -40
  32. package/dist/router/virtual-router/engine-selection/tier-selection.js +2 -5
  33. package/dist/router/virtual-router/engine.js +6 -21
  34. package/dist/router/virtual-router/types.d.ts +1 -14
  35. package/dist/servertool/clock/config.d.ts +1 -1
  36. package/dist/servertool/clock/config.js +5 -9
  37. package/dist/servertool/engine.js +11 -88
  38. package/dist/servertool/handlers/gemini-empty-reply-continue.js +1 -2
  39. package/dist/sse/sse-to-json/builders/response-builder.js +0 -16
  40. package/package.json +1 -1
  41. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +0 -10
  42. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +0 -142
  43. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +0 -6
  44. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +0 -79
  45. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +0 -3
  46. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +0 -46
  47. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +0 -8
  48. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +0 -366
  49. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +0 -9
  50. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +0 -390
  51. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +0 -3
  52. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +0 -14
  53. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +0 -2
  54. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +0 -144
  55. package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +0 -4
  56. package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +0 -32
  57. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +0 -8
  58. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +0 -63
  59. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +0 -2
  60. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +0 -43
  61. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +0 -1
  62. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +0 -29
  63. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +0 -2
  64. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +0 -16
  65. package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +0 -116
  66. package/dist/conversion/hub/pipeline/hub-pipeline/types.js +0 -1
  67. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.d.ts +0 -10
  68. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_thought_signature_inject/index.js +0 -172
  69. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.d.ts +0 -10
  70. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_thought_signature_capture/index.js +0 -71
  71. package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.d.ts +0 -14
  72. package/dist/conversion/hub/pipeline/thought-signature/thought-signature-center.js +0 -289
@@ -123,7 +123,7 @@ export function trySelectFromTier(routeName, tier, stickyKey, estimatedTokens, f
123
123
  return { providerKey: null, poolTargets: [], tierId: tier.id, failureHint: `${routeName}:${tier.id}:empty` };
124
124
  }
125
125
  const contextResult = deps.contextAdvisor.classify(targets, estimatedTokens, (key) => deps.providerRegistry.get(key));
126
- const prioritizedPools = buildContextCandidatePools(contextResult, routeName);
126
+ const prioritizedPools = buildContextCandidatePools(contextResult);
127
127
  const quotaView = deps.quotaView;
128
128
  const now = quotaView ? Date.now() : 0;
129
129
  const healthWeightedCfg = resolveHealthWeightedConfig(deps.loadBalancer.getPolicy().healthWeighted);
@@ -200,7 +200,7 @@ function recordAliasQueueFailuresFromExcludedKeys(excludedKeys, orderedTargets,
200
200
  }
201
201
  }
202
202
  }
203
- function buildContextCandidatePools(result, routeName) {
203
+ function buildContextCandidatePools(result) {
204
204
  const ordered = [];
205
205
  if (result.safe.length) {
206
206
  ordered.push(result.safe);
@@ -208,9 +208,6 @@ function buildContextCandidatePools(result, routeName) {
208
208
  if (result.risky.length) {
209
209
  ordered.push(result.risky);
210
210
  }
211
- if (routeName === 'longcontext' && result.overflow.length) {
212
- ordered.push(result.overflow);
213
- }
214
211
  return ordered;
215
212
  }
216
213
  function describeAttempt(routeName, poolId, result) {
@@ -10,9 +10,8 @@ import { parseRoutingInstructions, applyRoutingInstructions, cleanMessagesFromRo
10
10
  import { loadRoutingInstructionStateSync, saveRoutingInstructionStateAsync, saveRoutingInstructionStateSync } from './sticky-session-store.js';
11
11
  import { buildHitReason, formatVirtualRouterHit } from './engine-logging.js';
12
12
  import { selectDirectProviderModel, selectFromStickyPool, selectProviderImpl } from './engine-selection.js';
13
- import { applyQuotaDepletedImpl, applyQuotaRecoveryImpl, handleProviderFailureImpl, mapProviderErrorImpl } from './engine-health.js';
13
+ import { applyQuotaDepletedImpl, applyQuotaRecoveryImpl, applySeriesCooldownImpl, handleProviderFailureImpl, mapProviderErrorImpl } from './engine-health.js';
14
14
  import { mergeStopMessageFromPersisted } from './stop-message-state-sync.js';
15
- const ANTIGRAVITY_COOLDOWN_ALIAS_THRESHOLD_MS = 30_000;
16
15
  export class VirtualRouterEngine {
17
16
  routing = {};
18
17
  providerRegistry = new ProviderRegistry();
@@ -624,18 +623,10 @@ export class VirtualRouterEngine {
624
623
  // ignore persistence errors
625
624
  }
626
625
  }
627
- // When Host injects quotaView, pool decisions primarily follow quota;
628
- // however explicit host-provided health signals (quota recovery/depleted/series cooldown)
629
- // must still be applied so retry selection can avoid obviously blocked runtimes.
626
+ // Host 注入 quotaView 时,VirtualRouter 的入池/优先级决策应以 quota 为准;
627
+ // 此时不再在 engine-health 内部进行 429/backoff/series cooldown 等健康决策,
628
+ // 以避免与 daemon/quota-center 的长期熔断策略重复维护并导致日志噪声。
630
629
  if (this.quotaView) {
631
- const handledByQuota = applyQuotaRecoveryImpl(event, this.healthManager, (key) => this.clearProviderCooldown(key), this.debug);
632
- if (handledByQuota) {
633
- return;
634
- }
635
- const handledByQuotaDepleted = applyQuotaDepletedImpl(event, this.healthManager, (key, ttl) => this.markProviderCooldown(key, ttl), this.debug);
636
- if (handledByQuotaDepleted) {
637
- return;
638
- }
639
630
  return;
640
631
  }
641
632
  // 配额恢复事件优先处理:一旦识别到 virtualRouterQuotaRecovery,
@@ -648,6 +639,7 @@ export class VirtualRouterEngine {
648
639
  if (handledByQuotaDepleted) {
649
640
  return;
650
641
  }
642
+ applySeriesCooldownImpl(event, this.providerRegistry, this.healthManager, (key, ttl) => this.markProviderCooldown(key, ttl), this.debug);
651
643
  const derived = mapProviderErrorImpl(event, this.providerHealthConfig());
652
644
  if (!derived) {
653
645
  return;
@@ -1469,17 +1461,10 @@ export class VirtualRouterEngine {
1469
1461
  if (!expiry) {
1470
1462
  return false;
1471
1463
  }
1472
- const now = Date.now();
1473
- if (now >= expiry) {
1464
+ if (Date.now() >= expiry) {
1474
1465
  this.providerCooldowns.delete(providerKey);
1475
1466
  return false;
1476
1467
  }
1477
- if (providerKey.startsWith('antigravity.')) {
1478
- const remaining = expiry - now;
1479
- if (remaining < ANTIGRAVITY_COOLDOWN_ALIAS_THRESHOLD_MS) {
1480
- return false;
1481
- }
1482
- }
1483
1468
  return true;
1484
1469
  }
1485
1470
  restoreHealthFromStore() {
@@ -152,7 +152,7 @@ export interface HealthWeightedLoadBalancingConfig {
152
152
  */
153
153
  recoverToBestOnRetry?: boolean;
154
154
  }
155
- export type AliasSelectionStrategy = 'none' | 'sticky-queue' | 'best-quota';
155
+ export type AliasSelectionStrategy = 'none' | 'sticky-queue';
156
156
  export interface AliasSelectionConfig {
157
157
  /**
158
158
  * Global on/off switch. When false, no alias-level selection is applied.
@@ -514,19 +514,6 @@ export interface ProviderQuotaViewEntry {
514
514
  inPool: boolean;
515
515
  reason?: string;
516
516
  priorityTier?: number;
517
- /**
518
- * Optional remaining quota fraction for the provider key (0..1).
519
- * Used by alias-selection strategies that prefer higher remaining quota.
520
- */
521
- remainingFraction?: number | null;
522
- /**
523
- * Optional quota reset timestamp (ms since epoch) for the provider key.
524
- */
525
- quotaResetAtMs?: number | null;
526
- /**
527
- * Optional quota fetch timestamp (ms since epoch) for the provider key.
528
- */
529
- quotaFetchedAtMs?: number | null;
530
517
  /**
531
518
  * Optional soft penalty hint for selection ordering.
532
519
  * - 0 / undefined means no penalty
@@ -9,7 +9,7 @@ export declare function normalizeClockConfig(raw: unknown): ClockConfigSnapshot
9
9
  * Resolve the effective clock config for a request/session.
10
10
  *
11
11
  * - If a config object exists and enabled=true -> return normalized config.
12
+ * - If the config is absent (undefined) -> default-enable using CLOCK_CONFIG_DEFAULTS.
12
13
  * - If the config is explicitly present but disabled/invalid -> return null.
13
- * - If the config is absent (undefined) -> return null (opt-in only).
14
14
  */
15
15
  export declare function resolveClockConfig(raw: unknown): ClockConfigSnapshot | null;
@@ -3,11 +3,6 @@ export const CLOCK_CONFIG_DEFAULTS = {
3
3
  dueWindowMs: 60_000,
4
4
  tickMs: 60_000
5
5
  };
6
- function isClockDisabledByEnv() {
7
- const raw = process.env.ROUTECODEX_DISABLE_CLOCK ?? process.env.LLMSWITCH_DISABLE_CLOCK ?? '';
8
- const v = String(raw).trim().toLowerCase();
9
- return v === '1' || v === 'true' || v === 'yes' || v === 'on';
10
- }
11
6
  export function normalizeClockConfig(raw) {
12
7
  if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
13
8
  return null;
@@ -34,16 +29,17 @@ export function normalizeClockConfig(raw) {
34
29
  * Resolve the effective clock config for a request/session.
35
30
  *
36
31
  * - If a config object exists and enabled=true -> return normalized config.
32
+ * - If the config is absent (undefined) -> default-enable using CLOCK_CONFIG_DEFAULTS.
37
33
  * - If the config is explicitly present but disabled/invalid -> return null.
38
- * - If the config is absent (undefined) -> return null (opt-in only).
39
34
  */
40
35
  export function resolveClockConfig(raw) {
41
- if (isClockDisabledByEnv()) {
42
- return null;
43
- }
44
36
  const normalized = normalizeClockConfig(raw);
45
37
  if (normalized) {
46
38
  return normalized;
47
39
  }
40
+ // Important: only default-enable when the config is absent, not when it's explicitly disabled.
41
+ if (raw === undefined) {
42
+ return normalizeClockConfig({ enabled: true });
43
+ }
48
44
  return null;
49
45
  }
@@ -8,32 +8,6 @@ import { applyHubFollowupPolicyShadow } from './followup-shadow.js';
8
8
  import { buildServerToolFollowupChatPayloadFromInjection } from './handlers/followup-request-builder.js';
9
9
  import { findNextUndeliveredDueAtMs, listClockTasks, resolveClockConfig } from './clock/task-store.js';
10
10
  import { savePendingServerToolInjection } from './pending-session.js';
11
- function stripToolHistoryFromFollowupMessages(raw) {
12
- const messages = Array.isArray(raw) ? raw : null;
13
- if (!messages) {
14
- return raw;
15
- }
16
- const out = [];
17
- for (const msg of messages) {
18
- if (!msg || typeof msg !== 'object' || Array.isArray(msg)) {
19
- out.push(msg);
20
- continue;
21
- }
22
- const record = msg;
23
- const role = typeof record.role === 'string' ? record.role.trim().toLowerCase() : '';
24
- // Drop tool-role messages entirely for a "no-tools" recovery followup.
25
- if (role === 'tool') {
26
- continue;
27
- }
28
- // Remove OpenAI tool call fields that could trigger Gemini strict validation.
29
- const cloned = { ...record };
30
- delete cloned.tool_calls;
31
- delete cloned.tool_call_id;
32
- delete cloned.name;
33
- out.push(cloned);
34
- }
35
- return out;
36
- }
37
11
  function parseTimeoutMs(raw, fallback) {
38
12
  const n = typeof raw === 'string' ? Number(raw.trim()) : typeof raw === 'number' ? raw : NaN;
39
13
  if (!Number.isFinite(n) || n <= 0) {
@@ -93,8 +67,10 @@ function coerceFollowupPayloadStream(payload, stream) {
93
67
  if (!payload || typeof payload !== 'object') {
94
68
  return payload;
95
69
  }
96
- if (typeof stream === 'boolean') {
97
- payload.stream = stream;
70
+ // ServerTool followup requests must be non-streaming to keep parsing deterministic and avoid
71
+ // provider-side SSE wrappers leaking into internal reenter calls.
72
+ if (stream === false) {
73
+ payload.stream = false;
98
74
  }
99
75
  return payload;
100
76
  }
@@ -378,41 +354,7 @@ export async function runServerToolOrchestration(options) {
378
354
  }
379
355
  return null;
380
356
  })();
381
- // Prevent nested followup execution on serverToolFollowup hops.
382
- // Followup responses should still be eligible for servertool triggers (e.g. clock/web_search parsing),
383
- // but they must not start a new followup flow inside an existing followup hop.
384
- //
385
- // Exception: allow continuing the same flow when serverToolLoopState.flowId matches.
386
- const followupSeedPayload = (() => {
387
- if (!followupPayloadRaw) {
388
- return null;
389
- }
390
- try {
391
- const rt = readRuntimeMetadata(options.adapterContext);
392
- const followupFlagRaw = rt?.serverToolFollowup;
393
- const isFollowup = followupFlagRaw === true ||
394
- (typeof followupFlagRaw === 'string' && followupFlagRaw.trim().toLowerCase() === 'true');
395
- if (!isFollowup) {
396
- return followupPayloadRaw;
397
- }
398
- const loopState = rt?.serverToolLoopState;
399
- const loopFlowId = loopState && typeof loopState === 'object' && !Array.isArray(loopState)
400
- ? String(loopState.flowId || '').trim()
401
- : '';
402
- const flowId = typeof engineResult.execution?.flowId === 'string' && engineResult.execution.flowId.trim().length
403
- ? engineResult.execution.flowId.trim()
404
- : '';
405
- if (loopFlowId && flowId && loopFlowId === flowId) {
406
- return followupPayloadRaw;
407
- }
408
- return null;
409
- }
410
- catch {
411
- // best-effort: if metadata is malformed, avoid nested followups
412
- return null;
413
- }
414
- })();
415
- if (!followupSeedPayload) {
357
+ if (!followupPayloadRaw) {
416
358
  logProgress(5, totalSteps, 'completed (missing followup payload)', { flowId });
417
359
  return {
418
360
  chat: engineResult.finalChatResponse,
@@ -420,7 +362,7 @@ export async function runServerToolOrchestration(options) {
420
362
  flowId: engineResult.execution.flowId
421
363
  };
422
364
  }
423
- const loopState = buildServerToolLoopState(options.adapterContext, engineResult.execution.flowId, followupSeedPayload);
365
+ const loopState = buildServerToolLoopState(options.adapterContext, engineResult.execution.flowId, followupPayloadRaw);
424
366
  if (applyAutoLimit && loopState && typeof loopState.repeatCount === 'number' && loopState.repeatCount >= 3) {
425
367
  logProgress(5, totalSteps, 'completed (auto limit hit)', { flowId });
426
368
  return {
@@ -438,6 +380,7 @@ export async function runServerToolOrchestration(options) {
438
380
  };
439
381
  }
440
382
  const metadata = {
383
+ stream: false,
441
384
  ...(engineResult.execution.followup.metadata ?? {})
442
385
  };
443
386
  const rt = ensureRuntimeMetadata(metadata);
@@ -460,10 +403,9 @@ export async function runServerToolOrchestration(options) {
460
403
  (typeof options.entryEndpoint === 'string' && options.entryEndpoint.trim().length
461
404
  ? options.entryEndpoint
462
405
  : followupEntryEndpoint);
463
- // For stateful auto-followups, keep the same providerKey/alias.
464
- // Otherwise the followup requestId suffix could cause round-robin alias switching or
465
- // route re-evaluation (e.g. "continue" prompt being treated as a new intent).
466
- if (isStopMessageFlow || isGeminiEmptyReplyContinue) {
406
+ // For stateful auto-followups (e.g. stop_message_flow), keep the same providerKey/alias.
407
+ // Otherwise the followup requestId suffix would cause round-robin alias switching.
408
+ if (isStopMessageFlow) {
467
409
  const providerKeyRaw = options.adapterContext.providerKey;
468
410
  const providerKey = typeof providerKeyRaw === 'string' && providerKeyRaw.trim().length ? providerKeyRaw.trim() : '';
469
411
  if (providerKey) {
@@ -473,26 +415,7 @@ export async function runServerToolOrchestration(options) {
473
415
  const retryEmptyFollowupOnce = isStopMessageFlow || isGeminiEmptyReplyContinue;
474
416
  const maxAttempts = retryEmptyFollowupOnce ? 2 : 1;
475
417
  const followupRequestId = buildFollowupRequestId(options.requestId, engineResult.execution.followup.requestIdSuffix);
476
- const followupStream = typeof metadata.stream === 'boolean'
477
- ? (metadata.stream)
478
- : undefined;
479
- let followupPayload = coerceFollowupPayloadStream(followupSeedPayload, followupStream);
480
- if (isGeminiEmptyReplyContinue) {
481
- // For gemini_empty_reply_continue, the goal is to recover text output from an empty/malformed reply.
482
- // Force the followup to be non-tool-calling to avoid repeated MALFORMED_FUNCTION_CALL loops.
483
- const paramsRaw = followupPayload.parameters;
484
- const params = paramsRaw && typeof paramsRaw === 'object' && !Array.isArray(paramsRaw) ? { ...paramsRaw } : {};
485
- params.tool_choice = 'none';
486
- params.parallel_tool_calls = false;
487
- // Ensure we don't override the tool_choice->toolConfig mapping with an inherited tool_config.
488
- delete params.tool_config;
489
- delete params.toolConfig;
490
- followupPayload.parameters = params;
491
- // Additionally, strip tool-call history. Gemini/CloudCode can strict-validate
492
- // (history tool calls) ↔ (current tool declarations). We keep tools declared (so the
493
- // session can continue), but remove history tool artifacts to avoid malformed loops.
494
- followupPayload.messages = stripToolHistoryFromFollowupMessages(followupPayload.messages);
495
- }
418
+ let followupPayload = coerceFollowupPayloadStream(followupPayloadRaw, metadata.stream === true);
496
419
  followupPayload = applyHubFollowupPolicyShadow({
497
420
  requestId: followupRequestId,
498
421
  entryEndpoint: followupEntryEndpoint,
@@ -92,9 +92,8 @@ const handler = async (ctx) => {
92
92
  entryEndpoint: ctx.entryEndpoint,
93
93
  injection: {
94
94
  ops: [
95
- { op: 'trim_openai_messages', maxNonSystemMessages: 16 },
96
95
  { op: 'append_assistant_message', required: false },
97
- { op: 'append_user_text', text: 'continue' }
96
+ { op: 'append_user_text', text: '继续执行' }
98
97
  ]
99
98
  },
100
99
  metadata: (() => {
@@ -825,22 +825,6 @@ export class ResponsesResponseBuilder {
825
825
  }
826
826
  return { success: true, response: this.response };
827
827
  }
828
- // 进一步容错:少数上游会在输出流结束前没有发送 output_item.done/response.completed/response.done,
829
- // 但已发送 output_item.added/content_part.* 等事件。此时 outputItemBuilders 中已存在内容,
830
- // 直接按当前聚合结果构建 output 并视为 completed,避免 SSE_TO_JSON_ERROR: Building not completed。
831
- if (this.outputItemBuilders.size > 0) {
832
- this.response.status = 'completed';
833
- try {
834
- const cur = this.response.output;
835
- if (!Array.isArray(cur) || cur.length === 0) {
836
- this.response.output = this.buildOutputItems();
837
- }
838
- }
839
- catch {
840
- this.response.output = this.buildOutputItems();
841
- }
842
- return { success: true, response: this.response };
843
- }
844
828
  }
845
829
  catch { /* ignore */ }
846
830
  return { success: false, error: new Error('Building not completed') };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/llms",
3
- "version": "0.6.1399",
3
+ "version": "0.6.1403",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -1,10 +0,0 @@
1
- import type { StandardizedRequest, ProcessedRequest } from '../../types/standardized.js';
2
- import type { AdapterContext } from '../../types/chat-envelope.js';
3
- import type { StageRecorder } from '../../format-adapters/index.js';
4
- import type { NormalizedRequest, TargetMetadata } from './types.js';
5
- export declare function buildAdapterContext(normalized: NormalizedRequest, target?: TargetMetadata): AdapterContext;
6
- export declare function maybeCreateStageRecorder(context: AdapterContext, endpoint?: string, options?: {
7
- disableSnapshots?: boolean;
8
- }): StageRecorder | undefined;
9
- export declare function resolveOutboundStreamIntent(providerPreference?: TargetMetadata['streaming']): boolean | undefined;
10
- export declare function applyOutboundStreamPreference(request: StandardizedRequest | ProcessedRequest, stream: boolean | undefined): StandardizedRequest | ProcessedRequest;
@@ -1,142 +0,0 @@
1
- import { jsonClone } from '../../types/json.js';
2
- import { applyHubOperations } from '../../ops/operations.js';
3
- import { createSnapshotRecorder } from '../../snapshot-recorder.js';
4
- import { shouldRecordSnapshots } from '../../../shared/snapshot-utils.js';
5
- import { cloneRuntimeMetadata } from '../../../shared/runtime-metadata.js';
6
- function normalizeToolCallIdStyleCandidate(value) {
7
- if (typeof value !== 'string') {
8
- return undefined;
9
- }
10
- const normalized = value.trim().toLowerCase();
11
- if (normalized === 'fc') {
12
- return 'fc';
13
- }
14
- if (normalized === 'preserve') {
15
- return 'preserve';
16
- }
17
- return undefined;
18
- }
19
- export function buildAdapterContext(normalized, target) {
20
- const metadata = normalized.metadata || {};
21
- const providerProtocol = target?.outboundProfile || normalized.providerProtocol;
22
- const providerId = (target?.providerKey || metadata.providerKey);
23
- const routeId = metadata.routeName;
24
- const profileId = (target?.providerKey || metadata.pipelineId);
25
- const streamingHint = normalized.stream === true ? 'force' : normalized.stream === false ? 'disable' : 'auto';
26
- const toolCallIdStyle = normalizeToolCallIdStyleCandidate(metadata.toolCallIdStyle);
27
- const adapterContext = {
28
- requestId: normalized.id,
29
- entryEndpoint: normalized.entryEndpoint || '/v1/chat/completions',
30
- providerProtocol,
31
- providerId,
32
- routeId,
33
- profileId,
34
- streamingHint,
35
- toolCallIdStyle
36
- };
37
- const runtime = metadata.runtime;
38
- if (runtime && typeof runtime === 'object' && !Array.isArray(runtime)) {
39
- adapterContext.runtime = jsonClone(runtime);
40
- }
41
- const clientRequestId = typeof metadata.clientRequestId === 'string'
42
- ? metadata.clientRequestId.trim()
43
- : '';
44
- if (clientRequestId) {
45
- adapterContext.clientRequestId = clientRequestId;
46
- }
47
- const groupRequestId = typeof metadata.groupRequestId === 'string'
48
- ? metadata.groupRequestId.trim()
49
- : '';
50
- if (groupRequestId) {
51
- adapterContext.groupRequestId = groupRequestId;
52
- }
53
- if (typeof metadata.originalModelId === 'string') {
54
- adapterContext.originalModelId = metadata.originalModelId;
55
- }
56
- if (typeof metadata.clientModelId === 'string') {
57
- adapterContext.clientModelId = metadata.clientModelId;
58
- }
59
- if (typeof metadata.assignedModelId === 'string') {
60
- adapterContext.modelId = metadata.assignedModelId;
61
- }
62
- const rt = cloneRuntimeMetadata(metadata);
63
- if (rt) {
64
- adapterContext.__rt = rt;
65
- }
66
- const sessionId = typeof metadata.sessionId === 'string'
67
- ? metadata.sessionId.trim()
68
- : '';
69
- if (sessionId) {
70
- adapterContext.sessionId = sessionId;
71
- }
72
- const conversationId = typeof metadata.conversationId === 'string'
73
- ? metadata.conversationId.trim()
74
- : '';
75
- if (conversationId) {
76
- adapterContext.conversationId = conversationId;
77
- }
78
- const projectIdRaw = typeof metadata.projectId === 'string'
79
- ? metadata.projectId.trim()
80
- : typeof metadata.project_id === 'string'
81
- ? metadata.project_id.trim()
82
- : '';
83
- if (projectIdRaw) {
84
- adapterContext.projectId = projectIdRaw;
85
- }
86
- const clientConnectionState = metadata.clientConnectionState;
87
- if (clientConnectionState && typeof clientConnectionState === 'object' && !Array.isArray(clientConnectionState)) {
88
- const stateRecord = clientConnectionState;
89
- adapterContext.clientConnectionState = clientConnectionState;
90
- if (typeof stateRecord.disconnected === 'boolean') {
91
- adapterContext.clientDisconnected = stateRecord.disconnected;
92
- }
93
- }
94
- const clientDisconnectedRaw = metadata.clientDisconnected;
95
- if (clientDisconnectedRaw === true ||
96
- (typeof clientDisconnectedRaw === 'string' && clientDisconnectedRaw.trim().toLowerCase() === 'true')) {
97
- adapterContext.clientDisconnected = true;
98
- }
99
- if (target?.compatibilityProfile && typeof target.compatibilityProfile === 'string') {
100
- adapterContext.compatibilityProfile = target.compatibilityProfile;
101
- }
102
- return adapterContext;
103
- }
104
- export function maybeCreateStageRecorder(context, endpoint, options) {
105
- if (options?.disableSnapshots === true) {
106
- return undefined;
107
- }
108
- if (!shouldRecordSnapshots()) {
109
- return undefined;
110
- }
111
- const effectiveEndpoint = endpoint || context.entryEndpoint || '/v1/chat/completions';
112
- try {
113
- return createSnapshotRecorder(context, effectiveEndpoint);
114
- }
115
- catch {
116
- return undefined;
117
- }
118
- }
119
- export function resolveOutboundStreamIntent(providerPreference) {
120
- if (providerPreference === 'always') {
121
- return true;
122
- }
123
- if (providerPreference === 'never') {
124
- return false;
125
- }
126
- return undefined;
127
- }
128
- export function applyOutboundStreamPreference(request, stream) {
129
- if (!request || typeof request !== 'object') {
130
- return request;
131
- }
132
- const ops = [];
133
- if (typeof stream === 'boolean') {
134
- ops.push({ op: 'set_request_parameter_fields', fields: { stream } });
135
- ops.push({ op: 'set_request_metadata_fields', fields: { outboundStream: stream } });
136
- }
137
- else {
138
- ops.push({ op: 'unset_request_parameter_keys', keys: ['stream'] });
139
- ops.push({ op: 'unset_request_metadata_keys', keys: ['outboundStream'] });
140
- }
141
- return applyHubOperations(request, ops);
142
- }
@@ -1,6 +0,0 @@
1
- import type { AdapterContext, ChatEnvelope } from '../../types/chat-envelope.js';
2
- export declare function captureAnthropicToolNameAliasMap(options: {
3
- entryEndpoint: string;
4
- adapterContext: AdapterContext;
5
- chatEnvelope: ChatEnvelope;
6
- }): void;
@@ -1,79 +0,0 @@
1
- import { isJsonObject, jsonClone } from '../../types/json.js';
2
- function coerceAliasMap(candidate) {
3
- if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
4
- return undefined;
5
- }
6
- const normalized = {};
7
- for (const [key, value] of Object.entries(candidate)) {
8
- if (typeof key !== 'string' || typeof value !== 'string') {
9
- continue;
10
- }
11
- const trimmedKey = key.trim();
12
- const trimmedValue = value.trim();
13
- if (!trimmedKey.length || !trimmedValue.length) {
14
- continue;
15
- }
16
- normalized[trimmedKey] = trimmedValue;
17
- }
18
- return Object.keys(normalized).length ? normalized : undefined;
19
- }
20
- function readAliasMapFromSemantics(chatEnvelope) {
21
- if (!chatEnvelope?.semantics || typeof chatEnvelope.semantics !== 'object') {
22
- return undefined;
23
- }
24
- const node = chatEnvelope.semantics.tools;
25
- if (!node || !isJsonObject(node)) {
26
- return undefined;
27
- }
28
- const candidate = node.toolNameAliasMap ?? node.toolAliasMap;
29
- return coerceAliasMap(candidate);
30
- }
31
- function shouldCapture(entryEndpoint) {
32
- return typeof entryEndpoint === 'string' && entryEndpoint.toLowerCase().includes('/v1/messages');
33
- }
34
- function resolveAliasMapFromSources(adapterContext, chatEnvelope) {
35
- const fromContext = coerceAliasMap(adapterContext.anthropicToolNameMap);
36
- if (fromContext) {
37
- return fromContext;
38
- }
39
- const metadataNode = chatEnvelope.metadata;
40
- const direct = metadataNode ? coerceAliasMap(metadataNode.anthropicToolNameMap) : undefined;
41
- if (direct) {
42
- return direct;
43
- }
44
- const contextNode = metadataNode && metadataNode.context && typeof metadataNode.context === 'object'
45
- ? metadataNode.context
46
- : undefined;
47
- const fromContextNode = coerceAliasMap(contextNode?.anthropicToolNameMap);
48
- if (fromContextNode) {
49
- return fromContextNode;
50
- }
51
- return readAliasMapFromSemantics(chatEnvelope);
52
- }
53
- export function captureAnthropicToolNameAliasMap(options) {
54
- if (!shouldCapture(options.entryEndpoint)) {
55
- return;
56
- }
57
- const aliasMap = resolveAliasMapFromSources(options.adapterContext, options.chatEnvelope);
58
- if (!aliasMap) {
59
- return;
60
- }
61
- // Tool name alias map is mappable semantics and must live in chat.semantics (never metadata).
62
- try {
63
- const chatEnvelope = options.chatEnvelope;
64
- if (!chatEnvelope.semantics || typeof chatEnvelope.semantics !== 'object' || Array.isArray(chatEnvelope.semantics)) {
65
- chatEnvelope.semantics = {};
66
- }
67
- const semantics = chatEnvelope.semantics;
68
- if (!semantics.tools || !isJsonObject(semantics.tools)) {
69
- semantics.tools = {};
70
- }
71
- const toolsNode = semantics.tools;
72
- if (!isJsonObject(toolsNode.toolNameAliasMap) && !isJsonObject(toolsNode.toolAliasMap)) {
73
- toolsNode.toolNameAliasMap = jsonClone(aliasMap);
74
- }
75
- }
76
- catch {
77
- // best-effort: never block request handling due to alias map propagation failures
78
- }
79
- }
@@ -1,3 +0,0 @@
1
- import type { ApplyPatchToolMode } from './types.js';
2
- export declare function resolveApplyPatchToolModeFromEnv(): ApplyPatchToolMode | undefined;
3
- export declare function resolveApplyPatchToolModeFromTools(toolsRaw: unknown): ApplyPatchToolMode | undefined;
@@ -1,46 +0,0 @@
1
- function isTruthyEnv(value) {
2
- const v = typeof value === 'string' ? value.trim().toLowerCase() : '';
3
- return v === '1' || v === 'true' || v === 'yes' || v === 'on';
4
- }
5
- export function resolveApplyPatchToolModeFromEnv() {
6
- const rawMode = String(process.env.RCC_APPLY_PATCH_TOOL_MODE || process.env.ROUTECODEX_APPLY_PATCH_TOOL_MODE || '')
7
- .trim()
8
- .toLowerCase();
9
- if (rawMode === 'freeform')
10
- return 'freeform';
11
- if (rawMode === 'schema' || rawMode === 'json_schema')
12
- return 'schema';
13
- const freeformFlag = process.env.RCC_APPLY_PATCH_FREEFORM || process.env.ROUTECODEX_APPLY_PATCH_FREEFORM;
14
- if (isTruthyEnv(freeformFlag))
15
- return 'freeform';
16
- return undefined;
17
- }
18
- export function resolveApplyPatchToolModeFromTools(toolsRaw) {
19
- if (!Array.isArray(toolsRaw) || toolsRaw.length === 0) {
20
- return undefined;
21
- }
22
- for (const entry of toolsRaw) {
23
- if (!entry || typeof entry !== 'object' || Array.isArray(entry))
24
- continue;
25
- const record = entry;
26
- const type = typeof record.type === 'string' ? record.type.trim().toLowerCase() : '';
27
- if (type && type !== 'function')
28
- continue;
29
- const fn = record.function && typeof record.function === 'object' && !Array.isArray(record.function)
30
- ? record.function
31
- : undefined;
32
- const name = typeof fn?.name === 'string' ? fn.name.trim().toLowerCase() : '';
33
- if (name !== 'apply_patch')
34
- continue;
35
- const format = typeof record.format === 'string'
36
- ? record.format.trim().toLowerCase()
37
- : typeof fn?.format === 'string'
38
- ? String(fn.format).trim().toLowerCase()
39
- : '';
40
- if (format === 'freeform')
41
- return 'freeform';
42
- // If apply_patch is present without explicit freeform marker, default to schema mode.
43
- return 'schema';
44
- }
45
- return undefined;
46
- }
@@ -1,8 +0,0 @@
1
- import type { VirtualRouterEngine } from '../../../../router/virtual-router/engine.js';
2
- import type { HubPipelineConfig, HubPipelineResult, NormalizedRequest, ProviderProtocol, RequestStageHooks } from './types.js';
3
- export declare function executeChatProcessEntryPipeline(options: {
4
- config: HubPipelineConfig;
5
- routerEngine: VirtualRouterEngine;
6
- normalized: NormalizedRequest;
7
- resolveProtocolHooks: (protocol: ProviderProtocol) => RequestStageHooks<Record<string, unknown>> | undefined;
8
- }): Promise<HubPipelineResult>;