@jsonstudio/llms 0.6.568 → 0.6.586

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 (25) hide show
  1. package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +0 -1
  2. package/dist/conversion/hub/pipeline/hub-pipeline.js +47 -68
  3. package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +0 -34
  4. package/dist/conversion/hub/process/chat-process.js +3 -13
  5. package/dist/conversion/hub/response/provider-response.js +0 -8
  6. package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +7 -1
  7. package/dist/conversion/hub/semantic-mappers/responses-mapper.js +216 -12
  8. package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -2
  9. package/dist/conversion/responses/responses-openai-bridge.js +1 -13
  10. package/dist/conversion/shared/text-markup-normalizer.d.ts +20 -0
  11. package/dist/conversion/shared/text-markup-normalizer.js +76 -4
  12. package/dist/conversion/shared/tool-filter-pipeline.js +54 -29
  13. package/dist/filters/special/response-apply-patch-toon-decode.js +15 -7
  14. package/dist/filters/special/response-tool-arguments-toon-decode.js +108 -22
  15. package/dist/guidance/index.js +2 -0
  16. package/dist/router/virtual-router/classifier.js +16 -12
  17. package/dist/router/virtual-router/engine.js +30 -0
  18. package/dist/router/virtual-router/tool-signals.d.ts +2 -1
  19. package/dist/router/virtual-router/tool-signals.js +293 -134
  20. package/dist/router/virtual-router/types.d.ts +1 -1
  21. package/dist/router/virtual-router/types.js +1 -1
  22. package/dist/sse/json-to-sse/event-generators/responses.js +9 -2
  23. package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +7 -3
  24. package/dist/tools/apply-patch-structured.js +4 -3
  25. package/package.json +2 -2
