@jsonstudio/llms 0.6.567 → 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.
- package/dist/conversion/codecs/gemini-openai-codec.js +33 -4
- package/dist/conversion/codecs/openai-openai-codec.js +2 -1
- package/dist/conversion/codecs/responses-openai-codec.js +3 -2
- package/dist/conversion/compat/actions/glm-history-image-trim.d.ts +2 -0
- package/dist/conversion/compat/actions/glm-history-image-trim.js +88 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.d.ts +6 -2
- package/dist/conversion/hub/pipeline/hub-pipeline.js +72 -81
- package/dist/conversion/hub/pipeline/stages/resp_outbound/resp_outbound_stage1_client_remap/index.js +0 -34
- package/dist/conversion/hub/process/chat-process.js +68 -24
- package/dist/conversion/hub/response/provider-response.js +0 -8
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +22 -3
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +267 -14
- package/dist/conversion/hub/types/chat-envelope.d.ts +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -2
- package/dist/conversion/responses/responses-openai-bridge.js +1 -13
- package/dist/conversion/shared/anthropic-message-utils.js +54 -0
- package/dist/conversion/shared/args-mapping.js +11 -3
- package/dist/conversion/shared/responses-output-builder.js +42 -21
- package/dist/conversion/shared/streaming-text-extractor.d.ts +25 -0
- package/dist/conversion/shared/streaming-text-extractor.js +31 -38
- package/dist/conversion/shared/text-markup-normalizer.d.ts +20 -0
- package/dist/conversion/shared/text-markup-normalizer.js +118 -31
- package/dist/conversion/shared/tool-filter-pipeline.js +56 -30
- package/dist/conversion/shared/tool-harvester.js +43 -12
- package/dist/conversion/shared/tool-mapping.d.ts +1 -0
- package/dist/conversion/shared/tool-mapping.js +33 -19
- package/dist/filters/index.d.ts +1 -0
- package/dist/filters/index.js +1 -0
- package/dist/filters/special/request-tools-normalize.js +14 -4
- package/dist/filters/special/response-apply-patch-toon-decode.d.ts +23 -0
- package/dist/filters/special/response-apply-patch-toon-decode.js +117 -0
- package/dist/filters/special/response-tool-arguments-toon-decode.d.ts +10 -0
- package/dist/filters/special/response-tool-arguments-toon-decode.js +154 -26
- package/dist/guidance/index.js +71 -42
- package/dist/router/virtual-router/bootstrap.js +10 -5
- package/dist/router/virtual-router/classifier.js +16 -7
- package/dist/router/virtual-router/engine-health.d.ts +11 -0
- package/dist/router/virtual-router/engine-health.js +217 -4
- package/dist/router/virtual-router/engine-logging.d.ts +2 -1
- package/dist/router/virtual-router/engine-logging.js +35 -3
- package/dist/router/virtual-router/engine.d.ts +17 -1
- package/dist/router/virtual-router/engine.js +184 -6
- package/dist/router/virtual-router/routing-instructions.d.ts +2 -0
- package/dist/router/virtual-router/routing-instructions.js +19 -1
- package/dist/router/virtual-router/tool-signals.d.ts +2 -1
- package/dist/router/virtual-router/tool-signals.js +324 -119
- package/dist/router/virtual-router/types.d.ts +31 -1
- package/dist/router/virtual-router/types.js +2 -2
- package/dist/servertool/engine.js +3 -0
- package/dist/servertool/handlers/iflow-model-error-retry.d.ts +1 -0
- package/dist/servertool/handlers/iflow-model-error-retry.js +93 -0
- package/dist/servertool/handlers/stop-message-auto.js +61 -4
- package/dist/servertool/server-side-tools.d.ts +1 -0
- package/dist/servertool/server-side-tools.js +27 -0
- package/dist/sse/json-to-sse/event-generators/responses.js +9 -2
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +23 -3
- package/dist/tools/apply-patch-structured.d.ts +20 -0
- package/dist/tools/apply-patch-structured.js +240 -0
- package/dist/tools/tool-description-utils.d.ts +5 -0
- package/dist/tools/tool-description-utils.js +50 -0
- package/dist/tools/tool-registry.js +11 -193
- package/package.json +1 -1
|
@@ -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;
|
|
@@ -53,6 +54,41 @@ function mapToolOutputs(entries, missing) {
|
|
|
53
54
|
});
|
|
54
55
|
return outputs.length ? outputs : undefined;
|
|
55
56
|
}
|
|
57
|
+
function deriveResumeToolOutputs(ctx) {
|
|
58
|
+
if (!ctx || typeof ctx !== 'object') {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const resume = ctx.responsesResume;
|
|
62
|
+
if (!resume || typeof resume !== 'object') {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const detailed = Array.isArray(resume.toolOutputsDetailed)
|
|
66
|
+
? resume.toolOutputsDetailed
|
|
67
|
+
: undefined;
|
|
68
|
+
if (!detailed || detailed.length === 0) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
const outputs = [];
|
|
72
|
+
detailed.forEach((entry, index) => {
|
|
73
|
+
if (!entry || typeof entry !== 'object') {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const callIdRaw = typeof entry.callId === 'string' && entry.callId.trim().length
|
|
77
|
+
? entry.callId.trim()
|
|
78
|
+
: typeof entry.originalId === 'string' && entry.originalId.trim().length
|
|
79
|
+
? entry.originalId.trim()
|
|
80
|
+
: `resume_tool_${index}`;
|
|
81
|
+
if (!callIdRaw) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const outputText = typeof entry.outputText === 'string' ? entry.outputText : '';
|
|
85
|
+
outputs.push({
|
|
86
|
+
tool_call_id: callIdRaw,
|
|
87
|
+
content: outputText
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
return outputs.length ? outputs : undefined;
|
|
91
|
+
}
|
|
56
92
|
function collectParameters(payload, streamHint) {
|
|
57
93
|
const params = {};
|
|
58
94
|
for (const key of RESPONSES_PARAMETER_KEYS) {
|
|
@@ -162,6 +198,198 @@ function mergeMetadata(a, b) {
|
|
|
162
198
|
const right = jsonClone(b);
|
|
163
199
|
return { ...left, ...right };
|
|
164
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
|
+
}
|
|
165
393
|
export class ResponsesSemanticMapper {
|
|
166
394
|
async toChat(format, ctx) {
|
|
167
395
|
const payload = format.payload || {};
|
|
@@ -169,16 +397,30 @@ export class ResponsesSemanticMapper {
|
|
|
169
397
|
const { request, toolsNormalized } = buildChatRequestFromResponses(payload, responsesContext);
|
|
170
398
|
const missingFields = [];
|
|
171
399
|
const messages = normalizeMessages(request.messages, missingFields);
|
|
172
|
-
|
|
400
|
+
let toolOutputs = mapToolOutputs(payload.tool_outputs, missingFields);
|
|
401
|
+
if (!toolOutputs || toolOutputs.length === 0) {
|
|
402
|
+
const resumeToolOutputs = deriveResumeToolOutputs(ctx);
|
|
403
|
+
if (resumeToolOutputs && resumeToolOutputs.length) {
|
|
404
|
+
toolOutputs = resumeToolOutputs;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
173
407
|
const parameters = collectParameters(payload, responsesContext.stream);
|
|
174
408
|
const metadata = { context: ctx };
|
|
175
409
|
try {
|
|
176
410
|
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
177
411
|
const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
178
412
|
if (actions?.length) {
|
|
413
|
+
const capturedToolResults = Array.isArray(toolOutputs)
|
|
414
|
+
? toolOutputs.map((entry) => ({
|
|
415
|
+
tool_call_id: entry.tool_call_id,
|
|
416
|
+
output: entry.content,
|
|
417
|
+
name: entry.name
|
|
418
|
+
}))
|
|
419
|
+
: undefined;
|
|
179
420
|
const actionState = createBridgeActionState({
|
|
180
421
|
rawRequest: payload,
|
|
181
|
-
metadata: metadata
|
|
422
|
+
metadata: metadata,
|
|
423
|
+
capturedToolResults
|
|
182
424
|
});
|
|
183
425
|
runBridgeActionPipeline({
|
|
184
426
|
stage: 'request_inbound',
|
|
@@ -208,6 +450,28 @@ export class ResponsesSemanticMapper {
|
|
|
208
450
|
};
|
|
209
451
|
}
|
|
210
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
|
+
}
|
|
211
475
|
const modelValue = chat.parameters?.model;
|
|
212
476
|
if (typeof modelValue !== 'string' || !modelValue.trim()) {
|
|
213
477
|
throw new Error('ChatEnvelope.parameters.model is required for openai-responses outbound conversion');
|
|
@@ -222,18 +486,7 @@ export class ResponsesSemanticMapper {
|
|
|
222
486
|
.filter((message) => Boolean(message && typeof message === 'object' && message.role === 'system'))
|
|
223
487
|
.map(message => serializeSystemContent(message))
|
|
224
488
|
.filter((content) => typeof content === 'string' && content.length > 0);
|
|
225
|
-
|
|
226
|
-
const envelopeMetadata = chat.metadata && isJsonObject(chat.metadata) ? chat.metadata : undefined;
|
|
227
|
-
const responsesContext = isJsonObject(capturedContext)
|
|
228
|
-
? {
|
|
229
|
-
...capturedContext,
|
|
230
|
-
metadata: mergeMetadata(capturedContext.metadata, envelopeMetadata),
|
|
231
|
-
originalSystemMessages
|
|
232
|
-
}
|
|
233
|
-
: {
|
|
234
|
-
metadata: envelopeMetadata,
|
|
235
|
-
originalSystemMessages
|
|
236
|
-
};
|
|
489
|
+
responsesContext.originalSystemMessages = originalSystemMessages;
|
|
237
490
|
const responsesResult = buildResponsesRequestFromChat(requestShape, responsesContext);
|
|
238
491
|
const responses = responsesResult.request;
|
|
239
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?:
|
|
15
|
-
responseFormat?:
|
|
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') ?
|
|
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
|
}
|
|
@@ -647,6 +647,56 @@ function createAnthropicToolNameResolver(source) {
|
|
|
647
647
|
return lookup.get(trimmed) ?? lookup.get(trimmed.toLowerCase()) ?? trimmed;
|
|
648
648
|
};
|
|
649
649
|
}
|
|
650
|
+
function normalizeAnthropicToolChoice(value) {
|
|
651
|
+
if (value === undefined || value === null) {
|
|
652
|
+
return undefined;
|
|
653
|
+
}
|
|
654
|
+
if (isPlainRecord(value)) {
|
|
655
|
+
// Already an object – best-effort clone while trimming type, and also support
|
|
656
|
+
// Chat-style { type: 'function', function: { name } } by mapping to Anthropic's
|
|
657
|
+
// { type: 'tool', name } shape.
|
|
658
|
+
const cloned = cloneAnthropicSchema(value);
|
|
659
|
+
const rawType = typeof cloned.type === 'string' ? String(cloned.type).trim() : '';
|
|
660
|
+
if (rawType) {
|
|
661
|
+
cloned.type = rawType;
|
|
662
|
+
return cloned;
|
|
663
|
+
}
|
|
664
|
+
const selectorType = typeof cloned.type === 'string' ? String(cloned.type).trim() : '';
|
|
665
|
+
const fn = cloned.function;
|
|
666
|
+
if (selectorType === 'function' &&
|
|
667
|
+
fn &&
|
|
668
|
+
typeof fn === 'object' &&
|
|
669
|
+
typeof fn.name === 'string' &&
|
|
670
|
+
String(fn.name).trim().length) {
|
|
671
|
+
return { type: 'tool', name: String(fn.name).trim() };
|
|
672
|
+
}
|
|
673
|
+
return cloned;
|
|
674
|
+
}
|
|
675
|
+
if (typeof value === 'string') {
|
|
676
|
+
const trimmed = value.trim();
|
|
677
|
+
if (!trimmed.length) {
|
|
678
|
+
return undefined;
|
|
679
|
+
}
|
|
680
|
+
const lower = trimmed.toLowerCase();
|
|
681
|
+
if (lower === 'auto') {
|
|
682
|
+
return { type: 'auto' };
|
|
683
|
+
}
|
|
684
|
+
if (lower === 'none') {
|
|
685
|
+
return { type: 'none' };
|
|
686
|
+
}
|
|
687
|
+
if (lower === 'any') {
|
|
688
|
+
return { type: 'any' };
|
|
689
|
+
}
|
|
690
|
+
if (lower === 'required') {
|
|
691
|
+
// "required" in canonical Chat roughly maps to Anthropic's "any" semantics:
|
|
692
|
+
// the model must choose some tool if available.
|
|
693
|
+
return { type: 'any' };
|
|
694
|
+
}
|
|
695
|
+
// Fallback: preserve custom mode as-is in type field.
|
|
696
|
+
return { type: trimmed };
|
|
697
|
+
}
|
|
698
|
+
return undefined;
|
|
699
|
+
}
|
|
650
700
|
export function buildAnthropicRequestFromOpenAIChat(chatReq) {
|
|
651
701
|
const requestBody = isObject(chatReq) ? chatReq : {};
|
|
652
702
|
const model = String(requestBody?.model || 'unknown');
|
|
@@ -882,6 +932,10 @@ export function buildAnthropicRequestFromOpenAIChat(chatReq) {
|
|
|
882
932
|
if (anthropicTools !== undefined) {
|
|
883
933
|
out.tools = anthropicTools;
|
|
884
934
|
}
|
|
935
|
+
const normalizedToolChoice = normalizeAnthropicToolChoice(requestBody.tool_choice);
|
|
936
|
+
if (normalizedToolChoice !== undefined) {
|
|
937
|
+
out.tool_choice = normalizedToolChoice;
|
|
938
|
+
}
|
|
885
939
|
try {
|
|
886
940
|
if (requestBody.metadata && typeof requestBody.metadata === 'object') {
|
|
887
941
|
out.metadata = JSON.parse(JSON.stringify(requestBody.metadata));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { buildShellDescription, hasApplyPatchToolDeclared, isShellToolName } from '../../tools/tool-description-utils.js';
|
|
2
2
|
function isObject(v) {
|
|
3
3
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
4
4
|
}
|
|
@@ -155,6 +155,7 @@ export function normalizeTools(tools) {
|
|
|
155
155
|
if (!Array.isArray(tools))
|
|
156
156
|
return [];
|
|
157
157
|
const out = [];
|
|
158
|
+
const applyPatchAvailable = hasApplyPatchToolDeclared(tools);
|
|
158
159
|
for (const t of tools) {
|
|
159
160
|
if (!t || typeof t !== 'object')
|
|
160
161
|
continue;
|
|
@@ -175,7 +176,7 @@ export function normalizeTools(tools) {
|
|
|
175
176
|
}
|
|
176
177
|
// Enforce schema for known tools with minimal, compatible constraints
|
|
177
178
|
let finalParams;
|
|
178
|
-
if (
|
|
179
|
+
if (isShellToolName(name)) {
|
|
179
180
|
// Do NOT downgrade an existing schema; prefer string command, allow argv array as fallback
|
|
180
181
|
const base = isObject(params) ? params : {};
|
|
181
182
|
const props = isObject(base.properties) ? base.properties : {};
|
|
@@ -205,7 +206,14 @@ export function normalizeTools(tools) {
|
|
|
205
206
|
else {
|
|
206
207
|
finalParams = { type: 'object', properties: {}, additionalProperties: true };
|
|
207
208
|
}
|
|
208
|
-
const
|
|
209
|
+
const functionNode = { name, ...(desc ? { description: desc } : {}), parameters: finalParams };
|
|
210
|
+
if (isShellToolName(name)) {
|
|
211
|
+
const display = (typeof name === 'string' && name.trim().length > 0)
|
|
212
|
+
? name.trim()
|
|
213
|
+
: ((typeof topName === 'string' && topName.trim().length > 0) ? topName.trim() : 'shell');
|
|
214
|
+
functionNode.description = buildShellDescription(display, applyPatchAvailable);
|
|
215
|
+
}
|
|
216
|
+
const norm = { type: 'function', function: functionNode };
|
|
209
217
|
if (norm.function?.name)
|
|
210
218
|
out.push(norm);
|
|
211
219
|
}
|
|
@@ -153,29 +153,50 @@ export function buildResponsesOutputFromChat(options) {
|
|
|
153
153
|
};
|
|
154
154
|
}
|
|
155
155
|
function normalizeUsage(usageRaw) {
|
|
156
|
-
if (usageRaw
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
156
|
+
if (!usageRaw || typeof usageRaw !== 'object') {
|
|
157
|
+
return usageRaw;
|
|
158
|
+
}
|
|
159
|
+
const usage = { ...usageRaw };
|
|
160
|
+
// 统一 Responses 与 Chat 两种 usage 形态:
|
|
161
|
+
// - Responses: input_tokens / output_tokens / total_tokens
|
|
162
|
+
// - Chat: prompt_tokens / completion_tokens / total_tokens
|
|
163
|
+
const inputTokens = typeof usage.input_tokens === 'number'
|
|
164
|
+
? usage.input_tokens
|
|
165
|
+
: typeof usage.prompt_tokens === 'number'
|
|
166
|
+
? usage.prompt_tokens
|
|
167
|
+
: undefined;
|
|
168
|
+
const outputTokens = typeof usage.output_tokens === 'number'
|
|
169
|
+
? usage.output_tokens
|
|
170
|
+
: typeof usage.completion_tokens === 'number'
|
|
171
|
+
? usage.completion_tokens
|
|
172
|
+
: undefined;
|
|
173
|
+
let totalTokens = typeof usage.total_tokens === 'number'
|
|
174
|
+
? usage.total_tokens
|
|
175
|
+
: undefined;
|
|
176
|
+
if (totalTokens === undefined && inputTokens !== undefined && outputTokens !== undefined) {
|
|
177
|
+
const total = Number(inputTokens) + Number(outputTokens);
|
|
178
|
+
if (!Number.isNaN(total)) {
|
|
179
|
+
totalTokens = total;
|
|
175
180
|
}
|
|
176
|
-
return usage;
|
|
177
181
|
}
|
|
178
|
-
|
|
182
|
+
// Responses 规范字段:input_tokens / output_tokens / total_tokens
|
|
183
|
+
if (inputTokens !== undefined) {
|
|
184
|
+
usage.input_tokens = inputTokens;
|
|
185
|
+
}
|
|
186
|
+
if (outputTokens !== undefined) {
|
|
187
|
+
usage.output_tokens = outputTokens;
|
|
188
|
+
}
|
|
189
|
+
if (totalTokens !== undefined) {
|
|
190
|
+
usage.total_tokens = totalTokens;
|
|
191
|
+
}
|
|
192
|
+
// 为了兼容内部统计逻辑,保留 prompt_tokens / completion_tokens 映射(如果原本没有)
|
|
193
|
+
if (usage.prompt_tokens == null && inputTokens !== undefined) {
|
|
194
|
+
usage.prompt_tokens = inputTokens;
|
|
195
|
+
}
|
|
196
|
+
if (usage.completion_tokens == null && outputTokens !== undefined) {
|
|
197
|
+
usage.completion_tokens = outputTokens;
|
|
198
|
+
}
|
|
199
|
+
return usage;
|
|
179
200
|
}
|
|
180
201
|
function buildFunctionCallOutput(call, allocateOutputId, sanitizeFunctionName, baseCount, offset) {
|
|
181
202
|
try {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface StreamingToolCall {
|
|
2
|
+
id?: string;
|
|
3
|
+
type: 'function';
|
|
4
|
+
function: {
|
|
5
|
+
name?: string;
|
|
6
|
+
arguments?: string;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export interface StreamingToolExtractorOptions {
|
|
10
|
+
idPrefix?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class StreamingTextToolExtractor {
|
|
13
|
+
private opts;
|
|
14
|
+
private buffer;
|
|
15
|
+
private idCounter;
|
|
16
|
+
constructor(opts?: StreamingToolExtractorOptions);
|
|
17
|
+
reset(): void;
|
|
18
|
+
feedText(text: string): StreamingToolCall[];
|
|
19
|
+
private genId;
|
|
20
|
+
private toToolCall;
|
|
21
|
+
private tryExtractStructuredBlocks;
|
|
22
|
+
private tryExtractFunctionExecuteBlocks;
|
|
23
|
+
private splitCommand;
|
|
24
|
+
}
|
|
25
|
+
export declare function createStreamingToolExtractor(opts?: StreamingToolExtractorOptions): StreamingTextToolExtractor;
|