@jsonstudio/llms 0.6.3409 → 0.6.3539
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/anthropic-openai-codec.d.ts +12 -3
- package/dist/conversion/codecs/anthropic-openai-codec.js +32 -92
- package/dist/conversion/codecs/gemini-openai-codec.d.ts +6 -5
- package/dist/conversion/codecs/gemini-openai-codec.js +48 -685
- package/dist/conversion/codecs/openai-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/openai-openai-codec.js +34 -100
- package/dist/conversion/codecs/responses-openai-codec.d.ts +1 -1
- package/dist/conversion/codecs/responses-openai-codec.js +47 -159
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.d.ts +2 -6
- package/dist/conversion/compat/actions/anthropic-claude-code-system-prompt.js +29 -245
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.d.ts +3 -0
- package/dist/conversion/compat/actions/anthropic-claude-code-user-id.js +30 -0
- package/dist/conversion/compat/actions/antigravity-thought-signature-prepare.js +21 -232
- package/dist/conversion/compat/actions/deepseek-web-request.js +41 -276
- package/dist/conversion/compat/actions/deepseek-web-response.js +64 -859
- package/dist/conversion/compat/actions/gemini-cli-request.d.ts +1 -1
- package/dist/conversion/compat/actions/gemini-cli-request.js +20 -613
- package/dist/conversion/compat/actions/gemini-web-search.d.ts +1 -15
- package/dist/conversion/compat/actions/gemini-web-search.js +22 -69
- package/dist/conversion/compat/actions/glm-tool-extraction.d.ts +3 -2
- package/dist/conversion/compat/actions/glm-tool-extraction.js +28 -257
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +0 -8
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +24 -206
- package/dist/conversion/compat/actions/qwen-transform.d.ts +3 -2
- package/dist/conversion/compat/actions/qwen-transform.js +30 -271
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +3 -173
- package/dist/conversion/compat/actions/universal-shape-filter.d.ts +6 -23
- package/dist/conversion/compat/actions/universal-shape-filter.js +4 -383
- package/dist/conversion/hub/pipeline/compat/native-adapter-context.js +1 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +1 -2
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +50 -104
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +12 -10
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +0 -2
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +46 -67
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +15 -40
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +47 -348
- package/dist/conversion/responses/responses-openai-bridge.js +129 -611
- package/dist/conversion/shared/chat-output-normalizer.js +6 -0
- package/dist/conversion/shared/chat-request-filters.js +1 -1
- package/dist/conversion/shared/output-content-normalizer.js +10 -0
- package/dist/conversion/shared/responses-conversation-store.js +22 -135
- package/dist/conversion/shared/responses-output-builder.d.ts +0 -2
- package/dist/conversion/shared/responses-output-builder.js +28 -318
- package/dist/conversion/shared/responses-response-utils.js +35 -86
- package/dist/conversion/shared/streaming-text-extractor.d.ts +1 -2
- package/dist/conversion/shared/streaming-text-extractor.js +13 -14
- package/dist/native/router_hotpath_napi.node +0 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +11 -3
- package/dist/router/virtual-router/engine-legacy.d.ts +3 -3
- package/dist/router/virtual-router/engine-legacy.js +15 -7
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.d.ts +16 -0
- package/dist/router/virtual-router/engine-selection/native-compat-action-semantics.js +434 -46
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.d.ts +83 -0
- package/dist/router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js +295 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.d.ts +7 -0
- package/dist/router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js +8 -1
- package/dist/router/virtual-router/engine-selection/native-router-hotpath-loader.js +383 -298
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.d.ts +20 -0
- package/dist/router/virtual-router/engine-selection/native-shared-conversion-semantics.js +201 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/native-virtual-router-routing-instructions-semantics.js +37 -0
- package/dist/router/virtual-router/engine.js +0 -38
- package/dist/router/virtual-router/features.js +44 -3
- package/dist/router/virtual-router/routing-instructions/parse.d.ts +0 -12
- package/dist/router/virtual-router/routing-instructions/parse.js +9 -389
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +3 -6
- package/dist/router/virtual-router/stop-message-state-sync.js +50 -21
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +26 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +12 -2
- package/package.json +1 -1
- package/dist/router/virtual-router/engine-legacy/route-finalize.d.ts +0 -9
- package/dist/router/virtual-router/engine-legacy/route-finalize.js +0 -84
- package/dist/router/virtual-router/engine-legacy/route-selection.d.ts +0 -17
- package/dist/router/virtual-router/engine-legacy/route-selection.js +0 -205
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.d.ts +0 -3
- package/dist/router/virtual-router/engine-legacy/route-state-allowlist.js +0 -36
- package/dist/router/virtual-router/engine-legacy/route-state.d.ts +0 -12
- package/dist/router/virtual-router/engine-legacy/route-state.js +0 -386
- package/dist/router/virtual-router/engine-legacy/routing.d.ts +0 -8
- package/dist/router/virtual-router/engine-legacy/routing.js +0 -8
|
@@ -1,238 +1,43 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
1
|
import { ensureBridgeInstructions } from '../bridge-instructions.js';
|
|
5
2
|
import { evaluateResponsesHostPolicy } from './responses-host-policy.js';
|
|
6
3
|
import { convertBridgeInputToChatMessages } from '../bridge-message-utils.js';
|
|
7
|
-
import { createToolCallIdTransformer, enforceToolCallIdStyle,
|
|
4
|
+
import { createToolCallIdTransformer, enforceToolCallIdStyle, sanitizeResponsesFunctionName } from '../shared/responses-tool-utils.js';
|
|
8
5
|
import { mapChatToolsToBridge } from '../shared/tool-mapping.js';
|
|
9
6
|
import { ProviderProtocolError } from '../provider-protocol-error.js';
|
|
10
|
-
import { readRuntimeMetadata } from '../runtime-metadata.js';
|
|
11
|
-
import { clampResponsesInputItemId } from '../bridge-id-utils.js';
|
|
12
|
-
import { isImagePath } from '../media.js';
|
|
13
7
|
import { captureReqInboundResponsesContextSnapshotWithNative, mapReqInboundBridgeToolsToChatWithNative } from '../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
|
|
14
|
-
import { buildBridgeHistoryWithNative } from '../../router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js';
|
|
8
|
+
import { appendLocalImageBlockOnLatestUserInputWithNative, buildBridgeHistoryWithNative, filterBridgeInputForUpstreamWithNative, normalizeBridgeHistorySeedWithNative, prepareResponsesRequestEnvelopeWithNative, resolveResponsesRequestBridgeDecisionsWithNative, resolveResponsesBridgeToolsWithNative, runBridgeActionPipelineWithNative } from '../../router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js';
|
|
15
9
|
// --- Utilities (ported strictly) ---
|
|
16
|
-
import { createBridgeActionState, runBridgeActionPipeline } from '../bridge-actions.js';
|
|
17
10
|
import { resolveBridgePolicy, resolvePolicyActions } from '../bridge-policies.js';
|
|
18
11
|
function isObject(v) {
|
|
19
12
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
20
13
|
}
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return (Array.isArray(input) ? input : []).flatMap((item) => {
|
|
27
|
-
if (!item || typeof item !== 'object')
|
|
28
|
-
return [];
|
|
29
|
-
if (item.type === 'reasoning') {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
// OpenAI `/v1/responses` request schema uses `call_id` for tool outputs.
|
|
33
|
-
// Some internal carriers may set `tool_call_id`; strip it before sending upstream
|
|
34
|
-
// to avoid strict schema errors (e.g. "Unknown parameter: input[N].tool_call_id").
|
|
35
|
-
const clone = { ...item };
|
|
36
|
-
if (options?.allowToolCallId !== true && clone.tool_call_id !== undefined) {
|
|
37
|
-
delete clone.tool_call_id;
|
|
38
|
-
}
|
|
39
|
-
// OpenAI /v1/responses enforces max length (64) on input item id fields.
|
|
40
|
-
// Keep this as the single outbound guardrail for all bridged input item types.
|
|
41
|
-
const normalizedId = clampResponsesInputItemId(clone.id);
|
|
42
|
-
if (normalizedId) {
|
|
43
|
-
clone.id = normalizedId;
|
|
44
|
-
}
|
|
45
|
-
return [clone];
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
// normalizeTools unified in ../args-mapping.ts
|
|
49
|
-
// --- Structured self-repair helpers for tool failures (Responses path) ---
|
|
50
|
-
const IMAGE_MIME_BY_EXT = {
|
|
51
|
-
'.png': 'image/png',
|
|
52
|
-
'.jpg': 'image/jpeg',
|
|
53
|
-
'.jpeg': 'image/jpeg',
|
|
54
|
-
'.gif': 'image/gif',
|
|
55
|
-
'.webp': 'image/webp',
|
|
56
|
-
'.bmp': 'image/bmp',
|
|
57
|
-
'.svg': 'image/svg+xml',
|
|
58
|
-
'.tif': 'image/tiff',
|
|
59
|
-
'.tiff': 'image/tiff',
|
|
60
|
-
'.ico': 'image/x-icon',
|
|
61
|
-
'.heic': 'image/heic',
|
|
62
|
-
'.jxl': 'image/jxl'
|
|
63
|
-
};
|
|
64
|
-
function decodeEscapedPathLikeText(input) {
|
|
65
|
-
if (!input || input.indexOf('\\') < 0) {
|
|
66
|
-
return input;
|
|
67
|
-
}
|
|
68
|
-
return input
|
|
69
|
-
.replace(/\\\//g, '/')
|
|
70
|
-
.replace(/\\u([0-9a-fA-F]{4})/g, (_m, hex) => {
|
|
71
|
-
const codepoint = Number.parseInt(hex, 16);
|
|
72
|
-
return Number.isFinite(codepoint) ? String.fromCharCode(codepoint) : '';
|
|
73
|
-
})
|
|
74
|
-
.replace(/\\x([0-9a-fA-F]{2})/g, (_m, hex) => {
|
|
75
|
-
const codepoint = Number.parseInt(hex, 16);
|
|
76
|
-
return Number.isFinite(codepoint) ? String.fromCharCode(codepoint) : '';
|
|
77
|
-
})
|
|
78
|
-
.replace(/\\\\/g, '\\');
|
|
79
|
-
}
|
|
80
|
-
function normalizeLocalImagePath(candidate) {
|
|
81
|
-
if (!candidate || typeof candidate !== 'string')
|
|
82
|
-
return null;
|
|
83
|
-
let value = decodeEscapedPathLikeText(candidate.trim());
|
|
84
|
-
if (!value)
|
|
85
|
-
return null;
|
|
86
|
-
if (/^https?:\/\//i.test(value))
|
|
87
|
-
return null;
|
|
88
|
-
if (value.toLowerCase().startsWith('file://')) {
|
|
89
|
-
try {
|
|
90
|
-
const parsed = new URL(value);
|
|
91
|
-
value = decodeURIComponent(parsed.pathname || '');
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return null;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (!value)
|
|
98
|
-
return null;
|
|
99
|
-
if (value.startsWith('~')) {
|
|
100
|
-
value = path.join(os.homedir(), value.slice(1));
|
|
101
|
-
}
|
|
102
|
-
const isAbsolutePath = path.isAbsolute(value);
|
|
103
|
-
const normalized = isAbsolutePath ? path.normalize(value) : path.resolve(process.cwd(), value);
|
|
104
|
-
return isImagePath(normalized) ? normalized : null;
|
|
14
|
+
function readCapturedToolResults(context) {
|
|
15
|
+
const raw = context.__captured_tool_results;
|
|
16
|
+
return Array.isArray(raw)
|
|
17
|
+
? raw.filter((entry) => Boolean(entry && typeof entry === 'object' && !Array.isArray(entry)))
|
|
18
|
+
: undefined;
|
|
105
19
|
}
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const normalized = normalizeLocalImagePath(quotedMatch[2] || '');
|
|
118
|
-
if (normalized)
|
|
119
|
-
candidates.add(normalized);
|
|
20
|
+
function runNativeResponsesBridgePipeline(input) {
|
|
21
|
+
const output = runBridgeActionPipelineWithNative({
|
|
22
|
+
stage: input.stage,
|
|
23
|
+
actions: input.actions,
|
|
24
|
+
protocol: input.protocol,
|
|
25
|
+
moduleType: input.moduleType,
|
|
26
|
+
requestId: input.requestId,
|
|
27
|
+
state: {
|
|
28
|
+
messages: input.messages,
|
|
29
|
+
...(Array.isArray(input.capturedToolResults) ? { capturedToolResults: input.capturedToolResults } : {}),
|
|
30
|
+
...(input.rawRequest ? { rawRequest: input.rawRequest } : {})
|
|
120
31
|
}
|
|
121
|
-
let bareMatch;
|
|
122
|
-
barePathRegex.lastIndex = 0;
|
|
123
|
-
while ((bareMatch = barePathRegex.exec(variant)) !== null) {
|
|
124
|
-
const normalized = normalizeLocalImagePath(bareMatch[1] || '');
|
|
125
|
-
if (normalized)
|
|
126
|
-
candidates.add(normalized);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return Array.from(candidates);
|
|
130
|
-
}
|
|
131
|
-
function detectImageMime(filePath) {
|
|
132
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
133
|
-
return IMAGE_MIME_BY_EXT[ext] || 'application/octet-stream';
|
|
134
|
-
}
|
|
135
|
-
function encodeLocalImageAsDataUrl(filePath) {
|
|
136
|
-
const imageBuffer = fs.readFileSync(filePath);
|
|
137
|
-
const mimeType = detectImageMime(filePath);
|
|
138
|
-
return `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
|
|
139
|
-
}
|
|
140
|
-
function messageHasImageContent(content) {
|
|
141
|
-
if (!Array.isArray(content))
|
|
142
|
-
return false;
|
|
143
|
-
return content.some((part) => {
|
|
144
|
-
if (!part || typeof part !== 'object')
|
|
145
|
-
return false;
|
|
146
|
-
const type = String(part.type || '').toLowerCase();
|
|
147
|
-
if (type !== 'image_url' && type !== 'input_image' && type !== 'image')
|
|
148
|
-
return false;
|
|
149
|
-
const imageUrl = part.image_url;
|
|
150
|
-
return typeof imageUrl === 'string' || Boolean(imageUrl && typeof imageUrl === 'object' && typeof imageUrl.url === 'string');
|
|
151
32
|
});
|
|
33
|
+
return {
|
|
34
|
+
messages: Array.isArray(output?.messages) ? output.messages : input.messages,
|
|
35
|
+
metadata: output?.metadata && typeof output.metadata === 'object' && !Array.isArray(output.metadata)
|
|
36
|
+
? output.metadata
|
|
37
|
+
: undefined
|
|
38
|
+
};
|
|
152
39
|
}
|
|
153
|
-
|
|
154
|
-
if (!Array.isArray(messages) || !messages.length)
|
|
155
|
-
return;
|
|
156
|
-
let latestUserIndex = -1;
|
|
157
|
-
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
158
|
-
const role = String(messages[idx]?.role || '').toLowerCase();
|
|
159
|
-
if (role === 'user') {
|
|
160
|
-
latestUserIndex = idx;
|
|
161
|
-
break;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
if (latestUserIndex < 0)
|
|
165
|
-
return;
|
|
166
|
-
const latestUserMessage = messages[latestUserIndex];
|
|
167
|
-
const originalContent = latestUserMessage.content;
|
|
168
|
-
if (messageHasImageContent(originalContent))
|
|
169
|
-
return;
|
|
170
|
-
const textCandidates = [];
|
|
171
|
-
if (typeof originalContent === 'string' && originalContent.trim().length) {
|
|
172
|
-
textCandidates.push(originalContent);
|
|
173
|
-
}
|
|
174
|
-
else if (Array.isArray(originalContent)) {
|
|
175
|
-
for (const part of originalContent) {
|
|
176
|
-
if (!part || typeof part !== 'object')
|
|
177
|
-
continue;
|
|
178
|
-
const type = String(part.type || '').toLowerCase();
|
|
179
|
-
if ((type === 'text' || type === 'input_text' || type === 'output_text' || type === 'commentary') &&
|
|
180
|
-
typeof part.text === 'string' &&
|
|
181
|
-
part.text.trim().length) {
|
|
182
|
-
textCandidates.push(part.text);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
if (!textCandidates.length)
|
|
187
|
-
return;
|
|
188
|
-
const imagePaths = new Set();
|
|
189
|
-
for (const text of textCandidates) {
|
|
190
|
-
for (const candidate of collectLocalImagePathCandidates(text)) {
|
|
191
|
-
imagePaths.add(candidate);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (!imagePaths.size)
|
|
195
|
-
return;
|
|
196
|
-
const normalizedContent = [];
|
|
197
|
-
if (typeof originalContent === 'string') {
|
|
198
|
-
normalizedContent.push({ type: 'text', text: originalContent });
|
|
199
|
-
}
|
|
200
|
-
else if (Array.isArray(originalContent)) {
|
|
201
|
-
for (const part of originalContent) {
|
|
202
|
-
if (part && typeof part === 'object') {
|
|
203
|
-
normalizedContent.push({ ...part });
|
|
204
|
-
}
|
|
205
|
-
else if (typeof part === 'string') {
|
|
206
|
-
normalizedContent.push({ type: 'text', text: part });
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
const unreadableImageNotices = [];
|
|
211
|
-
for (const imagePath of imagePaths) {
|
|
212
|
-
let dataUrl = '';
|
|
213
|
-
try {
|
|
214
|
-
dataUrl = encodeLocalImageAsDataUrl(imagePath);
|
|
215
|
-
}
|
|
216
|
-
catch (error) {
|
|
217
|
-
const reason = error instanceof Error
|
|
218
|
-
? `${error.code ?? 'READ_FAILED'}: ${error.message}`
|
|
219
|
-
: String(error ?? 'READ_FAILED');
|
|
220
|
-
unreadableImageNotices.push(`[local_image_unreadable] 文件不可读,已跳过该图片路径: ${imagePath} (${reason})`);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
normalizedContent.push({ type: 'image_url', image_url: { url: dataUrl } });
|
|
224
|
-
}
|
|
225
|
-
if (unreadableImageNotices.length) {
|
|
226
|
-
normalizedContent.push({
|
|
227
|
-
type: 'text',
|
|
228
|
-
text: unreadableImageNotices.join('\n')
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
if (!messageHasImageContent(normalizedContent) && !unreadableImageNotices.length) {
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
latestUserMessage.content = normalizedContent;
|
|
235
|
-
}
|
|
40
|
+
// normalizeTools unified in ../args-mapping.ts
|
|
236
41
|
// NOTE: 自修复提示已移除(统一标准:不做模糊兜底)。
|
|
237
42
|
// --- Public bridge functions ---
|
|
238
43
|
export function captureResponsesContext(payload, dto) {
|
|
@@ -257,7 +62,7 @@ export function captureResponsesContext(payload, dto) {
|
|
|
257
62
|
captured.__rcc_reasoning_instructions_segments = normalized;
|
|
258
63
|
}
|
|
259
64
|
}
|
|
260
|
-
if (preservedInput) {
|
|
65
|
+
if (preservedInput && (!Array.isArray(captured.input) || captured.input.length === 0)) {
|
|
261
66
|
captured.input = preservedInput;
|
|
262
67
|
}
|
|
263
68
|
if (!captured.systemInstruction && typeof payload.instructions === 'string' && payload.instructions.trim().length) {
|
|
@@ -276,25 +81,22 @@ export function buildChatRequestFromResponses(payload, context) {
|
|
|
276
81
|
let messages = convertBridgeInputToChatMessages({
|
|
277
82
|
input: context.input,
|
|
278
83
|
tools: toolsNormalized,
|
|
279
|
-
normalizeFunctionName:
|
|
84
|
+
normalizeFunctionName: 'responses',
|
|
280
85
|
toolResultFallbackText: 'Command succeeded (no output).'
|
|
281
86
|
});
|
|
282
87
|
try {
|
|
283
88
|
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
284
89
|
const policyActions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
285
90
|
if (policyActions?.length) {
|
|
286
|
-
const actionState =
|
|
287
|
-
messages,
|
|
288
|
-
requiredAction: typeof payload?.required_action === 'object' ? payload.required_action : undefined,
|
|
289
|
-
rawRequest: payload
|
|
290
|
-
});
|
|
291
|
-
runBridgeActionPipeline({
|
|
91
|
+
const actionState = runNativeResponsesBridgePipeline({
|
|
292
92
|
stage: 'request_inbound',
|
|
293
93
|
actions: policyActions,
|
|
294
|
-
protocol: bridgePolicy?.protocol ?? 'openai-responses',
|
|
295
|
-
moduleType: bridgePolicy?.moduleType ?? 'openai-responses',
|
|
94
|
+
protocol: (bridgePolicy?.protocol ?? 'openai-responses'),
|
|
95
|
+
moduleType: (bridgePolicy?.moduleType ?? 'openai-responses'),
|
|
296
96
|
requestId: context.requestId,
|
|
297
|
-
|
|
97
|
+
messages,
|
|
98
|
+
capturedToolResults: readCapturedToolResults(context),
|
|
99
|
+
rawRequest: payload
|
|
298
100
|
});
|
|
299
101
|
messages = actionState.messages;
|
|
300
102
|
}
|
|
@@ -311,7 +113,7 @@ export function buildChatRequestFromResponses(payload, context) {
|
|
|
311
113
|
messages = [...preservedSystems, ...nonSystemMessages];
|
|
312
114
|
}
|
|
313
115
|
}
|
|
314
|
-
|
|
116
|
+
messages = appendLocalImageBlockOnLatestUserInputWithNative({ messages }).messages;
|
|
315
117
|
// 不在 Responses 路径做工具治理;统一在 Chat 后半段处理
|
|
316
118
|
// No system tips for MCP on OpenAI Responses path (avoid leaking tool names)
|
|
317
119
|
if (!messages.length) {
|
|
@@ -354,207 +156,34 @@ export function buildChatRequestFromResponses(payload, context) {
|
|
|
354
156
|
* - 将 user/assistant/tool 消息编码为 input[] 中的 message 块,使得 mapResponsesInputToChat 能够还原为等价 Chat 请求。
|
|
355
157
|
*/
|
|
356
158
|
function normalizeBridgeHistory(seed) {
|
|
357
|
-
if (!seed || typeof seed !== 'object') {
|
|
358
|
-
return undefined;
|
|
359
|
-
}
|
|
360
|
-
const record = seed;
|
|
361
|
-
if (!Array.isArray(record.input)) {
|
|
159
|
+
if (!seed || typeof seed !== 'object' || Array.isArray(seed)) {
|
|
362
160
|
return undefined;
|
|
363
161
|
}
|
|
364
|
-
|
|
365
|
-
? record.originalSystemMessages.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
|
|
366
|
-
: [];
|
|
367
|
-
return {
|
|
368
|
-
input: record.input,
|
|
369
|
-
combinedSystemInstruction: typeof record.combinedSystemInstruction === 'string' && record.combinedSystemInstruction.trim().length
|
|
370
|
-
? record.combinedSystemInstruction
|
|
371
|
-
: undefined,
|
|
372
|
-
latestUserInstruction: typeof record.latestUserInstruction === 'string' && record.latestUserInstruction.trim().length
|
|
373
|
-
? record.latestUserInstruction
|
|
374
|
-
: undefined,
|
|
375
|
-
originalSystemMessages: systemMessages
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
function stripRoutingTagsFromText(text) {
|
|
379
|
-
if (typeof text !== 'string') {
|
|
380
|
-
return typeof text === 'string' ? text : String(text ?? '');
|
|
381
|
-
}
|
|
382
|
-
// NOTE: Do not strip <**...**> here. Routing instructions must reach the
|
|
383
|
-
// virtual router so allowlist/sticky directives can be applied. The router
|
|
384
|
-
// will clean them from messages after parsing.
|
|
385
|
-
return text;
|
|
386
|
-
}
|
|
387
|
-
function sanitizeBridgeHistory(history) {
|
|
388
|
-
if (!history || typeof history !== 'object') {
|
|
389
|
-
return history;
|
|
390
|
-
}
|
|
391
|
-
if (Array.isArray(history.input)) {
|
|
392
|
-
for (const entry of history.input) {
|
|
393
|
-
if (!entry || typeof entry !== 'object')
|
|
394
|
-
continue;
|
|
395
|
-
const blocks = entry.content;
|
|
396
|
-
if (!Array.isArray(blocks))
|
|
397
|
-
continue;
|
|
398
|
-
for (const block of blocks) {
|
|
399
|
-
if (!block || typeof block !== 'object')
|
|
400
|
-
continue;
|
|
401
|
-
const record = block;
|
|
402
|
-
if (typeof record.text === 'string') {
|
|
403
|
-
record.text = stripRoutingTagsFromText(record.text);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
if (typeof history.combinedSystemInstruction === 'string') {
|
|
409
|
-
history.combinedSystemInstruction = stripRoutingTagsFromText(history.combinedSystemInstruction);
|
|
410
|
-
}
|
|
411
|
-
if (typeof history.latestUserInstruction === 'string') {
|
|
412
|
-
history.latestUserInstruction = stripRoutingTagsFromText(history.latestUserInstruction);
|
|
413
|
-
}
|
|
414
|
-
if (Array.isArray(history.originalSystemMessages)) {
|
|
415
|
-
history.originalSystemMessages = history.originalSystemMessages.map((msg) => stripRoutingTagsFromText(msg));
|
|
416
|
-
}
|
|
417
|
-
return history;
|
|
418
|
-
}
|
|
419
|
-
function mergeResponsesTools(originalTools, fromChat) {
|
|
420
|
-
const result = [];
|
|
421
|
-
const byKey = new Map();
|
|
422
|
-
const norm = (value) => {
|
|
423
|
-
if (typeof value !== 'string')
|
|
424
|
-
return undefined;
|
|
425
|
-
const trimmed = value.trim();
|
|
426
|
-
return trimmed.length ? trimmed.toLowerCase() : undefined;
|
|
427
|
-
};
|
|
428
|
-
const register = (tool) => {
|
|
429
|
-
const fn = tool.function ?? tool.function;
|
|
430
|
-
const baseName = norm(fn?.name ?? tool.name);
|
|
431
|
-
const typeName = norm(tool.type);
|
|
432
|
-
const key = baseName || typeName;
|
|
433
|
-
if (!key || byKey.has(key))
|
|
434
|
-
return;
|
|
435
|
-
byKey.set(key, tool);
|
|
436
|
-
result.push(tool);
|
|
437
|
-
};
|
|
438
|
-
if (Array.isArray(fromChat)) {
|
|
439
|
-
for (const t of fromChat) {
|
|
440
|
-
if (t && typeof t === 'object') {
|
|
441
|
-
register(t);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
if (Array.isArray(originalTools)) {
|
|
446
|
-
for (const t of originalTools) {
|
|
447
|
-
if (!t || typeof t !== 'object')
|
|
448
|
-
continue;
|
|
449
|
-
const typeName = norm(t.type);
|
|
450
|
-
const isWebSearch = typeName === 'web_search' || (typeof typeName === 'string' && typeName.startsWith('web_search'));
|
|
451
|
-
if (!isWebSearch) {
|
|
452
|
-
// 目前仅恢复原始载荷中的 builtin web_search 工具,其它非函数工具保持忽略,避免意外改变行为。
|
|
453
|
-
continue;
|
|
454
|
-
}
|
|
455
|
-
register(t);
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
return result.length ? result : undefined;
|
|
162
|
+
return normalizeBridgeHistorySeedWithNative(seed);
|
|
459
163
|
}
|
|
460
164
|
export function buildResponsesRequestFromChat(payload, ctx, extras) {
|
|
461
165
|
const chat = unwrapData(payload);
|
|
462
166
|
const out = {};
|
|
463
|
-
const
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
const metaCarrier = ctx.metadata && typeof ctx.metadata === 'object'
|
|
468
|
-
? ctx.metadata
|
|
469
|
-
: undefined;
|
|
470
|
-
const rt = readRuntimeMetadata(metaCarrier ?? null);
|
|
471
|
-
if (rt && typeof rt === 'object') {
|
|
472
|
-
if (rt.forceWebSearch === true) {
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
475
|
-
const webSearch = rt.webSearch;
|
|
476
|
-
if (webSearch && typeof webSearch === 'object' && webSearch.force === true) {
|
|
477
|
-
return true;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return false;
|
|
481
|
-
})();
|
|
167
|
+
const envelopeMetadata = ctx?.metadata && typeof ctx.metadata === 'object' ? ctx.metadata : undefined;
|
|
168
|
+
const requestMetadata = chat && typeof chat === 'object' && chat.metadata && typeof chat.metadata === 'object'
|
|
169
|
+
? chat.metadata
|
|
170
|
+
: undefined;
|
|
482
171
|
// 基本字段
|
|
483
172
|
out.model = chat.model;
|
|
484
|
-
// tools: 反向映射为 ResponsesToolDefinition 形状
|
|
485
|
-
const chatTools = Array.isArray(chat.tools) ? chat.tools : [];
|
|
486
|
-
// 对于 openai-responses upstream,内建 web_search 由官方服务器处理。
|
|
487
|
-
// Chat 侧注入的 server-side web_search 函数(带 engine/query/recency/count)
|
|
488
|
-
// 仅用于非 Responses provider 的 server-tool 回环;在这里构造真正的
|
|
489
|
-
// `/v1/responses` 请求时,需要:
|
|
490
|
-
// 1) 不再把函数版 web_search 透传上游;
|
|
491
|
-
// 2) 若检测到 Chat 侧启用了 web_search 且原始请求中没有 builtin web_search,
|
|
492
|
-
// 则补一个 `{ type: "web_search" }` 内建工具给 OpenAI Responses。
|
|
493
|
-
const hasServerSideWebSearch = !forceWebSearch && chatTools.some((tool) => {
|
|
494
|
-
const fn = tool && typeof tool === 'object' ? tool.function : undefined;
|
|
495
|
-
const name = typeof fn?.name === 'string' ? fn.name.trim().toLowerCase() : '';
|
|
496
|
-
return name === 'web_search';
|
|
497
|
-
});
|
|
498
|
-
const toolsForBridge = hasServerSideWebSearch
|
|
499
|
-
? chatTools.filter((tool) => {
|
|
500
|
-
const fn = tool && typeof tool === 'object' ? tool.function : undefined;
|
|
501
|
-
const name = typeof fn?.name === 'string' ? fn.name.trim().toLowerCase() : '';
|
|
502
|
-
return name !== 'web_search';
|
|
503
|
-
})
|
|
504
|
-
: chatTools;
|
|
505
|
-
const responsesToolsFromChat = mapChatToolsToBridge(toolsForBridge, {
|
|
506
|
-
sanitizeName: sanitizeResponsesFunctionName
|
|
507
|
-
});
|
|
508
|
-
// Prefer Chat‑normalized tools, but if the original Responses payload carried
|
|
509
|
-
// non‑function tools (such as builtin `web_search`), merge them back so that
|
|
510
|
-
// upstream `/v1/responses` providers see their original tool definitions.
|
|
511
|
-
const originalTools = Array.isArray(ctx?.toolsRaw) ? ctx.toolsRaw : undefined;
|
|
512
|
-
let mergedTools = mergeResponsesTools(originalTools, responsesToolsFromChat);
|
|
513
|
-
if (hasServerSideWebSearch) {
|
|
514
|
-
const normalizeType = (value) => typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
515
|
-
const hasBuiltinWebSearch = (mergedTools && mergedTools.some((tool) => normalizeType(tool.type) === 'web_search')) ||
|
|
516
|
-
(originalTools && originalTools.some((tool) => normalizeType(tool.type) === 'web_search'));
|
|
517
|
-
if (!hasBuiltinWebSearch) {
|
|
518
|
-
const injected = { type: 'web_search' };
|
|
519
|
-
mergedTools = mergedTools ? [...mergedTools, injected] : [injected];
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
if (mergedTools?.length) {
|
|
523
|
-
out.tools = mergedTools;
|
|
524
|
-
}
|
|
525
|
-
const passthroughKeys = [
|
|
526
|
-
'temperature',
|
|
527
|
-
'tool_choice',
|
|
528
|
-
'parallel_tool_calls',
|
|
529
|
-
'response_format',
|
|
530
|
-
'user',
|
|
531
|
-
'top_p',
|
|
532
|
-
'prompt_cache_key',
|
|
533
|
-
'reasoning',
|
|
534
|
-
'logit_bias',
|
|
535
|
-
'seed'
|
|
536
|
-
];
|
|
537
|
-
for (const key of passthroughKeys) {
|
|
538
|
-
if (chat[key] !== undefined)
|
|
539
|
-
out[key] = chat[key];
|
|
540
|
-
}
|
|
541
173
|
let messages = Array.isArray(chat.messages) ? chat.messages : [];
|
|
542
174
|
let bridgeMetadata;
|
|
543
175
|
try {
|
|
544
176
|
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
545
177
|
const policyActions = resolvePolicyActions(bridgePolicy, 'request_outbound');
|
|
546
178
|
if (policyActions?.length) {
|
|
547
|
-
const actionState =
|
|
548
|
-
messages,
|
|
549
|
-
rawRequest: chat
|
|
550
|
-
});
|
|
551
|
-
runBridgeActionPipeline({
|
|
179
|
+
const actionState = runNativeResponsesBridgePipeline({
|
|
552
180
|
stage: 'request_outbound',
|
|
553
181
|
actions: policyActions,
|
|
554
|
-
protocol: bridgePolicy?.protocol ?? 'openai-responses',
|
|
555
|
-
moduleType: bridgePolicy?.moduleType ?? 'openai-responses',
|
|
182
|
+
protocol: (bridgePolicy?.protocol ?? 'openai-responses'),
|
|
183
|
+
moduleType: (bridgePolicy?.moduleType ?? 'openai-responses'),
|
|
556
184
|
requestId: ctx?.requestId,
|
|
557
|
-
|
|
185
|
+
messages,
|
|
186
|
+
rawRequest: chat
|
|
558
187
|
});
|
|
559
188
|
messages = actionState.messages;
|
|
560
189
|
if (actionState.metadata && Object.keys(actionState.metadata).length) {
|
|
@@ -565,78 +194,67 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
|
|
|
565
194
|
catch {
|
|
566
195
|
// ignore policy errors
|
|
567
196
|
}
|
|
568
|
-
const envelopeMetadata = ctx?.metadata && typeof ctx.metadata === 'object' ? ctx.metadata : undefined;
|
|
569
197
|
const metadataExtraFields = extractMetadataExtraFields(envelopeMetadata);
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
198
|
+
const bridgeDecisions = resolveResponsesRequestBridgeDecisionsWithNative({
|
|
199
|
+
context: ctx && typeof ctx === 'object' ? ctx : undefined,
|
|
200
|
+
requestMetadata,
|
|
201
|
+
envelopeMetadata,
|
|
202
|
+
bridgeMetadata,
|
|
203
|
+
extraBridgeHistory: extras?.bridgeHistory
|
|
204
|
+
});
|
|
205
|
+
const forceWebSearch = bridgeDecisions.forceWebSearch === true;
|
|
206
|
+
const toolCallIdStyle = bridgeDecisions.toolCallIdStyle;
|
|
207
|
+
const historySeed = bridgeDecisions.historySeed;
|
|
208
|
+
// tools: 反向映射为 ResponsesToolDefinition 形状
|
|
209
|
+
const chatTools = Array.isArray(chat.tools) ? chat.tools : [];
|
|
210
|
+
const responsesToolsFromChat = mapChatToolsToBridge(chatTools, {
|
|
211
|
+
sanitizeName: sanitizeResponsesFunctionName
|
|
212
|
+
});
|
|
213
|
+
const originalTools = Array.isArray(ctx?.toolsRaw) ? ctx.toolsRaw : undefined;
|
|
214
|
+
const resolvedBridgeTools = resolveResponsesBridgeToolsWithNative({
|
|
215
|
+
originalTools: Array.isArray(originalTools) ? originalTools : undefined,
|
|
216
|
+
chatTools: Array.isArray(responsesToolsFromChat) ? responsesToolsFromChat : undefined,
|
|
217
|
+
hasServerSideWebSearch: !forceWebSearch,
|
|
218
|
+
passthroughKeys: [
|
|
219
|
+
'temperature',
|
|
220
|
+
'tool_choice',
|
|
221
|
+
'parallel_tool_calls',
|
|
222
|
+
'response_format',
|
|
223
|
+
'user',
|
|
224
|
+
'top_p',
|
|
225
|
+
'prompt_cache_key',
|
|
226
|
+
'reasoning',
|
|
227
|
+
'logit_bias',
|
|
228
|
+
'seed'
|
|
229
|
+
],
|
|
230
|
+
request: chat
|
|
231
|
+
});
|
|
232
|
+
const mergedTools = resolvedBridgeTools.mergedTools;
|
|
233
|
+
if (mergedTools?.length) {
|
|
234
|
+
out.tools = mergedTools;
|
|
235
|
+
}
|
|
236
|
+
if (resolvedBridgeTools.request && typeof resolvedBridgeTools.request === 'object') {
|
|
237
|
+
for (const [key, value] of Object.entries(resolvedBridgeTools.request)) {
|
|
238
|
+
if (out[key] === undefined) {
|
|
239
|
+
out[key] = value;
|
|
240
|
+
}
|
|
586
241
|
}
|
|
587
|
-
|
|
588
|
-
const historySeed = sanitizeBridgeHistory(normalizeBridgeHistory(extras?.bridgeHistory)) ??
|
|
589
|
-
sanitizeBridgeHistory(normalizeBridgeHistory(fallbackHistory)) ??
|
|
590
|
-
sanitizeBridgeHistory(normalizeBridgeHistory(bridgeMetadata?.bridgeHistory)) ??
|
|
591
|
-
sanitizeBridgeHistory(normalizeBridgeHistory(envelopeMetadata?.bridgeHistory)) ??
|
|
592
|
-
sanitizeBridgeHistory(fallbackHistory);
|
|
242
|
+
}
|
|
593
243
|
const history = historySeed ??
|
|
594
|
-
|
|
244
|
+
buildBridgeHistoryWithNative({
|
|
595
245
|
messages,
|
|
596
246
|
tools: Array.isArray(out.tools) ? out.tools : undefined
|
|
597
|
-
})
|
|
247
|
+
});
|
|
598
248
|
const callIdTransformer = createToolCallIdTransformer(toolCallIdStyle);
|
|
599
249
|
if (callIdTransformer) {
|
|
600
250
|
enforceToolCallIdStyle(history.input, callIdTransformer);
|
|
601
251
|
}
|
|
602
252
|
const { input, combinedSystemInstruction, originalSystemMessages } = history;
|
|
603
|
-
const instructionCandidates = [
|
|
604
|
-
typeof ctx?.systemInstruction === 'string' && ctx.systemInstruction.trim().length ? ctx.systemInstruction.trim() : undefined,
|
|
605
|
-
typeof extras?.systemInstruction === 'string' && extras.systemInstruction.trim().length ? extras.systemInstruction.trim() : undefined,
|
|
606
|
-
typeof envelopeMetadata?.systemInstruction === 'string' && envelopeMetadata.systemInstruction.trim().length ? envelopeMetadata.systemInstruction.trim() : undefined,
|
|
607
|
-
combinedSystemInstruction && combinedSystemInstruction.length > 0 ? combinedSystemInstruction : undefined
|
|
608
|
-
];
|
|
609
|
-
let resolvedInstruction;
|
|
610
|
-
for (const candidate of instructionCandidates) {
|
|
611
|
-
if (candidate && candidate.length) {
|
|
612
|
-
resolvedInstruction = candidate;
|
|
613
|
-
break;
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
if (resolvedInstruction && resolvedInstruction.length) {
|
|
617
|
-
const reasoningSegments = (() => {
|
|
618
|
-
if (!ctx || typeof ctx !== 'object')
|
|
619
|
-
return [];
|
|
620
|
-
const ctxAny = ctx;
|
|
621
|
-
const raw = ctxAny.__rcc_reasoning_instructions_segments;
|
|
622
|
-
if (!raw)
|
|
623
|
-
return [];
|
|
624
|
-
const segments = Array.isArray(raw) ? raw : [raw];
|
|
625
|
-
return segments
|
|
626
|
-
.map((entry) => (typeof entry === 'string' ? entry.trim() : String(entry ?? '').trim()))
|
|
627
|
-
.filter((entry) => entry.length > 0);
|
|
628
|
-
})();
|
|
629
|
-
if (reasoningSegments.length) {
|
|
630
|
-
const combined = `${reasoningSegments.join('\n')}\n${resolvedInstruction}`.trim();
|
|
631
|
-
out.instructions = combined;
|
|
632
|
-
out.instructions_is_raw = true;
|
|
633
|
-
}
|
|
634
|
-
else {
|
|
635
|
-
out.instructions = resolvedInstruction;
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
253
|
// 不追加 metadata,以便 roundtrip 与原始 payload 对齐;系统提示直接写入 instructions。
|
|
639
|
-
const upstreamInput =
|
|
254
|
+
const upstreamInput = filterBridgeInputForUpstreamWithNative({
|
|
255
|
+
input,
|
|
256
|
+
allowToolCallId: toolCallIdStyle === 'preserve'
|
|
257
|
+
}).input;
|
|
640
258
|
if (upstreamInput.length) {
|
|
641
259
|
out.input = upstreamInput;
|
|
642
260
|
}
|
|
@@ -644,144 +262,44 @@ export function buildResponsesRequestFromChat(payload, ctx, extras) {
|
|
|
644
262
|
const streamFromParameters = chat?.parameters && typeof chat.parameters?.stream === 'boolean'
|
|
645
263
|
? chat.parameters.stream
|
|
646
264
|
: undefined;
|
|
647
|
-
const resolvedStream = typeof ctx?.stream === 'boolean'
|
|
648
|
-
? ctx.stream
|
|
649
|
-
: (streamFromChat !== undefined ? streamFromChat : streamFromParameters);
|
|
650
|
-
if (resolvedStream === true) {
|
|
651
|
-
out.stream = true;
|
|
652
|
-
}
|
|
653
|
-
else if (resolvedStream === false) {
|
|
654
|
-
out.stream = false;
|
|
655
|
-
}
|
|
656
|
-
if (ctx?.include !== undefined && out.include === undefined) {
|
|
657
|
-
out.include = ctx.include;
|
|
658
|
-
}
|
|
659
|
-
else if (metadataExtraFields?.include !== undefined && out.include === undefined) {
|
|
660
|
-
out.include = metadataExtraFields.include;
|
|
661
|
-
}
|
|
662
265
|
const stripHostFields = shouldStripHostManagedFields(ctx);
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
else if (metadataExtraFields?.service_tier !== undefined && out.service_tier === undefined) {
|
|
698
|
-
out.service_tier = metadataExtraFields.service_tier;
|
|
699
|
-
}
|
|
700
|
-
if (ctx?.truncation !== undefined && out.truncation === undefined) {
|
|
701
|
-
out.truncation = ctx.truncation;
|
|
702
|
-
}
|
|
703
|
-
else if (metadataExtraFields?.truncation !== undefined && out.truncation === undefined) {
|
|
704
|
-
out.truncation = metadataExtraFields.truncation;
|
|
705
|
-
}
|
|
706
|
-
if (ctx?.metadata && Object.keys(ctx.metadata).length) {
|
|
707
|
-
out.metadata = { ...ctx.metadata };
|
|
708
|
-
}
|
|
709
|
-
else if (isPlainObject(metadataExtraFields?.metadata)) {
|
|
710
|
-
out.metadata = { ...metadataExtraFields.metadata };
|
|
711
|
-
}
|
|
712
|
-
// Some upstream `/v1/responses` providers reject an unknown top-level `parameters` object
|
|
713
|
-
// (e.g. HTTP 400: Unsupported parameter: parameters).
|
|
714
|
-
// We accept `parameters` as an internal carrier (capturedChatRequest.parameters, followups),
|
|
715
|
-
// but must flatten supported fields into the top-level request shape before sending upstream.
|
|
716
|
-
const parametersCandidate = (ctx?.parameters && Object.keys(ctx.parameters).length ? { ...ctx.parameters } : undefined) ??
|
|
717
|
-
(chat.parameters && typeof chat.parameters === 'object' && !Array.isArray(chat.parameters) ? { ...chat.parameters } : undefined) ??
|
|
718
|
-
(isPlainObject(metadataExtraFields?.parameters) ? { ...metadataExtraFields.parameters } : undefined);
|
|
719
|
-
if (parametersCandidate) {
|
|
720
|
-
const allowed = new Set([
|
|
721
|
-
// Common OpenAI Responses parameters
|
|
722
|
-
'temperature',
|
|
723
|
-
'top_p',
|
|
724
|
-
'max_output_tokens',
|
|
725
|
-
'seed',
|
|
726
|
-
'logit_bias',
|
|
727
|
-
'user',
|
|
728
|
-
'parallel_tool_calls',
|
|
729
|
-
'tool_choice',
|
|
730
|
-
'response_format',
|
|
731
|
-
'service_tier',
|
|
732
|
-
'truncation',
|
|
733
|
-
'include',
|
|
734
|
-
'store',
|
|
735
|
-
'prompt_cache_key',
|
|
736
|
-
'reasoning',
|
|
737
|
-
'stream'
|
|
738
|
-
]);
|
|
739
|
-
// Back-compat: StandardizedRequest uses max_tokens; map it to Responses max_output_tokens.
|
|
740
|
-
if (parametersCandidate.max_output_tokens === undefined && parametersCandidate.max_tokens !== undefined) {
|
|
741
|
-
parametersCandidate.max_output_tokens = parametersCandidate.max_tokens;
|
|
742
|
-
}
|
|
743
|
-
for (const [key, value] of Object.entries(parametersCandidate)) {
|
|
744
|
-
if (!allowed.has(key))
|
|
745
|
-
continue;
|
|
746
|
-
if (key === 'stream')
|
|
747
|
-
continue; // handled by resolvedStream above
|
|
748
|
-
if (out[key] === undefined) {
|
|
749
|
-
out[key] = value;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
}
|
|
266
|
+
const preparedEnvelope = prepareResponsesRequestEnvelopeWithNative({
|
|
267
|
+
request: out,
|
|
268
|
+
contextSystemInstruction: ctx?.systemInstruction,
|
|
269
|
+
extraSystemInstruction: extras?.systemInstruction,
|
|
270
|
+
metadataSystemInstruction: envelopeMetadata?.systemInstruction,
|
|
271
|
+
combinedSystemInstruction,
|
|
272
|
+
reasoningInstructionSegments: ctx?.__rcc_reasoning_instructions_segments,
|
|
273
|
+
contextParameters: ctx?.parameters,
|
|
274
|
+
chatParameters: chat.parameters,
|
|
275
|
+
metadataParameters: metadataExtraFields?.parameters,
|
|
276
|
+
contextStream: ctx?.stream,
|
|
277
|
+
metadataStream: metadataExtraFields?.stream,
|
|
278
|
+
chatStream: streamFromChat,
|
|
279
|
+
chatParametersStream: streamFromParameters,
|
|
280
|
+
contextInclude: ctx?.include,
|
|
281
|
+
metadataInclude: metadataExtraFields?.include,
|
|
282
|
+
contextStore: ctx?.store,
|
|
283
|
+
metadataStore: metadataExtraFields?.store,
|
|
284
|
+
stripHostFields,
|
|
285
|
+
contextToolChoice: ctx?.toolChoice,
|
|
286
|
+
metadataToolChoice: metadataExtraFields?.tool_choice,
|
|
287
|
+
contextParallelToolCalls: ctx?.parallelToolCalls,
|
|
288
|
+
metadataParallelToolCalls: metadataExtraFields?.parallel_tool_calls,
|
|
289
|
+
contextResponseFormat: ctx?.responseFormat,
|
|
290
|
+
metadataResponseFormat: metadataExtraFields?.response_format,
|
|
291
|
+
contextServiceTier: ctx?.serviceTier,
|
|
292
|
+
metadataServiceTier: metadataExtraFields?.service_tier,
|
|
293
|
+
contextTruncation: ctx?.truncation,
|
|
294
|
+
metadataTruncation: metadataExtraFields?.truncation,
|
|
295
|
+
contextMetadata: ctx?.metadata,
|
|
296
|
+
metadataMetadata: metadataExtraFields?.metadata
|
|
297
|
+
});
|
|
298
|
+
Object.assign(out, preparedEnvelope.request);
|
|
299
|
+
delete out.parameters;
|
|
753
300
|
ensureBridgeInstructions(out);
|
|
754
301
|
return { request: out, originalSystemMessages };
|
|
755
302
|
}
|
|
756
|
-
function readToolCallIdStyleFromContext(ctx) {
|
|
757
|
-
if (!ctx) {
|
|
758
|
-
return undefined;
|
|
759
|
-
}
|
|
760
|
-
const direct = normalizeToolCallIdStyleCandidate(ctx.toolCallIdStyle);
|
|
761
|
-
if (direct) {
|
|
762
|
-
return direct;
|
|
763
|
-
}
|
|
764
|
-
if (ctx.metadata && typeof ctx.metadata === 'object') {
|
|
765
|
-
const fromMetadata = normalizeToolCallIdStyleCandidate(ctx.metadata.toolCallIdStyle);
|
|
766
|
-
if (fromMetadata) {
|
|
767
|
-
return fromMetadata;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
return undefined;
|
|
771
|
-
}
|
|
772
|
-
function normalizeToolCallIdStyleCandidate(candidate) {
|
|
773
|
-
if (typeof candidate !== 'string') {
|
|
774
|
-
return undefined;
|
|
775
|
-
}
|
|
776
|
-
const normalized = candidate.trim().toLowerCase();
|
|
777
|
-
if (normalized === 'fc') {
|
|
778
|
-
return 'fc';
|
|
779
|
-
}
|
|
780
|
-
if (normalized === 'preserve') {
|
|
781
|
-
return 'preserve';
|
|
782
|
-
}
|
|
783
|
-
return undefined;
|
|
784
|
-
}
|
|
785
303
|
function cloneBridgeEntries(entries) {
|
|
786
304
|
if (!Array.isArray(entries)) {
|
|
787
305
|
return undefined;
|