@@ -61,7 +61,6 @@ export declare class HubPipeline {
61
61
  private buildAdapterContext;
62
62
  private maybeCreateStageRecorder;
63
63
  private asJsonObject;
64
- private pickRawRequestBody;
65
64
  private normalizeRequest;
66
65
  private convertProcessNodeResult;
67
66
  private materializePayload;
@@ -22,6 +22,7 @@ import { runReqOutboundStage1SemanticMap } from './stages/req_outbound/req_outbo
22
22
  import { runReqOutboundStage2FormatBuild } from './stages/req_outbound/req_outbound_stage2_format_build/index.js';
23
23
  import { runReqOutboundStage3Compat } from './stages/req_outbound/req_outbound_stage3_compat/index.js';
24
24
  import { extractSessionIdentifiersFromMetadata } from './session-identifiers.js';
25
+ import { computeRequestTokens } from '../../../router/virtual-router/token-estimator.js';
25
26
  export class HubPipeline {
26
27
  routerEngine;
27
28
  config;
@@ -119,6 +120,18 @@ export class HubPipeline {
119
120
  }
120
121
  }
121
122
  const workingRequest = processedRequest ?? standardizedRequest;
123
+ // 使用与 VirtualRouter 一致的 tiktoken 计数逻辑,对标准化请求进行一次
124
+ // 上下文 token 估算,供后续 usage 归一化与统计使用。
125
+ try {
126
+ const estimatedTokens = computeRequestTokens(workingRequest, '');
127
+ if (typeof estimatedTokens === 'number' && Number.isFinite(estimatedTokens) && estimatedTokens > 0) {
128
+ normalized.metadata = normalized.metadata || {};
129
+ normalized.metadata.estimatedInputTokens = estimatedTokens;
130
+ }
131
+ }
132
+ catch {
133
+ // 估算失败不应影响主流程
134
+ }
122
135
  const normalizedMeta = normalized.metadata;
123
136
  const responsesResume = normalizedMeta && typeof normalizedMeta.responsesResume === 'object'
124
137
  ? normalizedMeta.responsesResume
@@ -182,66 +195,42 @@ export class HubPipeline {
182
195
  const outboundEndpoint = resolveEndpointForProviderProtocol(outboundAdapterContext.providerProtocol);
183
196
  const outboundRecorder = this.maybeCreateStageRecorder(outboundAdapterContext, outboundEndpoint);
184
197
  const outboundStart = Date.now();
185
- const isResponsesSubmit = normalized.entryEndpoint === '/v1/responses.submit_tool_outputs' &&
186
- outboundProtocol === 'openai-responses';
187
198
  let providerPayload;
188
- if (isResponsesSubmit) {
189
- providerPayload =
190
- this.pickRawRequestBody(normalized.metadata) ?? normalized.payload;
191
- const outboundEnd = Date.now();
192
- nodeResults.push({
193
- id: 'req_outbound',
194
- success: true,
195
- metadata: {
196
- node: 'req_outbound',
197
- executionTime: outboundEnd - outboundStart,
198
- startTime: outboundStart,
199
- endTime: outboundEnd,
200
- dataProcessed: {
201
- messages: 0,
202
- tools: 0
203
- },
204
- bypass: 'responses-submit-passthrough'
205
- }
206
- });
207
- }
208
- else {
209
- const outboundStage1 = await runReqOutboundStage1SemanticMap({
210
- request: workingRequest,
211
- adapterContext: outboundAdapterContext,
212
- semanticMapper: outboundSemanticMapper,
213
- contextSnapshot: outboundContextSnapshot,
214
- contextMetadataKey: outboundContextMetadataKey,
215
- stageRecorder: outboundRecorder
216
- });
217
- let formattedPayload = await runReqOutboundStage2FormatBuild({
218
- formatEnvelope: outboundStage1.formatEnvelope,
219
- adapterContext: outboundAdapterContext,
220
- formatAdapter: outboundFormatAdapter,
221
- stageRecorder: outboundRecorder
222
- });
223
- formattedPayload = await runReqOutboundStage3Compat({
224
- payload: formattedPayload,
225
- adapterContext: outboundAdapterContext,
226
- stageRecorder: outboundRecorder
227
- });
228
- providerPayload = formattedPayload;
229
- const outboundEnd = Date.now();
230
- nodeResults.push({
231
- id: 'req_outbound',
232
- success: true,
233
- metadata: {
234
- node: 'req_outbound',
235
- executionTime: outboundEnd - outboundStart,
236
- startTime: outboundStart,
237
- endTime: outboundEnd,
238
- dataProcessed: {
239
- messages: workingRequest.messages.length,
240
- tools: workingRequest.tools?.length ?? 0
241
- }
199
+ const outboundStage1 = await runReqOutboundStage1SemanticMap({
200
+ request: workingRequest,
201
+ adapterContext: outboundAdapterContext,
202
+ semanticMapper: outboundSemanticMapper,
203
+ contextSnapshot: outboundContextSnapshot,
204
+ contextMetadataKey: outboundContextMetadataKey,
205
+ stageRecorder: outboundRecorder
206
+ });
207
+ let formattedPayload = await runReqOutboundStage2FormatBuild({
208
+ formatEnvelope: outboundStage1.formatEnvelope,
209
+ adapterContext: outboundAdapterContext,
210
+ formatAdapter: outboundFormatAdapter,
211
+ stageRecorder: outboundRecorder
212
+ });
213
+ formattedPayload = await runReqOutboundStage3Compat({
214
+ payload: formattedPayload,
215
+ adapterContext: outboundAdapterContext,
216
+ stageRecorder: outboundRecorder
217
+ });
218
+ providerPayload = formattedPayload;
219
+ const outboundEnd = Date.now();
220
+ nodeResults.push({
221
+ id: 'req_outbound',
222
+ success: true,
223
+ metadata: {
224
+ node: 'req_outbound',
225
+ executionTime: outboundEnd - outboundStart,
226
+ startTime: outboundStart,
227
+ endTime: outboundEnd,
228
+ dataProcessed: {
229
+ messages: workingRequest.messages.length,
230
+ tools: workingRequest.tools?.length ?? 0
242
231
  }
243
- });
244
- }
232
+ }
233
+ });
245
234
  // 为响应侧 servertool/web_search 提供一次性 Chat 请求快照,便于在 Hub 内部实现
246
235
  // 第三跳(将工具结果注入消息历史后重新调用主模型)。
247
236
  //
@@ -432,16 +421,6 @@ export class HubPipeline {
432
421
  }
433
422
  return value;
434
423
  }
435
- pickRawRequestBody(metadata) {
436
- if (!metadata || typeof metadata !== 'object') {
437
- return undefined;
438
- }
439
- const raw = metadata.__raw_request_body;
440
- if (!raw || typeof raw !== 'object') {
441
- return undefined;
442
- }
443
- return raw;
444
- }
445
424
  async normalizeRequest(request) {
446
425
  if (!request || typeof request !== 'object') {
447
426
  throw new Error('HubPipeline requires request payload');
@@ -15,7 +15,6 @@ export function runRespOutboundStage1ClientRemap(options) {
15
15
  clientPayload = buildResponsesPayloadFromChat(options.payload, {
16
16
  requestId: options.requestId
17
17
  });
18
- mergeOriginalResponsesPayload(clientPayload, options.adapterContext);
19
18
  }
20
19
  recordStage(options.stageRecorder, 'resp_outbound_stage1_client_remap', clientPayload);
21
20
  return clientPayload;
@@ -42,36 +41,3 @@ function resolveAliasMapFromContext(adapterContext) {
42
41
  }
43
42
  return Object.keys(map).length ? map : undefined;
44
43
  }
45
- function mergeOriginalResponsesPayload(payload, adapterContext) {
46
- if (!adapterContext) {
47
- return;
48
- }
49
- const raw = adapterContext.__raw_responses_payload;
50
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
51
- return;
52
- }
53
- try {
54
- if (payload.required_action == null && raw.required_action != null) {
55
- payload.required_action = JSON.parse(JSON.stringify(raw.required_action));
56
- }
57
- }
58
- catch {
59
- /* ignore clone errors */
60
- }
61
- const rawStatus = typeof raw.status === 'string' ? raw.status : undefined;
62
- if (rawStatus === 'requires_action') {
63
- payload.status = 'requires_action';
64
- }
65
- // 如果桥接后的 payload 没有 usage,而原始 Responses 载荷带有 usage,则回填原始 usage,
66
- // 确保 token usage 不在工具/桥接路径中丢失。
67
- const payloadUsage = payload.usage;
68
- const rawUsage = raw.usage;
69
- if ((payloadUsage == null || typeof payloadUsage !== 'object') && rawUsage && typeof rawUsage === 'object') {
70
- try {
71
- payload.usage = JSON.parse(JSON.stringify(rawUsage));
72
- }
73
- catch {
74
- payload.usage = rawUsage;
75
- }
76
- }
77
- }
@@ -1,6 +1,5 @@
1
1
  import { runChatRequestToolFilters } from '../../shared/tool-filter-pipeline.js';
2
2
  import { ToolGovernanceEngine } from '../tool-governance/index.js';
3
- import { detectLastAssistantToolCategory } from '../../../router/virtual-router/tool-signals.js';
4
3
  import { ensureApplyPatchSchema } from '../../shared/tool-mapping.js';
5
4
  const toolGovernanceEngine = new ToolGovernanceEngine();
6
5
  export async function runHubChatProcess(options) {
@@ -409,19 +408,10 @@ function maybeInjectWebSearchTool(request, metadata) {
409
408
  : 'selective';
410
409
  const intent = detectWebSearchIntent(request);
411
410
  if (injectPolicy === 'selective') {
411
+ // 仅当当前这一轮用户输入明确表达“联网搜索”意图时才注入 web_search。
412
+ // 不再依赖上一轮工具分类(read/search/websearch),避免形成隐式续写语义。
412
413
  if (!intent.hasIntent) {
413
- // 当最近一条用户消息没有明显的“联网搜索”关键词时,
414
- // 如果上一轮 assistant 的工具调用已经属于搜索类(如 web_search),
415
- // 则仍然视为 web_search 续写场景,强制注入 web_search 工具,
416
- // 以便在后续路由中按 servertool 逻辑跳过不适配的 Provider(例如 serverToolsDisabled 的 crs)。
417
- const assistantMessages = Array.isArray(request.messages)
418
- ? request.messages.filter((msg) => msg && msg.role === 'assistant')
419
- : [];
420
- const lastTool = detectLastAssistantToolCategory(assistantMessages);
421
- const hasSearchToolContext = lastTool?.category === 'search';
422
- if (!hasSearchToolContext) {
423
- return request;
424
- }
414
+ return request;
425
415
  }
426
416
  }
427
417
  const existingTools = Array.isArray(request.tools) ? request.tools : [];
@@ -130,14 +130,6 @@ export async function convertProviderResponse(options) {
130
130
  catch {
131
131
  // ignore conversation capture errors
132
132
  }
133
- if (formatEnvelope.payload && typeof formatEnvelope.payload === 'object') {
134
- try {
135
- options.context.__raw_responses_payload = JSON.parse(JSON.stringify(formatEnvelope.payload));
136
- }
137
- catch {
138
- /* best-effort clone */
139
- }
140
- }
141
133
  }
142
134
  formatEnvelope.payload = runRespInboundStageCompatResponse({
143
135
  payload: formatEnvelope.payload,
@@ -337,7 +337,13 @@ function buildGeminiRequestFromChat(chat, metadata) {
337
337
  else {
338
338
  argsStruct = fn.arguments ?? {};
339
339
  }
340
- const functionCall = { name, args: cloneAsJsonValue(argsStruct) };
340
+ let argsJson = cloneAsJsonValue(argsStruct);
341
+ // Gemini / Antigravity 期望 functionCall.args 为对象(Struct),
342
+ // 若顶层为数组或原始类型,则包装到 value 字段下,避免产生非法的 list 形状。
343
+ if (!argsJson || typeof argsJson !== 'object' || Array.isArray(argsJson)) {
344
+ argsJson = { value: argsJson };
345
+ }
346
+ const functionCall = { name, args: argsJson };
341
347
  const part = { functionCall };
342
348
  if (typeof tc.id === 'string') {
343
349
  part.functionCall.id = tc.id;
@@ -19,6 +19,7 @@ const RESPONSES_PARAMETER_KEYS = [
19
19
  'stop_sequences',
20
20
  'modalities'
21
21
  ];
22
+ const RESPONSES_SUBMIT_ENDPOINT = '/v1/responses.submit_tool_outputs';
22
23
  function mapToolOutputs(entries, missing) {
23
24
  if (!entries || !entries.length)
24
25
  return undefined;
@@ -197,6 +198,198 @@ function mergeMetadata(a, b) {
197
198
  const right = jsonClone(b);
198
199
  return { ...left, ...right };
199
200
  }
201
+ function isSubmitToolOutputsEndpoint(ctx) {
202
+ if (!ctx || typeof ctx !== 'object') {
203
+ return false;
204
+ }
205
+ const entry = typeof ctx.entryEndpoint === 'string' ? ctx.entryEndpoint.trim().toLowerCase() : '';
206
+ return entry === RESPONSES_SUBMIT_ENDPOINT;
207
+ }
208
+ function resolveSubmitResponseId(ctx, responsesContext) {
209
+ const resumeMeta = ctx.responsesResume && typeof ctx.responsesResume === 'object'
210
+ ? ctx.responsesResume
211
+ : undefined;
212
+ const resumeId = typeof resumeMeta?.restoredFromResponseId === 'string'
213
+ ? resumeMeta.restoredFromResponseId.trim()
214
+ : undefined;
215
+ if (resumeId) {
216
+ return resumeId;
217
+ }
218
+ const contextRecord = responsesContext && typeof responsesContext === 'object'
219
+ ? responsesContext
220
+ : undefined;
221
+ const previousId = typeof contextRecord?.previous_response_id === 'string'
222
+ ? contextRecord.previous_response_id.trim()
223
+ : undefined;
224
+ if (previousId) {
225
+ return previousId;
226
+ }
227
+ return undefined;
228
+ }
229
+ function extractSubmitMetadata(responsesContext, resumeMeta) {
230
+ if (responsesContext && responsesContext.metadata && isJsonObject(responsesContext.metadata)) {
231
+ return jsonClone(responsesContext.metadata);
232
+ }
233
+ if (resumeMeta && resumeMeta.metadata && isJsonObject(resumeMeta.metadata)) {
234
+ return jsonClone(resumeMeta.metadata);
235
+ }
236
+ return undefined;
237
+ }
238
+ function coerceOutputText(value) {
239
+ if (typeof value === 'string') {
240
+ return value;
241
+ }
242
+ if (value === undefined || value === null) {
243
+ return '';
244
+ }
245
+ try {
246
+ return JSON.stringify(value);
247
+ }
248
+ catch {
249
+ return String(value);
250
+ }
251
+ }
252
+ function extractCapturedToolOutputs(responsesContext) {
253
+ if (!responsesContext || typeof responsesContext !== 'object') {
254
+ return [];
255
+ }
256
+ const snapshot = responsesContext.__captured_tool_results;
257
+ if (!Array.isArray(snapshot) || !snapshot.length) {
258
+ return [];
259
+ }
260
+ const entries = [];
261
+ snapshot.forEach((entry) => {
262
+ if (!entry || typeof entry !== 'object') {
263
+ return;
264
+ }
265
+ const record = entry;
266
+ const toolId = typeof record.tool_call_id === 'string' && record.tool_call_id.trim().length
267
+ ? record.tool_call_id.trim()
268
+ : typeof record.call_id === 'string' && record.call_id.trim().length
269
+ ? record.call_id.trim()
270
+ : undefined;
271
+ if (!toolId) {
272
+ return;
273
+ }
274
+ entries.push({
275
+ tool_call_id: toolId,
276
+ id: toolId,
277
+ output: typeof record.output === 'string' ? record.output : coerceOutputText(record.output),
278
+ name: typeof record.name === 'string' ? record.name : undefined
279
+ });
280
+ });
281
+ return entries;
282
+ }
283
+ function collectSubmitToolOutputs(chat, ctx, responsesContext) {
284
+ const outputs = [];
285
+ const seen = new Set();
286
+ const append = (idSeed, outputSeed, name) => {
287
+ const trimmed = typeof idSeed === 'string' && idSeed.trim().length ? idSeed.trim() : '';
288
+ const fallbackId = trimmed || `submit_tool_${outputs.length + 1}`;
289
+ if (seen.has(fallbackId)) {
290
+ return;
291
+ }
292
+ seen.add(fallbackId);
293
+ outputs.push({
294
+ tool_call_id: fallbackId,
295
+ id: fallbackId,
296
+ output: coerceOutputText(outputSeed),
297
+ name
298
+ });
299
+ };
300
+ if (Array.isArray(chat.toolOutputs) && chat.toolOutputs.length) {
301
+ chat.toolOutputs.forEach((entry) => {
302
+ append(entry.tool_call_id ?? entry.call_id, entry.content, entry.name);
303
+ });
304
+ }
305
+ if (!outputs.length) {
306
+ const captured = extractCapturedToolOutputs(responsesContext);
307
+ captured.forEach((entry) => append(entry.tool_call_id ?? entry.id, entry.output, entry.name));
308
+ }
309
+ if (!outputs.length) {
310
+ const resume = ctx.responsesResume && typeof ctx.responsesResume === 'object'
311
+ ? ctx.responsesResume
312
+ : undefined;
313
+ const detailed = Array.isArray(resume?.toolOutputsDetailed)
314
+ ? resume?.toolOutputsDetailed
315
+ : undefined;
316
+ if (detailed) {
317
+ detailed.forEach((entry, index) => {
318
+ if (!entry || typeof entry !== 'object') {
319
+ return;
320
+ }
321
+ const callId = typeof entry.callId === 'string' && entry.callId.trim().length
322
+ ? entry.callId.trim()
323
+ : typeof entry.originalId === 'string' && entry.originalId.trim().length
324
+ ? entry.originalId.trim()
325
+ : `resume_tool_${index + 1}`;
326
+ append(callId, typeof entry.outputText === 'string' ? entry.outputText : entry.outputText ?? '');
327
+ });
328
+ }
329
+ }
330
+ return outputs;
331
+ }
332
+ function resolveSubmitStreamFlag(chat, ctx, responsesContext) {
333
+ if (chat.parameters && typeof chat.parameters.stream === 'boolean') {
334
+ return chat.parameters.stream;
335
+ }
336
+ if (responsesContext && typeof responsesContext.stream === 'boolean') {
337
+ return responsesContext.stream;
338
+ }
339
+ if (ctx.streamingHint === 'force') {
340
+ return true;
341
+ }
342
+ if (ctx.streamingHint === 'disable') {
343
+ return false;
344
+ }
345
+ return undefined;
346
+ }
347
+ function resolveSubmitModel(chat, responsesContext) {
348
+ const direct = chat.parameters && typeof chat.parameters.model === 'string'
349
+ ? chat.parameters.model.trim()
350
+ : undefined;
351
+ if (direct) {
352
+ return direct;
353
+ }
354
+ if (responsesContext && typeof responsesContext.parameters === 'object') {
355
+ const params = responsesContext.parameters;
356
+ const model = typeof params.model === 'string' ? params.model.trim() : undefined;
357
+ if (model) {
358
+ return model;
359
+ }
360
+ }
361
+ return undefined;
362
+ }
363
+ function buildSubmitToolOutputsPayload(chat, ctx, responsesContext) {
364
+ const responseId = resolveSubmitResponseId(ctx, responsesContext);
365
+ if (!responseId) {
366
+ throw new Error('Submit tool outputs requires response_id from Responses resume context');
367
+ }
368
+ const toolOutputs = collectSubmitToolOutputs(chat, ctx, responsesContext);
369
+ if (!toolOutputs.length) {
370
+ throw new Error('Submit tool outputs requires at least one tool output entry');
371
+ }
372
+ const resumeMeta = ctx.responsesResume && typeof ctx.responsesResume === 'object'
373
+ ? ctx.responsesResume
374
+ : undefined;
375
+ const payload = {
376
+ response_id: responseId,
377
+ tool_outputs: toolOutputs
378
+ };
379
+ const modelValue = resolveSubmitModel(chat, responsesContext);
380
+ if (modelValue) {
381
+ payload.model = modelValue;
382
+ }
383
+ const streamValue = resolveSubmitStreamFlag(chat, ctx, responsesContext);
384
+ if (typeof streamValue === 'boolean') {
385
+ payload.stream = streamValue;
386
+ }
387
+ const metadata = extractSubmitMetadata(responsesContext, resumeMeta);
388
+ if (metadata) {
389
+ payload.metadata = metadata;
390
+ }
391
+ return payload;
392
+ }
200
393
  export class ResponsesSemanticMapper {
201
394
  async toChat(format, ctx) {
202
395
  const payload = format.payload || {};
@@ -257,6 +450,28 @@ export class ResponsesSemanticMapper {
257
450
  };
258
451
  }
259
452
  async fromChat(chat, ctx) {
453
+ const capturedContext = chat.metadata?.responsesContext;
454
+ const envelopeMetadata = chat.metadata && isJsonObject(chat.metadata) ? chat.metadata : undefined;
455
+ const responsesContext = isJsonObject(capturedContext)
456
+ ? {
457
+ ...capturedContext,
458
+ metadata: mergeMetadata(capturedContext.metadata, envelopeMetadata)
459
+ }
460
+ : {
461
+ metadata: envelopeMetadata
462
+ };
463
+ if (isSubmitToolOutputsEndpoint(ctx)) {
464
+ const submitPayload = buildSubmitToolOutputsPayload(chat, ctx, responsesContext);
465
+ return {
466
+ protocol: 'openai-responses',
467
+ direction: 'response',
468
+ payload: submitPayload,
469
+ meta: {
470
+ context: ctx,
471
+ submitToolOutputs: true
472
+ }
473
+ };
474
+ }
260
475
  const modelValue = chat.parameters?.model;
261
476
  if (typeof modelValue !== 'string' || !modelValue.trim()) {
262
477
  throw new Error('ChatEnvelope.parameters.model is required for openai-responses outbound conversion');
@@ -271,18 +486,7 @@ export class ResponsesSemanticMapper {
271
486
  .filter((message) => Boolean(message && typeof message === 'object' && message.role === 'system'))
272
487
  .map(message => serializeSystemContent(message))
273
488
  .filter((content) => typeof content === 'string' && content.length > 0);
274
- const capturedContext = chat.metadata?.responsesContext;
275
- const envelopeMetadata = chat.metadata && isJsonObject(chat.metadata) ? chat.metadata : undefined;
276
- const responsesContext = isJsonObject(capturedContext)
277
- ? {
278
- ...capturedContext,
279
- metadata: mergeMetadata(capturedContext.metadata, envelopeMetadata),
280
- originalSystemMessages
281
- }
282
- : {
283
- metadata: envelopeMetadata,
284
- originalSystemMessages
285
- };
489
+ responsesContext.originalSystemMessages = originalSystemMessages;
286
490
  const responsesResult = buildResponsesRequestFromChat(requestShape, responsesContext);
287
491
  const responses = responsesResult.request;
288
492
  if (chat.parameters && chat.parameters.stream !== undefined) {
@@ -1,5 +1,6 @@
1
1
  import type { BridgeInputItem, BridgeToolDefinition } from '../shared/bridge-message-types.js';
2
2
  import type { ChatToolDefinition } from '../hub/types/chat-envelope.js';
3
+ import type { JsonObject, JsonValue } from '../hub/types/json.js';
3
4
  import type { BridgeInputBuildResult } from '../shared/bridge-message-utils.js';
4
5
  import type { ToolCallIdStyle } from '../shared/responses-tool-utils.js';
5
6
  type Unknown = Record<string, unknown>;
@@ -11,8 +12,8 @@ export interface ResponsesRequestContext extends Unknown {
11
12
  store?: unknown;
12
13
  toolChoice?: unknown;
13
14
  parallelToolCalls?: boolean;
14
- metadata?: Record<string, unknown> | undefined;
15
- responseFormat?: unknown;
15
+ metadata?: JsonObject;
16
+ responseFormat?: JsonValue;
16
17
  stream?: boolean;
17
18
  isChatPayload?: boolean;
18
19
  isResponsesPayload?: boolean;
@@ -10,7 +10,6 @@ import { normalizeMessageReasoningTools } from '../shared/reasoning-tool-normali
10
10
  import { createBridgeActionState, runBridgeActionPipeline } from '../shared/bridge-actions.js';
11
11
  import { resolveBridgePolicy, resolvePolicyActions } from '../shared/bridge-policies.js';
12
12
  import { buildResponsesOutputFromChat } from '../shared/responses-output-builder.js';
13
- import { consumeResponsesPayloadSnapshot, consumeResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
14
13
  function isObject(v) {
15
14
  return !!v && typeof v === 'object' && !Array.isArray(v);
16
15
  }
@@ -29,7 +28,7 @@ export function captureResponsesContext(payload, dto) {
29
28
  store: payload.store,
30
29
  toolChoice: payload.tool_choice,
31
30
  parallelToolCalls: typeof payload.parallel_tool_calls === 'boolean' ? payload.parallel_tool_calls : undefined,
32
- metadata: (payload.metadata && typeof payload.metadata === 'object') ? { ...payload.metadata } : undefined,
31
+ metadata: (payload.metadata && typeof payload.metadata === 'object') ? payload.metadata : undefined,
33
32
  responseFormat: payload.response_format,
34
33
  stream: typeof payload.stream === 'boolean' ? payload.stream : undefined,
35
34
  isChatPayload: Array.isArray(payload.messages)
@@ -461,17 +460,6 @@ export function buildResponsesPayloadFromChat(payload, context) {
461
460
  const response = unwrapData(payload);
462
461
  if (!response || typeof response !== 'object')
463
462
  return payload;
464
- const snapshotKey = resolveSnapshotLookupKey(response, context);
465
- if (snapshotKey) {
466
- const passthrough = consumeResponsesPassthrough(snapshotKey);
467
- if (passthrough) {
468
- return passthrough;
469
- }
470
- const snapshot = consumeResponsesPayloadSnapshot(snapshotKey);
471
- if (snapshot) {
472
- return snapshot;
473
- }
474
- }
475
463
  if (response.object === 'response' && Array.isArray(response.output)) {
476
464
  return response;
477
465
  }
@@ -0,0 +1,20 @@
1
+ export type ToolCallLite = {
2
+ id?: string;
3
+ name: string;
4
+ args: string;
5
+ };
6
+ export declare function extractApplyPatchCallsFromText(text: string): ToolCallLite[] | null;
7
+ export declare function extractExecuteBlocksFromText(text: string): ToolCallLite[] | null;
8
+ export declare function extractXMLToolCallsFromText(text: string): ToolCallLite[] | null;
9
+ /**
10
+ * 提取简单 XML 形式的工具调用块,例如:
11
+ *
12
+ * <list_directory>
13
+ * <path>/path/to/dir</path>
14
+ * <recursive>false</recursive>
15
+ * </list_directory>
16
+ *
17
+ * 仅针对已知工具名(目前为 list_directory),避免误伤普通 XML 文本。
18
+ */
19
+ export declare function extractSimpleXmlToolsFromText(text: string): ToolCallLite[] | null;
20
+ export declare function normalizeAssistantTextToToolCalls(message: Record<string, any>): Record<string, any>;