@jsonstudio/llms 0.6.1172 → 0.6.1354

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 (160) hide show
  1. package/dist/conversion/codecs/gemini-openai-codec.d.ts +3 -1
  2. package/dist/conversion/codecs/gemini-openai-codec.js +10 -4
  3. package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -1
  4. package/dist/conversion/compat/actions/gemini-web-search.js +5 -2
  5. package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +12 -0
  6. package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +199 -0
  7. package/dist/conversion/compat/actions/iflow-web-search.d.ts +1 -1
  8. package/dist/conversion/compat/actions/iflow-web-search.js +5 -2
  9. package/dist/conversion/hub/operation-table/semantic-mappers/anthropic-mapper.js +47 -56
  10. package/dist/conversion/hub/operation-table/semantic-mappers/chat-mapper.js +1 -13
  11. package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +523 -50
  12. package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +18 -38
  13. package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +6 -0
  14. package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +3 -0
  15. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.d.ts +10 -0
  16. package/dist/conversion/hub/pipeline/hub-pipeline/adapter-context.js +134 -0
  17. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.d.ts +6 -0
  18. package/dist/conversion/hub/pipeline/hub-pipeline/anthropic-alias-map.js +79 -0
  19. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.d.ts +3 -0
  20. package/dist/conversion/hub/pipeline/hub-pipeline/apply-patch-tool-mode.js +46 -0
  21. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.d.ts +8 -0
  22. package/dist/conversion/hub/pipeline/hub-pipeline/execute-chat-process-entry.js +366 -0
  23. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.d.ts +9 -0
  24. package/dist/conversion/hub/pipeline/hub-pipeline/execute-request-stage.js +384 -0
  25. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.d.ts +3 -0
  26. package/dist/conversion/hub/pipeline/hub-pipeline/node-results.js +14 -0
  27. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.d.ts +2 -0
  28. package/dist/conversion/hub/pipeline/hub-pipeline/payload-normalize.js +144 -0
  29. package/dist/conversion/hub/pipeline/hub-pipeline/policy.d.ts +4 -0
  30. package/dist/conversion/hub/pipeline/hub-pipeline/policy.js +32 -0
  31. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.d.ts +8 -0
  32. package/dist/conversion/hub/pipeline/hub-pipeline/protocol.js +63 -0
  33. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.d.ts +2 -0
  34. package/dist/conversion/hub/pipeline/hub-pipeline/resolve-protocol-hooks.js +43 -0
  35. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.d.ts +1 -0
  36. package/dist/conversion/hub/pipeline/hub-pipeline/semantic-gate.js +29 -0
  37. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.d.ts +2 -0
  38. package/dist/conversion/hub/pipeline/hub-pipeline/servertool-runtime-config.js +16 -0
  39. package/dist/conversion/hub/pipeline/hub-pipeline/types.d.ts +116 -0
  40. package/dist/conversion/hub/pipeline/hub-pipeline/types.js +1 -0
  41. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +3 -95
  42. package/dist/conversion/hub/pipeline/hub-pipeline.js +19 -1281
  43. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage1_format_parse/index.js +1 -1
  44. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.d.ts +7 -0
  45. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage2_semantic_map/index.js +65 -1
  46. package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +25 -22
  47. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage1_semantic_map/index.js +1 -1
  48. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.d.ts +1 -1
  49. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage2_format_build/index.js +2 -2
  50. package/dist/conversion/hub/pipeline/stages/req_outbound/req_outbound_stage3_compat/index.js +2 -2
  51. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage1_tool_governance/index.js +1 -1
  52. package/dist/conversion/hub/pipeline/stages/req_process/req_process_stage2_route_select/index.js +1 -1
  53. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +11 -11
  54. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage2_format_parse/index.js +1 -1
  55. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.d.ts +1 -0
  56. package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage3_semantic_map/index.js +4 -2
  57. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.d.ts +1 -0
  58. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +17 -9
  59. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage2_sse_stream/index.js +2 -2
  60. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +40 -2
  61. package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage2_finalize/index.js +1 -1
  62. package/dist/conversion/hub/pipeline/target-utils.js +9 -5
  63. package/dist/conversion/hub/process/chat-process.js +256 -16
  64. package/dist/conversion/hub/response/provider-response.d.ts +8 -0
  65. package/dist/conversion/hub/response/provider-response.js +85 -27
  66. package/dist/conversion/hub/response/response-mappers.d.ts +10 -3
  67. package/dist/conversion/hub/response/response-mappers.js +30 -6
  68. package/dist/conversion/hub/response/response-runtime.js +4 -38
  69. package/dist/conversion/hub/snapshot-recorder.js +5 -1
  70. package/dist/conversion/hub/standardized-bridge.js +23 -15
  71. package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +36 -5
  72. package/dist/conversion/responses/responses-openai-bridge.js +20 -4
  73. package/dist/conversion/shared/gemini-tool-utils.d.ts +8 -1
  74. package/dist/conversion/shared/gemini-tool-utils.js +580 -108
  75. package/dist/conversion/shared/jsonish.js +1 -1
  76. package/dist/conversion/shared/mcp-injection.js +67 -33
  77. package/dist/conversion/shared/openai-finalizer.js +2 -1
  78. package/dist/conversion/shared/openai-message-normalize.js +76 -21
  79. package/dist/conversion/shared/responses-output-builder.js +6 -0
  80. package/dist/conversion/shared/runtime-metadata.d.ts +7 -0
  81. package/dist/conversion/shared/runtime-metadata.js +23 -0
  82. package/dist/conversion/shared/text-markup-normalizer.d.ts +2 -0
  83. package/dist/conversion/shared/text-markup-normalizer.js +284 -4
  84. package/dist/conversion/shared/tool-canonicalizer.js +2 -1
  85. package/dist/conversion/shared/tool-governor.js +3 -3
  86. package/dist/filters/engine.js +5 -5
  87. package/dist/filters/special/request-tool-list-filter.js +194 -60
  88. package/dist/filters/special/request-tools-normalize.js +1 -1
  89. package/dist/filters/special/response-tool-text-canonicalize.d.ts +4 -7
  90. package/dist/filters/special/response-tool-text-canonicalize.js +7 -35
  91. package/dist/filters/special/tool-filter-hooks.js +58 -62
  92. package/dist/guidance/index.js +5 -1
  93. package/dist/http/sse-response.js +6 -6
  94. package/dist/router/virtual-router/bootstrap.js +48 -4
  95. package/dist/router/virtual-router/engine-health.d.ts +1 -1
  96. package/dist/router/virtual-router/engine-health.js +11 -110
  97. package/dist/router/virtual-router/engine-selection/alias-selection.d.ts +15 -0
  98. package/dist/router/virtual-router/engine-selection/alias-selection.js +156 -0
  99. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.d.ts +11 -0
  100. package/dist/router/virtual-router/engine-selection/context-weight-multipliers.js +23 -0
  101. package/dist/router/virtual-router/engine-selection/direct-provider-model.d.ts +9 -0
  102. package/dist/router/virtual-router/engine-selection/direct-provider-model.js +49 -0
  103. package/dist/router/virtual-router/engine-selection/instruction-target.d.ts +6 -0
  104. package/dist/router/virtual-router/engine-selection/instruction-target.js +54 -0
  105. package/dist/router/virtual-router/engine-selection/key-parsing.d.ts +8 -0
  106. package/dist/router/virtual-router/engine-selection/key-parsing.js +64 -0
  107. package/dist/router/virtual-router/engine-selection/route-utils.d.ts +12 -0
  108. package/dist/router/virtual-router/engine-selection/route-utils.js +150 -0
  109. package/dist/router/virtual-router/engine-selection/routing-state-filter.d.ts +4 -0
  110. package/dist/router/virtual-router/engine-selection/routing-state-filter.js +50 -0
  111. package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +39 -0
  112. package/dist/router/virtual-router/engine-selection/selection-deps.js +1 -0
  113. package/dist/router/virtual-router/engine-selection/sticky-pool.d.ts +11 -0
  114. package/dist/router/virtual-router/engine-selection/sticky-pool.js +109 -0
  115. package/dist/router/virtual-router/engine-selection/tier-priority.d.ts +12 -0
  116. package/dist/router/virtual-router/engine-selection/tier-priority.js +55 -0
  117. package/dist/router/virtual-router/engine-selection/tier-selection-select.d.ts +22 -0
  118. package/dist/router/virtual-router/engine-selection/tier-selection-select.js +400 -0
  119. package/dist/router/virtual-router/engine-selection/tier-selection.d.ts +3 -0
  120. package/dist/router/virtual-router/engine-selection/tier-selection.js +225 -0
  121. package/dist/router/virtual-router/engine-selection.d.ts +4 -30
  122. package/dist/router/virtual-router/engine-selection.js +10 -962
  123. package/dist/router/virtual-router/engine.d.ts +1 -0
  124. package/dist/router/virtual-router/engine.js +55 -10
  125. package/dist/router/virtual-router/routing-instructions.js +6 -1
  126. package/dist/router/virtual-router/stop-message-state-sync.d.ts +5 -0
  127. package/dist/router/virtual-router/stop-message-state-sync.js +6 -14
  128. package/dist/router/virtual-router/types.d.ts +25 -1
  129. package/dist/servertool/clock/config.d.ts +8 -0
  130. package/dist/servertool/clock/config.js +22 -0
  131. package/dist/servertool/clock/log.d.ts +3 -0
  132. package/dist/servertool/clock/log.js +13 -0
  133. package/dist/servertool/clock/task-store.d.ts +1 -1
  134. package/dist/servertool/clock/task-store.js +1 -1
  135. package/dist/servertool/clock/tasks.js +1 -1
  136. package/dist/servertool/engine.js +146 -21
  137. package/dist/servertool/handlers/clock-auto.js +11 -6
  138. package/dist/servertool/handlers/clock.js +36 -10
  139. package/dist/servertool/handlers/followup-request-builder.js +8 -2
  140. package/dist/servertool/handlers/gemini-empty-reply-continue.js +15 -9
  141. package/dist/servertool/handlers/iflow-model-error-retry.js +6 -4
  142. package/dist/servertool/handlers/recursive-detection-guard.js +4 -2
  143. package/dist/servertool/handlers/stop-message-auto.js +100 -10
  144. package/dist/servertool/handlers/vision.js +4 -1
  145. package/dist/servertool/handlers/web-search.js +3 -1
  146. package/dist/servertool/pending-session.d.ts +19 -0
  147. package/dist/servertool/pending-session.js +97 -0
  148. package/dist/servertool/reenter-backend.js +5 -3
  149. package/dist/servertool/server-side-tools.js +235 -6
  150. package/dist/servertool/types.d.ts +13 -0
  151. package/dist/sse/json-to-sse/event-generators/responses.js +1 -1
  152. package/dist/sse/shared/chat-serializer.js +2 -2
  153. package/dist/sse/shared/constants.js +1 -1
  154. package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +7 -1
  155. package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
  156. package/dist/sse/sse-to-json/responses-sse-to-json-converter.d.ts +1 -1
  157. package/dist/tools/apply-patch/execution-capturer.js +1 -1
  158. package/dist/tools/exec-command/normalize.js +4 -0
  159. package/dist/tools/exec-command/regression-capturer.js +1 -1
  160. package/package.json +10 -5
@@ -1,6 +1,6 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runReqInboundStage1FormatParse(options) {
3
3
  const envelope = (await options.formatAdapter.parseRequest(options.rawRequest, options.adapterContext));
4
- recordStage(options.stageRecorder, 'req_inbound_stage1_format_parse', envelope);
4
+ recordStage(options.stageRecorder, 'chat_process.req.stage1.format_parse', envelope);
5
5
  return envelope;
6
6
  }
@@ -7,6 +7,13 @@ export interface ReqInboundStage2SemanticMapOptions {
7
7
  adapterContext: AdapterContext;
8
8
  formatEnvelope: FormatEnvelope<JsonObject>;
9
9
  semanticMapper: Pick<SemanticMapper, 'toChat'>;
10
+ /**
11
+ * Mappable cross-protocol semantics (e.g. /v1/responses submit resume) must be
12
+ * lifted into chat semantics before entering chat_process.
13
+ *
14
+ * This must never be stored in metadata/AdapterContext.
15
+ */
16
+ responsesResume?: JsonObject;
10
17
  stageRecorder?: StageRecorder;
11
18
  }
12
19
  export interface ReqInboundStage2SemanticMapResult {
@@ -2,6 +2,7 @@ import { chatEnvelopeToStandardized } from '../../../../standardized-bridge.js';
2
2
  import { validateChatEnvelope } from '../../../../../shared/chat-envelope-validator.js';
3
3
  import { applyHubOperationTableInbound } from '../../../../operation-table/operation-table-runner.js';
4
4
  import { recordStage } from '../../../stages/utils.js';
5
+ import { isJsonObject, jsonClone } from '../../../../types/json.js';
5
6
  export async function runReqInboundStage2SemanticMap(options) {
6
7
  const chatEnvelope = await options.semanticMapper.toChat(options.formatEnvelope, options.adapterContext);
7
8
  applyHubOperationTableInbound({
@@ -9,11 +10,74 @@ export async function runReqInboundStage2SemanticMap(options) {
9
10
  chatEnvelope,
10
11
  adapterContext: options.adapterContext
11
12
  });
13
+ // Semantic Gate (request): before entering chat_process, lift any mappable protocol semantics
14
+ // into ChatEnvelope.semantics. Do not persist these in metadata.
15
+ try {
16
+ if (!chatEnvelope.semantics || typeof chatEnvelope.semantics !== 'object') {
17
+ chatEnvelope.semantics = {};
18
+ }
19
+ const semantics = chatEnvelope.semantics;
20
+ if (!semantics.tools || !isJsonObject(semantics.tools)) {
21
+ semantics.tools = {};
22
+ }
23
+ const toolsNode = semantics.tools;
24
+ const rawTools = Array.isArray(options.formatEnvelope.payload?.tools)
25
+ ? (options.formatEnvelope.payload.tools ?? [])
26
+ : [];
27
+ if (rawTools.length && toolsNode.clientToolsRaw === undefined) {
28
+ toolsNode.clientToolsRaw = jsonClone(rawTools);
29
+ }
30
+ if (options.responsesResume && isJsonObject(options.responsesResume)) {
31
+ if (!semantics.responses || !isJsonObject(semantics.responses)) {
32
+ semantics.responses = {};
33
+ }
34
+ const responsesNode = semantics.responses;
35
+ if (responsesNode.resume === undefined) {
36
+ responsesNode.resume = jsonClone(options.responsesResume);
37
+ }
38
+ // If this is a Responses submit/resume hop, tool outputs may exist only in resume metadata.
39
+ // Lift them into canonical chat.toolOutputs so chat_process sees a single unified surface.
40
+ const hasDirectToolOutputs = Array.isArray(chatEnvelope.toolOutputs) && chatEnvelope.toolOutputs.length > 0;
41
+ if (!hasDirectToolOutputs) {
42
+ const detailed = Array.isArray(options.responsesResume.toolOutputsDetailed)
43
+ ? options.responsesResume.toolOutputsDetailed
44
+ : undefined;
45
+ if (detailed && detailed.length) {
46
+ const mapped = detailed
47
+ .map((entry, index) => {
48
+ if (!entry || typeof entry !== 'object')
49
+ return undefined;
50
+ const callId = typeof entry.callId === 'string' && String(entry.callId).trim().length
51
+ ? String(entry.callId).trim()
52
+ : typeof entry.originalId === 'string' && String(entry.originalId).trim().length
53
+ ? String(entry.originalId).trim()
54
+ : `resume_tool_${index + 1}`;
55
+ const outputText = typeof entry.outputText === 'string'
56
+ ? entry.outputText
57
+ : (() => { try {
58
+ return JSON.stringify(entry.outputText ?? '');
59
+ }
60
+ catch {
61
+ return String(entry.outputText ?? '');
62
+ } })();
63
+ return { tool_call_id: callId, content: outputText };
64
+ })
65
+ .filter(Boolean);
66
+ if (mapped.length) {
67
+ chatEnvelope.toolOutputs = mapped;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ catch {
74
+ // fail-fast is enforced at chat_process entry; this gate is best-effort
75
+ }
12
76
  validateChatEnvelope(chatEnvelope, {
13
77
  stage: 'req_inbound',
14
78
  direction: 'request'
15
79
  });
16
- recordStage(options.stageRecorder, 'req_inbound_stage2_semantic_map', chatEnvelope);
80
+ recordStage(options.stageRecorder, 'chat_process.req.stage2.semantic_map', chatEnvelope);
17
81
  const standardizedRequest = chatEnvelopeToStandardized(chatEnvelope, {
18
82
  adapterContext: options.adapterContext,
19
83
  endpoint: options.adapterContext.entryEndpoint,
@@ -1,26 +1,7 @@
1
1
  import { captureResponsesContext, buildChatRequestFromResponses } from '../../../../../responses/responses-openai-bridge.js';
2
+ import { captureResponsesRequestContext } from '../../../../../shared/responses-conversation-store.js';
2
3
  import { recordStage } from '../../../stages/utils.js';
3
4
  export async function runReqInboundStage3ContextCapture(options) {
4
- // 对由 server-side 工具触发的二跳/内部跳转请求(例如 stopMessage followup),
5
- // 跳过工具输出扫描与 apply_patch 诊断日志,避免在内部流中重复放大客户端已收到的
6
- // 工具错误信息。此类请求的工具治理在 chat-process 阶段完成,这里仅保留最小快照。
7
- try {
8
- const ctx = options.adapterContext;
9
- const followupFlag = ctx?.serverToolFollowup;
10
- const isFollowup = followupFlag === true ||
11
- (typeof followupFlag === 'string' && followupFlag.trim().toLowerCase() === 'true');
12
- if (isFollowup) {
13
- const snapshot = {
14
- providerProtocol: options.adapterContext.providerProtocol ?? 'unknown',
15
- tool_outputs: []
16
- };
17
- recordStage(options.stageRecorder, 'req_inbound_stage3_context_capture', snapshot);
18
- return snapshot;
19
- }
20
- }
21
- catch {
22
- // best-effort: 若检测 serverToolFollowup 失败,则继续走通用路径
23
- }
24
5
  let context;
25
6
  if (options.captureContext) {
26
7
  try {
@@ -37,7 +18,7 @@ export async function runReqInboundStage3ContextCapture(options) {
37
18
  const snapshot = context
38
19
  ? augmentContextSnapshot(context, fallbackSnapshot)
39
20
  : fallbackSnapshot;
40
- recordStage(options.stageRecorder, 'req_inbound_stage3_context_capture', snapshot);
21
+ recordStage(options.stageRecorder, 'chat_process.req.stage3.context_capture', snapshot);
41
22
  return context ?? snapshot;
42
23
  }
43
24
  export function runChatContextCapture(options) {
@@ -73,6 +54,28 @@ export function captureResponsesContextSnapshot(options) {
73
54
  catch {
74
55
  // best-effort context capture
75
56
  }
57
+ // OpenAI Responses tool loop: store the request context keyed by requestId so that
58
+ // `/v1/responses/:id/submit_tool_outputs` can resume the conversation later.
59
+ //
60
+ // This must be done on the hub pipeline inbound path (not in host/provider), because:
61
+ // - the tool loop is a client-protocol behavior (/v1/responses), independent of providerProtocol;
62
+ // - providers must remain transport-only;
63
+ // - the host may later enhance requestId with providerKey/model for logging, which is handled via rebind.
64
+ try {
65
+ const requestId = typeof options.adapterContext.requestId === 'string' && options.adapterContext.requestId.trim().length
66
+ ? options.adapterContext.requestId
67
+ : undefined;
68
+ if (requestId) {
69
+ captureResponsesRequestContext({
70
+ requestId,
71
+ payload: options.rawRequest,
72
+ context: context
73
+ });
74
+ }
75
+ }
76
+ catch {
77
+ // best-effort: capture failures must not block the request path
78
+ }
76
79
  return context;
77
80
  }
78
81
  function captureChatContextSnapshot(options) {
@@ -306,7 +309,7 @@ function buildApplyPatchDiagnostics(output) {
306
309
  return '\n\n[RouteCodex precheck] apply_patch 参数解析失败:缺少字段 "input"。当前 RouteCodex 期望 { input, patch } 形态,并且两个字段都应包含完整统一 diff 文本。';
307
310
  }
308
311
  if (output.includes('invalid type: map, expected a string')) {
309
- return '\n\n[RouteCodex precheck] apply_patch 参数类型错误:检测到 JSON 对象(map),但客户端期望字符串。请先对参数做 JSON.stringify 再写入 arguments,或直接提供 { patch: \"<统一 diff>\" } 形式。';
312
+ return '\n\n[RouteCodex precheck] apply_patch 参数类型错误:检测到 JSON 对象(map),但客户端期望字符串。请先对参数做 JSON.stringify 再写入 arguments,或直接提供 { patch: "<统一 diff>" } 形式。';
310
313
  }
311
314
  return undefined;
312
315
  }
@@ -28,7 +28,7 @@ export async function runReqOutboundStage1SemanticMap(options) {
28
28
  formatEnvelope,
29
29
  adapterContext: options.adapterContext
30
30
  });
31
- recordStage(options.stageRecorder, 'req_outbound_stage1_semantic_map', chatEnvelope);
31
+ recordStage(options.stageRecorder, 'chat_process.req.stage6.outbound.semantic_map', chatEnvelope);
32
32
  return { chatEnvelope, formatEnvelope };
33
33
  }
34
34
  function attachToolOutputsFromContext(chatEnvelope, snapshot) {
@@ -5,7 +5,7 @@ import type { FormatAdapter, StageRecorder } from '../../../../format-adapters/i
5
5
  export interface ReqOutboundStage2FormatBuildOptions {
6
6
  formatEnvelope: FormatEnvelope<JsonObject>;
7
7
  adapterContext: AdapterContext;
8
- formatAdapter: Pick<FormatAdapter, 'buildResponse'>;
8
+ formatAdapter: Pick<FormatAdapter, 'buildRequest'>;
9
9
  stageRecorder?: StageRecorder;
10
10
  }
11
11
  export declare function runReqOutboundStage2FormatBuild(options: ReqOutboundStage2FormatBuildOptions): Promise<JsonObject>;
@@ -1,9 +1,9 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  const PRIVATE_FIELD_PREFIX = '__rcc_';
3
3
  export async function runReqOutboundStage2FormatBuild(options) {
4
- const payload = (await options.formatAdapter.buildResponse(options.formatEnvelope, options.adapterContext));
4
+ const payload = (await options.formatAdapter.buildRequest(options.formatEnvelope, options.adapterContext));
5
5
  stripPrivateFields(payload);
6
- recordStage(options.stageRecorder, 'req_outbound_stage2_format_build', payload);
6
+ recordStage(options.stageRecorder, 'chat_process.req.stage7.outbound.format_build', payload);
7
7
  return payload;
8
8
  }
9
9
  function stripPrivateFields(value) {
@@ -6,7 +6,7 @@ function pickCompatProfile(adapterContext) {
6
6
  export async function runReqOutboundStage3Compat(options) {
7
7
  const profile = pickCompatProfile(options.adapterContext);
8
8
  const result = applyRequestCompat(profile, options.payload, { adapterContext: options.adapterContext });
9
- options.stageRecorder?.record('req_outbound_stage3_compat', {
9
+ options.stageRecorder?.record('chat_process.req.stage8.outbound.compat', {
10
10
  applied: Boolean(result.appliedProfile),
11
11
  profile: result.appliedProfile || profile || 'passthrough'
12
12
  });
@@ -15,7 +15,7 @@ export async function runReqOutboundStage3Compat(options) {
15
15
  export function runRespInboundStageCompatResponse(options) {
16
16
  const profile = pickCompatProfile(options.adapterContext);
17
17
  const result = applyResponseCompat(profile, options.payload, { adapterContext: options.adapterContext });
18
- options.stageRecorder?.record('resp_inbound_stage_compat', {
18
+ options.stageRecorder?.record('chat_process.resp.stage3.compat', {
19
19
  applied: Boolean(result.appliedProfile),
20
20
  profile: result.appliedProfile || profile || 'passthrough'
21
21
  });
@@ -10,7 +10,7 @@ export async function runReqProcessStage1ToolGovernance(options) {
10
10
  requestId: options.requestId
11
11
  });
12
12
  if (result.processedRequest) {
13
- recordStage(options.stageRecorder, 'req_process_stage1_tool_governance', result.processedRequest);
13
+ recordStage(options.stageRecorder, 'chat_process.req.stage4.tool_governance', result.processedRequest);
14
14
  // Best-effort: capture apply_patch execution failures reported by tool role messages.
15
15
  // This is for errorsamples collection only and must not affect runtime behavior.
16
16
  captureApplyPatchExecutionFailuresFromProcessedRequest(result.processedRequest);
@@ -5,7 +5,7 @@ export function runReqProcessStage2RouteSelect(options) {
5
5
  const result = options.routerEngine.route(options.request, options.metadataInput);
6
6
  applyTargetMetadata(options.normalizedMetadata, result.target, result.decision.routeName, previousModel);
7
7
  applyTargetToSubject(options.request, result.target, previousModel);
8
- recordStage(options.stageRecorder, 'req_process_stage2_route_select', {
8
+ recordStage(options.stageRecorder, 'chat_process.req.stage5.route_select', {
9
9
  target: result.target,
10
10
  decision: result.decision,
11
11
  diagnostics: result.diagnostics
@@ -43,43 +43,43 @@ export async function runRespInboundStage1SseDecode(options) {
43
43
  // 某些 mock-provider / 捕获样本在 SSE 连接被异常终止时会携带 error 标记,
44
44
  // 即使仍保留 __sse_responses 流,也应视为上游异常并终止。
45
45
  if (wrapperError) {
46
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
46
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
47
47
  streamDetected: Boolean(stream),
48
48
  decoded: false,
49
49
  protocol: options.providerProtocol,
50
50
  reason: 'sse_wrapper_error',
51
51
  error: wrapperError
52
52
  });
53
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Upstream SSE terminated: ${wrapperError}`, {
53
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Upstream SSE terminated: ${wrapperError}`, {
54
54
  code: 'SSE_DECODE_ERROR',
55
55
  protocol: options.providerProtocol,
56
56
  providerType: resolveProviderType(options.providerProtocol),
57
57
  details: {
58
- phase: 'resp_inbound_stage1_sse_decode',
58
+ phase: 'chat_process.resp.stage1.sse_decode',
59
59
  requestId: options.adapterContext.requestId,
60
60
  message: wrapperError
61
61
  }
62
62
  });
63
63
  }
64
64
  if (!stream) {
65
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
65
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
66
66
  streamDetected: false
67
67
  });
68
68
  return { payload: options.payload, decodedFromSse: false };
69
69
  }
70
70
  if (!supportsSseProtocol(options.providerProtocol)) {
71
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
71
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
72
72
  streamDetected: true,
73
73
  decoded: false,
74
74
  reason: 'protocol_unsupported',
75
75
  protocol: options.providerProtocol
76
76
  });
77
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Protocol ${options.providerProtocol} does not support SSE decoding`, {
77
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Protocol ${options.providerProtocol} does not support SSE decoding`, {
78
78
  code: 'SSE_DECODE_ERROR',
79
79
  protocol: options.providerProtocol,
80
80
  providerType: resolveProviderType(options.providerProtocol),
81
81
  details: {
82
- phase: 'resp_inbound_stage1_sse_decode',
82
+ phase: 'chat_process.resp.stage1.sse_decode',
83
83
  reason: 'protocol_unsupported'
84
84
  }
85
85
  });
@@ -90,7 +90,7 @@ export async function runRespInboundStage1SseDecode(options) {
90
90
  requestId: options.adapterContext.requestId,
91
91
  model: options.adapterContext.modelId
92
92
  }));
93
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
93
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
94
94
  streamDetected: true,
95
95
  decoded: true,
96
96
  protocol: options.providerProtocol
@@ -99,18 +99,18 @@ export async function runRespInboundStage1SseDecode(options) {
99
99
  }
100
100
  catch (error) {
101
101
  const message = error instanceof Error ? error.message : String(error);
102
- recordStage(options.stageRecorder, 'resp_inbound_stage1_sse_decode', {
102
+ recordStage(options.stageRecorder, 'chat_process.resp.stage1.sse_decode', {
103
103
  streamDetected: true,
104
104
  decoded: false,
105
105
  protocol: options.providerProtocol,
106
106
  error: message
107
107
  });
108
- throw new ProviderProtocolError(`[resp_inbound_stage1_sse_decode] Failed to decode SSE payload for protocol ${options.providerProtocol}: ${message}`, {
108
+ throw new ProviderProtocolError(`[chat_process.resp.stage1.sse_decode] Failed to decode SSE payload for protocol ${options.providerProtocol}: ${message}`, {
109
109
  code: 'SSE_DECODE_ERROR',
110
110
  protocol: options.providerProtocol,
111
111
  providerType: resolveProviderType(options.providerProtocol),
112
112
  details: {
113
- phase: 'resp_inbound_stage1_sse_decode',
113
+ phase: 'chat_process.resp.stage1.sse_decode',
114
114
  requestId: options.adapterContext.requestId,
115
115
  message
116
116
  }
@@ -1,6 +1,6 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runRespInboundStage2FormatParse(options) {
3
3
  const envelope = (await options.formatAdapter.parseResponse(options.payload, options.adapterContext));
4
- recordStage(options.stageRecorder, 'resp_inbound_stage2_format_parse', envelope);
4
+ recordStage(options.stageRecorder, 'chat_process.resp.stage2.format_parse', envelope);
5
5
  return envelope;
6
6
  }
@@ -7,6 +7,7 @@ export interface RespInboundStage3SemanticMapOptions {
7
7
  adapterContext: AdapterContext;
8
8
  formatEnvelope: FormatEnvelope<JsonObject>;
9
9
  mapper: ResponseMapper;
10
+ requestSemantics?: JsonObject;
10
11
  stageRecorder?: StageRecorder;
11
12
  }
12
13
  export declare function runRespInboundStage3SemanticMap(options: RespInboundStage3SemanticMapOptions): Promise<ChatCompletionLike>;
@@ -1,6 +1,8 @@
1
1
  import { recordStage } from '../../../stages/utils.js';
2
2
  export async function runRespInboundStage3SemanticMap(options) {
3
- const chatResponse = await options.mapper.toChatCompletion(options.formatEnvelope, options.adapterContext);
4
- recordStage(options.stageRecorder, 'resp_inbound_stage3_semantic_map', chatResponse);
3
+ const chatResponse = await options.mapper.toChatCompletion(options.formatEnvelope, options.adapterContext, {
4
+ requestSemantics: options.requestSemantics
5
+ });
6
+ recordStage(options.stageRecorder, 'chat_process.resp.stage4.semantic_map_to_chat', chatResponse);
5
7
  return chatResponse;
6
8
  }
@@ -7,6 +7,7 @@ export interface RespOutboundStage1ClientRemapOptions {
7
7
  clientProtocol: ClientProtocol;
8
8
  requestId: string;
9
9
  adapterContext?: AdapterContext;
10
+ requestSemantics?: JsonObject;
10
11
  stageRecorder?: StageRecorder;
11
12
  }
12
13
  export declare function runRespOutboundStage1ClientRemap(options: RespOutboundStage1ClientRemapOptions): JsonObject;
@@ -8,24 +8,28 @@ export function runRespOutboundStage1ClientRemap(options) {
8
8
  }
9
9
  else if (options.clientProtocol === 'anthropic-messages') {
10
10
  clientPayload = buildAnthropicResponseFromChat(options.payload, {
11
- aliasMap: resolveAliasMapFromContext(options.adapterContext)
11
+ aliasMap: resolveAliasMapFromSemantics(options.requestSemantics)
12
12
  });
13
13
  }
14
14
  else {
15
- const toolsRaw = resolveClientToolsRawFromContext(options.adapterContext);
15
+ const toolsRaw = resolveClientToolsRawFromSemantics(options.requestSemantics);
16
16
  clientPayload = buildResponsesPayloadFromChat(options.payload, {
17
17
  requestId: options.requestId,
18
18
  ...(toolsRaw ? { toolsRaw } : {})
19
19
  });
20
20
  }
21
- recordStage(options.stageRecorder, 'resp_outbound_stage1_client_remap', clientPayload);
21
+ recordStage(options.stageRecorder, 'chat_process.resp.stage9.client_remap', clientPayload);
22
22
  return clientPayload;
23
23
  }
24
- function resolveAliasMapFromContext(adapterContext) {
25
- if (!adapterContext) {
24
+ function resolveAliasMapFromSemantics(semantics) {
25
+ if (!semantics || typeof semantics !== 'object' || Array.isArray(semantics)) {
26
26
  return undefined;
27
27
  }
28
- const candidate = adapterContext.anthropicToolNameMap;
28
+ const toolsNode = semantics.tools;
29
+ if (!toolsNode || typeof toolsNode !== 'object' || Array.isArray(toolsNode)) {
30
+ return undefined;
31
+ }
32
+ const candidate = toolsNode.toolNameAliasMap;
29
33
  if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) {
30
34
  return undefined;
31
35
  }
@@ -43,11 +47,15 @@ function resolveAliasMapFromContext(adapterContext) {
43
47
  }
44
48
  return Object.keys(map).length ? map : undefined;
45
49
  }
46
- function resolveClientToolsRawFromContext(adapterContext) {
47
- if (!adapterContext) {
50
+ function resolveClientToolsRawFromSemantics(semantics) {
51
+ if (!semantics || typeof semantics !== 'object' || Array.isArray(semantics)) {
52
+ return undefined;
53
+ }
54
+ const toolsNode = semantics.tools;
55
+ if (!toolsNode || typeof toolsNode !== 'object' || Array.isArray(toolsNode)) {
48
56
  return undefined;
49
57
  }
50
- const candidate = adapterContext.clientToolsRaw;
58
+ const candidate = toolsNode.clientToolsRaw;
51
59
  if (!candidate || !Array.isArray(candidate)) {
52
60
  return undefined;
53
61
  }
@@ -2,7 +2,7 @@ import { defaultSseCodecRegistry } from '../../../../../../sse/index.js';
2
2
  import { recordStage } from '../../../stages/utils.js';
3
3
  export async function runRespOutboundStage2SseStream(options) {
4
4
  if (!options.wantsStream) {
5
- recordStage(options.stageRecorder, 'resp_outbound_stage2_sse_stream', {
5
+ recordStage(options.stageRecorder, 'chat_process.resp.stage10.sse_stream', {
6
6
  passthrough: false,
7
7
  protocol: options.clientProtocol,
8
8
  payload: options.clientPayload
@@ -13,7 +13,7 @@ export async function runRespOutboundStage2SseStream(options) {
13
13
  const stream = await codec.convertJsonToSse(options.clientPayload, {
14
14
  requestId: options.requestId
15
15
  });
16
- recordStage(options.stageRecorder, 'resp_outbound_stage2_sse_stream', {
16
+ recordStage(options.stageRecorder, 'chat_process.resp.stage10.sse_stream', {
17
17
  passthrough: false,
18
18
  protocol: options.clientProtocol,
19
19
  payload: options.clientPayload
@@ -1,17 +1,55 @@
1
1
  import { runChatResponseToolFilters } from '../../../../../shared/tool-filter-pipeline.js';
2
2
  import { normalizeApplyPatchToolCallsOnResponse } from '../../../../../shared/tool-governor.js';
3
+ import { buildChatResponseFromResponses } from '../../../../../shared/responses-response-utils.js';
3
4
  import { ToolGovernanceEngine } from '../../../../tool-governance/index.js';
4
5
  import { recordStage } from '../../../stages/utils.js';
5
6
  const toolGovernanceEngine = new ToolGovernanceEngine();
7
+ function isCanonicalChatCompletion(payload) {
8
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
9
+ return false;
10
+ }
11
+ const obj = payload;
12
+ const choices = Array.isArray(obj.choices) ? obj.choices : [];
13
+ if (!choices.length)
14
+ return false;
15
+ const first = choices[0] && typeof choices[0] === 'object' && !Array.isArray(choices[0]) ? choices[0] : null;
16
+ if (!first)
17
+ return false;
18
+ const msg = first.message;
19
+ return Boolean(msg && typeof msg === 'object' && !Array.isArray(msg));
20
+ }
21
+ function coerceToCanonicalChatCompletion(payload) {
22
+ if (isCanonicalChatCompletion(payload)) {
23
+ return payload;
24
+ }
25
+ // ServerTool followups may re-enter via the client protocol shape (e.g. OpenAI Responses object:'response').
26
+ // Response tool governance requires an OpenAI-chat-like surface (choices[].message) so text tool harvesting
27
+ // and tool governance remain a single fixed path.
28
+ try {
29
+ const coerced = buildChatResponseFromResponses(payload);
30
+ if (isCanonicalChatCompletion(coerced)) {
31
+ return coerced;
32
+ }
33
+ }
34
+ catch {
35
+ // best-effort: keep original payload when bridge fails
36
+ }
37
+ return payload;
38
+ }
6
39
  export async function runRespProcessStage1ToolGovernance(options) {
7
- const filtered = await runChatResponseToolFilters(options.payload, {
40
+ const canonicalInput = coerceToCanonicalChatCompletion(options.payload);
41
+ recordStage(options.stageRecorder, 'chat_process.resp.stage6.canonicalize_chat_completion', {
42
+ converted: canonicalInput !== options.payload,
43
+ canonicalPayload: canonicalInput
44
+ });
45
+ const filtered = await runChatResponseToolFilters(canonicalInput, {
8
46
  entryEndpoint: options.entryEndpoint,
9
47
  requestId: options.requestId,
10
48
  profile: 'openai-chat'
11
49
  });
12
50
  const patched = normalizeApplyPatchToolCallsOnResponse(filtered);
13
51
  const { payload: governed, summary } = toolGovernanceEngine.governResponse(patched, options.clientProtocol);
14
- recordStage(options.stageRecorder, 'resp_process_stage1_tool_governance', {
52
+ recordStage(options.stageRecorder, 'chat_process.resp.stage7.tool_governance', {
15
53
  summary,
16
54
  applied: summary?.applied,
17
55
  filteredPayload: patched,
@@ -11,7 +11,7 @@ export async function runRespProcessStage2Finalize(options) {
11
11
  const processedRequest = buildProcessedRequestFromChatResponse(finalized, {
12
12
  stream: options.wantsStream
13
13
  });
14
- recordStage(options.stageRecorder, 'resp_process_stage2_finalize', {
14
+ recordStage(options.stageRecorder, 'chat_process.resp.stage8.finalize', {
15
15
  model: finalized.model,
16
16
  stream: options.wantsStream
17
17
  });
@@ -1,3 +1,4 @@
1
+ import { ensureRuntimeMetadata } from '../../shared/runtime-metadata.js';
1
2
  export function applyTargetMetadata(metadata, target, routeName, originalModel) {
2
3
  if (!metadata || typeof metadata !== 'object') {
3
4
  return;
@@ -9,11 +10,14 @@ export function applyTargetMetadata(metadata, target, routeName, originalModel)
9
10
  metadata.providerType = target.providerType;
10
11
  metadata.modelId = target.modelId;
11
12
  metadata.processMode = target.processMode || 'chat';
12
- if (target.forceWebSearch === true) {
13
- metadata.forceWebSearch = true;
14
- }
15
- if (target.forceVision === true) {
16
- metadata.forceVision = true;
13
+ if (target.forceWebSearch === true || target.forceVision === true) {
14
+ const rt = ensureRuntimeMetadata(metadata);
15
+ if (target.forceWebSearch === true) {
16
+ rt.forceWebSearch = true;
17
+ }
18
+ if (target.forceVision === true) {
19
+ rt.forceVision = true;
20
+ }
17
21
  }
18
22
  if (target.responsesConfig?.toolCallIdStyle) {
19
23
  metadata.toolCallIdStyle = target.responsesConfig.toolCallIdStyle;