@jsonstudio/llms 0.6.1892 → 0.6.2172
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/compat/actions/deepseek-web-request.js +16 -2
- package/dist/conversion/compat/actions/deepseek-web-response.d.ts +7 -1
- package/dist/conversion/compat/actions/deepseek-web-response.js +302 -40
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.d.ts +5 -0
- package/dist/conversion/compat/actions/harvest-tool-calls-from-text.js +7 -4
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.d.ts +1 -0
- package/dist/conversion/compat/actions/iflow-tool-text-fallback.js +12 -0
- package/dist/conversion/compat/actions/strip-orphan-function-calls-tag.js +1 -1
- package/dist/conversion/compat/actions/tool-text-request-guidance.d.ts +9 -0
- package/dist/conversion/compat/actions/tool-text-request-guidance.js +177 -0
- package/dist/conversion/compat/antigravity-session-signature.d.ts +6 -0
- package/dist/conversion/compat/antigravity-session-signature.js +15 -0
- package/dist/conversion/compat/profiles/chat-deepseek-web.json +52 -1
- package/dist/conversion/compat/profiles/chat-glm.json +22 -0
- package/dist/conversion/compat/profiles/chat-iflow.json +4 -0
- package/dist/conversion/hub/operation-table/semantic-mappers/gemini-mapper.js +13 -27
- package/dist/conversion/hub/operation-table/semantic-mappers/responses-mapper.js +10 -1
- package/dist/conversion/hub/pipeline/compat/compat-pipeline-executor.js +13 -4
- package/dist/conversion/hub/pipeline/compat/compat-profile-resolver.js +1 -53
- package/dist/conversion/hub/pipeline/compat/compat-types.d.ts +8 -0
- package/dist/conversion/hub/pipeline/hub-pipeline.js +8 -4
- package/dist/conversion/hub/pipeline/stages/req_inbound/req_inbound_stage3_context_capture/index.js +191 -9
- package/dist/conversion/hub/pipeline/stages/resp_inbound/resp_inbound_stage1_sse_decode/index.js +118 -15
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage1_tool_governance/index.js +65 -2
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.d.ts +34 -0
- package/dist/conversion/hub/pipeline/stages/resp_process/resp_process_stage3_servertool_orchestration/index.js +75 -0
- package/dist/conversion/hub/process/chat-process.js +85 -18
- package/dist/conversion/hub/response/provider-response.js +21 -50
- package/dist/conversion/hub/response/response-runtime.js +71 -10
- package/dist/conversion/responses/responses-openai-bridge/response-payload.d.ts +3 -0
- package/dist/conversion/responses/responses-openai-bridge/response-payload.js +576 -0
- package/dist/conversion/responses/responses-openai-bridge/types.d.ts +42 -0
- package/dist/conversion/responses/responses-openai-bridge/types.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +3 -44
- package/dist/conversion/responses/responses-openai-bridge.js +193 -504
- package/dist/conversion/shared/anthropic-message-utils.js +82 -2
- package/dist/conversion/shared/bridge-message-utils.js +92 -39
- package/dist/conversion/shared/snapshot-hooks.js +8 -13
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-apply-patch.js +129 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.d.ts +4 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-json.js +637 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.d.ts +21 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-shared.js +177 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-transcript.js +385 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.d.ts +10 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors-xml.js +602 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.d.ts +5 -0
- package/dist/conversion/shared/text-markup-normalizer/extractors.js +4 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.d.ts +2 -0
- package/dist/conversion/shared/text-markup-normalizer/normalize.js +76 -0
- package/dist/conversion/shared/text-markup-normalizer.d.ts +3 -25
- package/dist/conversion/shared/text-markup-normalizer.js +2 -1386
- package/dist/conversion/shared/tool-governor.js +136 -10
- package/dist/filters/utils/snapshot-writer.js +3 -3
- package/dist/router/virtual-router/bootstrap/auth-utils.d.ts +6 -0
- package/dist/router/virtual-router/bootstrap/auth-utils.js +288 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.d.ts +11 -0
- package/dist/router/virtual-router/bootstrap/claude-code-helpers.js +18 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/config-defaults.js +13 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/config-normalizers.js +106 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.d.ts +7 -0
- package/dist/router/virtual-router/bootstrap/profile-builder.js +68 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.d.ts +40 -0
- package/dist/router/virtual-router/bootstrap/provider-normalization.js +212 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.d.ts +15 -0
- package/dist/router/virtual-router/bootstrap/responses-helpers.js +65 -0
- package/dist/router/virtual-router/bootstrap/routing-config.d.ts +23 -0
- package/dist/router/virtual-router/bootstrap/routing-config.js +293 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.d.ts +12 -0
- package/dist/router/virtual-router/bootstrap/streaming-helpers.js +128 -0
- package/dist/router/virtual-router/bootstrap/utils.d.ts +5 -0
- package/dist/router/virtual-router/bootstrap/utils.js +41 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.d.ts +4 -0
- package/dist/router/virtual-router/bootstrap/web-search-config.js +131 -0
- package/dist/router/virtual-router/bootstrap.d.ts +0 -4
- package/dist/router/virtual-router/bootstrap.js +31 -1275
- package/dist/router/virtual-router/classifier.js +32 -14
- package/dist/router/virtual-router/engine/antigravity/alias-lease.js +2 -2
- package/dist/router/virtual-router/engine/cooldown-manager.d.ts +34 -0
- package/dist/router/virtual-router/engine/cooldown-manager.js +118 -0
- package/dist/router/virtual-router/engine/route-analytics.d.ts +28 -0
- package/dist/router/virtual-router/engine/route-analytics.js +44 -0
- package/dist/router/virtual-router/engine/routing-pools/index.js +165 -4
- package/dist/router/virtual-router/engine/sticky-session-manager.d.ts +29 -0
- package/dist/router/virtual-router/engine/sticky-session-manager.js +55 -0
- package/dist/router/virtual-router/engine-logging.d.ts +42 -1
- package/dist/router/virtual-router/engine-logging.js +82 -15
- package/dist/router/virtual-router/engine-selection/multimodal-capability.d.ts +3 -0
- package/dist/router/virtual-router/engine-selection/multimodal-capability.js +26 -0
- package/dist/router/virtual-router/engine-selection/route-utils.js +6 -2
- package/dist/router/virtual-router/engine-selection/selection-deps.d.ts +1 -0
- package/dist/router/virtual-router/engine-selection/tier-selection.js +31 -1
- package/dist/router/virtual-router/engine.d.ts +21 -7
- package/dist/router/virtual-router/engine.js +198 -194
- package/dist/router/virtual-router/features.js +12 -4
- package/dist/router/virtual-router/message-utils.d.ts +8 -0
- package/dist/router/virtual-router/message-utils.js +170 -45
- package/dist/router/virtual-router/pre-command-file-resolver.js +40 -2
- package/dist/router/virtual-router/routing-instructions.d.ts +8 -0
- package/dist/router/virtual-router/routing-instructions.js +18 -2
- package/dist/router/virtual-router/routing-stop-message-actions.js +34 -10
- package/dist/router/virtual-router/routing-stop-message-state-codec.d.ts +2 -0
- package/dist/router/virtual-router/routing-stop-message-state-codec.js +50 -1
- package/dist/router/virtual-router/stop-message-state-sync.d.ts +1 -1
- package/dist/router/virtual-router/stop-message-state-sync.js +3 -0
- package/dist/router/virtual-router/token-counter.js +51 -10
- package/dist/router/virtual-router/tool-signals.js +4 -0
- package/dist/router/virtual-router/types.d.ts +15 -0
- package/dist/servertool/clock/session-scope.d.ts +3 -0
- package/dist/servertool/clock/session-scope.js +52 -0
- package/dist/servertool/clock/state.js +9 -0
- package/dist/servertool/clock/tasks.js +12 -1
- package/dist/servertool/clock/types.d.ts +3 -0
- package/dist/servertool/engine.js +177 -31
- package/dist/servertool/handlers/clock-auto.js +2 -8
- package/dist/servertool/handlers/clock.js +6 -9
- package/dist/servertool/handlers/recursive-detection-guard.js +53 -14
- package/dist/servertool/handlers/stop-message-auto/blocked-report.d.ts +16 -0
- package/dist/servertool/handlers/stop-message-auto/blocked-report.js +349 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.d.ts +23 -0
- package/dist/servertool/handlers/stop-message-auto/iflow-followup.js +503 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.d.ts +38 -0
- package/dist/servertool/handlers/stop-message-auto/routing-state.js +149 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.d.ts +67 -0
- package/dist/servertool/handlers/stop-message-auto/runtime-utils.js +387 -0
- package/dist/servertool/handlers/stop-message-auto.d.ts +1 -1
- package/dist/servertool/handlers/stop-message-auto.js +80 -556
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.d.ts +18 -0
- package/dist/servertool/handlers/stop-message-stage-policy/bd-runtime.js +398 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/decision.js +127 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.d.ts +2 -0
- package/dist/servertool/handlers/stop-message-stage-policy/observation.js +179 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.d.ts +4 -0
- package/dist/servertool/handlers/stop-message-stage-policy/templates.js +96 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.d.ts +9 -0
- package/dist/servertool/handlers/stop-message-stage-policy/text-utils.js +89 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.d.ts +59 -0
- package/dist/servertool/handlers/stop-message-stage-policy/types.js +1 -0
- package/dist/servertool/handlers/stop-message-stage-policy.d.ts +3 -43
- package/dist/servertool/handlers/stop-message-stage-policy.js +2 -684
- package/dist/servertool/handlers/web-search.js +117 -0
- package/dist/servertool/server-side-tools.d.ts +0 -1
- package/dist/servertool/server-side-tools.js +4 -3
- package/dist/sse/sse-to-json/builders/response-builder.js +16 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +1 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +110 -37
- package/dist/telemetry/stats-center.d.ts +9 -0
- package/dist/telemetry/stats-center.js +29 -1
- package/dist/tools/apply-patch/structured/coercion.js +3 -11
- package/dist/tools/exec-command/validator.d.ts +1 -0
- package/dist/tools/exec-command/validator.js +132 -0
- package/dist/tools/tool-registry.d.ts +1 -0
- package/dist/tools/tool-registry.js +1 -1
- package/package.json +1 -1
|
@@ -1,18 +1,18 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
1
4
|
import { ensureBridgeInstructions } from '../shared/bridge-instructions.js';
|
|
2
5
|
import { evaluateResponsesHostPolicy } from './responses-host-policy.js';
|
|
3
6
|
import { convertMessagesToBridgeInput, convertBridgeInputToChatMessages } from '../shared/bridge-message-utils.js';
|
|
4
|
-
import { createToolCallIdTransformer, enforceToolCallIdStyle, resolveToolCallIdStyle,
|
|
7
|
+
import { createToolCallIdTransformer, enforceToolCallIdStyle, resolveToolCallIdStyle, sanitizeResponsesFunctionName } from '../shared/responses-tool-utils.js';
|
|
5
8
|
import { mapBridgeToolsToChat, mapChatToolsToBridge } from '../shared/tool-mapping.js';
|
|
6
9
|
import { ProviderProtocolError } from '../shared/errors.js';
|
|
7
10
|
import { readRuntimeMetadata } from '../shared/runtime-metadata.js';
|
|
8
11
|
import { clampResponsesInputItemId } from '../shared/bridge-id-utils.js';
|
|
12
|
+
import { isImagePath } from '../shared/media.js';
|
|
9
13
|
// --- Utilities (ported strictly) ---
|
|
10
|
-
import { canonicalizeChatResponseTools } from '../shared/tool-canonicalizer.js';
|
|
11
|
-
import { normalizeMessageReasoningTools } from '../shared/reasoning-tool-normalizer.js';
|
|
12
14
|
import { createBridgeActionState, runBridgeActionPipeline } from '../shared/bridge-actions.js';
|
|
13
15
|
import { resolveBridgePolicy, resolvePolicyActions } from '../shared/bridge-policies.js';
|
|
14
|
-
import { buildResponsesOutputFromChat } from '../shared/responses-output-builder.js';
|
|
15
|
-
import { consumeResponsesPayloadSnapshot, consumeResponsesPassthrough } from '../shared/responses-reasoning-registry.js';
|
|
16
16
|
function isObject(v) {
|
|
17
17
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
18
18
|
}
|
|
@@ -45,7 +45,192 @@ function filterBridgeInputForUpstream(input, options) {
|
|
|
45
45
|
}
|
|
46
46
|
// normalizeTools unified in ../shared/args-mapping.ts
|
|
47
47
|
// --- Structured self-repair helpers for tool failures (Responses path) ---
|
|
48
|
-
|
|
48
|
+
const IMAGE_MIME_BY_EXT = {
|
|
49
|
+
'.png': 'image/png',
|
|
50
|
+
'.jpg': 'image/jpeg',
|
|
51
|
+
'.jpeg': 'image/jpeg',
|
|
52
|
+
'.gif': 'image/gif',
|
|
53
|
+
'.webp': 'image/webp',
|
|
54
|
+
'.bmp': 'image/bmp',
|
|
55
|
+
'.svg': 'image/svg+xml',
|
|
56
|
+
'.tif': 'image/tiff',
|
|
57
|
+
'.tiff': 'image/tiff',
|
|
58
|
+
'.ico': 'image/x-icon',
|
|
59
|
+
'.heic': 'image/heic',
|
|
60
|
+
'.jxl': 'image/jxl'
|
|
61
|
+
};
|
|
62
|
+
function decodeEscapedPathLikeText(input) {
|
|
63
|
+
if (!input || input.indexOf('\\') < 0) {
|
|
64
|
+
return input;
|
|
65
|
+
}
|
|
66
|
+
return input
|
|
67
|
+
.replace(/\\\//g, '/')
|
|
68
|
+
.replace(/\\u([0-9a-fA-F]{4})/g, (_m, hex) => {
|
|
69
|
+
const codepoint = Number.parseInt(hex, 16);
|
|
70
|
+
return Number.isFinite(codepoint) ? String.fromCharCode(codepoint) : '';
|
|
71
|
+
})
|
|
72
|
+
.replace(/\\x([0-9a-fA-F]{2})/g, (_m, hex) => {
|
|
73
|
+
const codepoint = Number.parseInt(hex, 16);
|
|
74
|
+
return Number.isFinite(codepoint) ? String.fromCharCode(codepoint) : '';
|
|
75
|
+
})
|
|
76
|
+
.replace(/\\\\/g, '\\');
|
|
77
|
+
}
|
|
78
|
+
function normalizeLocalImagePath(candidate) {
|
|
79
|
+
if (!candidate || typeof candidate !== 'string')
|
|
80
|
+
return null;
|
|
81
|
+
let value = decodeEscapedPathLikeText(candidate.trim());
|
|
82
|
+
if (!value)
|
|
83
|
+
return null;
|
|
84
|
+
if (/^https?:\/\//i.test(value))
|
|
85
|
+
return null;
|
|
86
|
+
if (value.toLowerCase().startsWith('file://')) {
|
|
87
|
+
try {
|
|
88
|
+
const parsed = new URL(value);
|
|
89
|
+
value = decodeURIComponent(parsed.pathname || '');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (!value)
|
|
96
|
+
return null;
|
|
97
|
+
if (value.startsWith('~')) {
|
|
98
|
+
value = path.join(os.homedir(), value.slice(1));
|
|
99
|
+
}
|
|
100
|
+
const isAbsolutePath = path.isAbsolute(value);
|
|
101
|
+
const normalized = isAbsolutePath ? path.normalize(value) : path.resolve(process.cwd(), value);
|
|
102
|
+
return isImagePath(normalized) ? normalized : null;
|
|
103
|
+
}
|
|
104
|
+
function collectLocalImagePathCandidates(text) {
|
|
105
|
+
if (typeof text !== 'string' || !text.trim())
|
|
106
|
+
return [];
|
|
107
|
+
const candidates = new Set();
|
|
108
|
+
const variants = [text, decodeEscapedPathLikeText(text)].filter(Boolean);
|
|
109
|
+
const quotedPathRegex = /(["'`])((?:\\.|(?!\1).)+)\1/g;
|
|
110
|
+
const barePathRegex = /(?:^|[\s(])((?:~|\/|\.\.?\/)[^\s"'`<>]+?\.(?:png|jpg|jpeg|gif|webp|bmp|svg|tiff?|ico|heic|jxl))(?=$|[\s),.;!?])/gi;
|
|
111
|
+
for (const variant of variants) {
|
|
112
|
+
let quotedMatch;
|
|
113
|
+
quotedPathRegex.lastIndex = 0;
|
|
114
|
+
while ((quotedMatch = quotedPathRegex.exec(variant)) !== null) {
|
|
115
|
+
const normalized = normalizeLocalImagePath(quotedMatch[2] || '');
|
|
116
|
+
if (normalized)
|
|
117
|
+
candidates.add(normalized);
|
|
118
|
+
}
|
|
119
|
+
let bareMatch;
|
|
120
|
+
barePathRegex.lastIndex = 0;
|
|
121
|
+
while ((bareMatch = barePathRegex.exec(variant)) !== null) {
|
|
122
|
+
const normalized = normalizeLocalImagePath(bareMatch[1] || '');
|
|
123
|
+
if (normalized)
|
|
124
|
+
candidates.add(normalized);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return Array.from(candidates);
|
|
128
|
+
}
|
|
129
|
+
function detectImageMime(filePath) {
|
|
130
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
131
|
+
return IMAGE_MIME_BY_EXT[ext] || 'application/octet-stream';
|
|
132
|
+
}
|
|
133
|
+
function encodeLocalImageAsDataUrl(filePath) {
|
|
134
|
+
const imageBuffer = fs.readFileSync(filePath);
|
|
135
|
+
const mimeType = detectImageMime(filePath);
|
|
136
|
+
return `data:${mimeType};base64,${imageBuffer.toString('base64')}`;
|
|
137
|
+
}
|
|
138
|
+
function messageHasImageContent(content) {
|
|
139
|
+
if (!Array.isArray(content))
|
|
140
|
+
return false;
|
|
141
|
+
return content.some((part) => {
|
|
142
|
+
if (!part || typeof part !== 'object')
|
|
143
|
+
return false;
|
|
144
|
+
const type = String(part.type || '').toLowerCase();
|
|
145
|
+
if (type !== 'image_url' && type !== 'input_image' && type !== 'image')
|
|
146
|
+
return false;
|
|
147
|
+
const imageUrl = part.image_url;
|
|
148
|
+
return typeof imageUrl === 'string' || Boolean(imageUrl && typeof imageUrl === 'object' && typeof imageUrl.url === 'string');
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function appendLocalImageBlockOnLatestUserInput(messages, context) {
|
|
152
|
+
if (!Array.isArray(messages) || !messages.length)
|
|
153
|
+
return;
|
|
154
|
+
let latestUserIndex = -1;
|
|
155
|
+
for (let idx = messages.length - 1; idx >= 0; idx -= 1) {
|
|
156
|
+
const role = String(messages[idx]?.role || '').toLowerCase();
|
|
157
|
+
if (role === 'user') {
|
|
158
|
+
latestUserIndex = idx;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (latestUserIndex < 0)
|
|
163
|
+
return;
|
|
164
|
+
const latestUserMessage = messages[latestUserIndex];
|
|
165
|
+
const originalContent = latestUserMessage.content;
|
|
166
|
+
if (messageHasImageContent(originalContent))
|
|
167
|
+
return;
|
|
168
|
+
const textCandidates = [];
|
|
169
|
+
if (typeof originalContent === 'string' && originalContent.trim().length) {
|
|
170
|
+
textCandidates.push(originalContent);
|
|
171
|
+
}
|
|
172
|
+
else if (Array.isArray(originalContent)) {
|
|
173
|
+
for (const part of originalContent) {
|
|
174
|
+
if (!part || typeof part !== 'object')
|
|
175
|
+
continue;
|
|
176
|
+
const type = String(part.type || '').toLowerCase();
|
|
177
|
+
if ((type === 'text' || type === 'input_text' || type === 'output_text' || type === 'commentary') &&
|
|
178
|
+
typeof part.text === 'string' &&
|
|
179
|
+
part.text.trim().length) {
|
|
180
|
+
textCandidates.push(part.text);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (!textCandidates.length)
|
|
185
|
+
return;
|
|
186
|
+
const imagePaths = new Set();
|
|
187
|
+
for (const text of textCandidates) {
|
|
188
|
+
for (const candidate of collectLocalImagePathCandidates(text)) {
|
|
189
|
+
imagePaths.add(candidate);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!imagePaths.size)
|
|
193
|
+
return;
|
|
194
|
+
const normalizedContent = [];
|
|
195
|
+
if (typeof originalContent === 'string') {
|
|
196
|
+
normalizedContent.push({ type: 'text', text: originalContent });
|
|
197
|
+
}
|
|
198
|
+
else if (Array.isArray(originalContent)) {
|
|
199
|
+
for (const part of originalContent) {
|
|
200
|
+
if (part && typeof part === 'object') {
|
|
201
|
+
normalizedContent.push({ ...part });
|
|
202
|
+
}
|
|
203
|
+
else if (typeof part === 'string') {
|
|
204
|
+
normalizedContent.push({ type: 'text', text: part });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const unreadableImageNotices = [];
|
|
209
|
+
for (const imagePath of imagePaths) {
|
|
210
|
+
let dataUrl = '';
|
|
211
|
+
try {
|
|
212
|
+
dataUrl = encodeLocalImageAsDataUrl(imagePath);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
const reason = error instanceof Error
|
|
216
|
+
? `${error.code ?? 'READ_FAILED'}: ${error.message}`
|
|
217
|
+
: String(error ?? 'READ_FAILED');
|
|
218
|
+
unreadableImageNotices.push(`[local_image_unreadable] 文件不可读,已跳过该图片路径: ${imagePath} (${reason})`);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
normalizedContent.push({ type: 'image_url', image_url: { url: dataUrl } });
|
|
222
|
+
}
|
|
223
|
+
if (unreadableImageNotices.length) {
|
|
224
|
+
normalizedContent.push({
|
|
225
|
+
type: 'text',
|
|
226
|
+
text: unreadableImageNotices.join('\n')
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
if (!messageHasImageContent(normalizedContent) && !unreadableImageNotices.length) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
latestUserMessage.content = normalizedContent;
|
|
233
|
+
}
|
|
49
234
|
// NOTE: 自修复提示已移除(统一标准:不做模糊兜底)。
|
|
50
235
|
// --- Public bridge functions ---
|
|
51
236
|
export function captureResponsesContext(payload, dto) {
|
|
@@ -149,6 +334,7 @@ export function buildChatRequestFromResponses(payload, context) {
|
|
|
149
334
|
messages = [...preservedSystems, ...nonSystemMessages];
|
|
150
335
|
}
|
|
151
336
|
}
|
|
337
|
+
appendLocalImageBlockOnLatestUserInput(messages, context);
|
|
152
338
|
// 不在 Responses 路径做工具治理;统一在 Chat 后半段处理
|
|
153
339
|
// No system tips for MCP on OpenAI Responses path (avoid leaking tool names)
|
|
154
340
|
if (!messages.length) {
|
|
@@ -625,496 +811,6 @@ function extractMetadataExtraFields(metadata) {
|
|
|
625
811
|
function isPlainObject(value) {
|
|
626
812
|
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
627
813
|
}
|
|
628
|
-
function deepCloneRecord(value) {
|
|
629
|
-
try {
|
|
630
|
-
const structuredCloneImpl = globalThis.structuredClone;
|
|
631
|
-
if (typeof structuredCloneImpl === 'function') {
|
|
632
|
-
return structuredCloneImpl(value);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
catch {
|
|
636
|
-
// ignore structuredClone failures
|
|
637
|
-
}
|
|
638
|
-
try {
|
|
639
|
-
return JSON.parse(JSON.stringify(value));
|
|
640
|
-
}
|
|
641
|
-
catch {
|
|
642
|
-
return { ...value };
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
function isMissingResponseField(value) {
|
|
646
|
-
if (value === undefined || value === null) {
|
|
647
|
-
return true;
|
|
648
|
-
}
|
|
649
|
-
if (Array.isArray(value)) {
|
|
650
|
-
return value.length === 0;
|
|
651
|
-
}
|
|
652
|
-
return false;
|
|
653
|
-
}
|
|
654
|
-
function mergeResponseOutputItems(baseOutput, sourceOutput) {
|
|
655
|
-
if (!Array.isArray(baseOutput) || !Array.isArray(sourceOutput)) {
|
|
656
|
-
return baseOutput;
|
|
657
|
-
}
|
|
658
|
-
const sourceById = new Map();
|
|
659
|
-
sourceOutput.forEach((entry) => {
|
|
660
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
661
|
-
return;
|
|
662
|
-
}
|
|
663
|
-
const id = typeof entry.id === 'string' ? entry.id.trim() : '';
|
|
664
|
-
if (!id.length) {
|
|
665
|
-
return;
|
|
666
|
-
}
|
|
667
|
-
sourceById.set(id, entry);
|
|
668
|
-
});
|
|
669
|
-
return baseOutput.map((entry, index) => {
|
|
670
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
671
|
-
return entry;
|
|
672
|
-
}
|
|
673
|
-
const baseItem = deepCloneRecord(entry);
|
|
674
|
-
const baseId = typeof baseItem.id === 'string' ? String(baseItem.id).trim() : '';
|
|
675
|
-
let sourceItem;
|
|
676
|
-
if (baseId.length) {
|
|
677
|
-
sourceItem = sourceById.get(baseId);
|
|
678
|
-
}
|
|
679
|
-
if (!sourceItem) {
|
|
680
|
-
const candidate = sourceOutput[index];
|
|
681
|
-
if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
|
|
682
|
-
sourceItem = candidate;
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
if (!sourceItem) {
|
|
686
|
-
return baseItem;
|
|
687
|
-
}
|
|
688
|
-
if (isMissingResponseField(baseItem.content) && sourceItem.content !== undefined) {
|
|
689
|
-
baseItem.content = deepCloneRecord({ value: sourceItem.content }).value;
|
|
690
|
-
}
|
|
691
|
-
if (isMissingResponseField(baseItem.summary) && sourceItem.summary !== undefined) {
|
|
692
|
-
baseItem.summary = deepCloneRecord({ value: sourceItem.summary }).value;
|
|
693
|
-
}
|
|
694
|
-
return baseItem;
|
|
695
|
-
});
|
|
696
|
-
}
|
|
697
|
-
function mergeResponseTopLevelFields(options) {
|
|
698
|
-
const merged = deepCloneRecord(options.base);
|
|
699
|
-
const source = options.source;
|
|
700
|
-
const sourceWins = options.sourceWinsKeys ?? [];
|
|
701
|
-
for (const key of sourceWins) {
|
|
702
|
-
if (source[key] !== undefined) {
|
|
703
|
-
merged[key] = deepCloneRecord({ value: source[key] }).value;
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
const passthroughKeys = ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning'];
|
|
707
|
-
for (const key of passthroughKeys) {
|
|
708
|
-
if (source[key] !== undefined && merged[key] === undefined) {
|
|
709
|
-
merged[key] = deepCloneRecord({ value: source[key] }).value;
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
if (merged.output !== undefined && source.output !== undefined) {
|
|
713
|
-
merged.output = mergeResponseOutputItems(merged.output, source.output);
|
|
714
|
-
}
|
|
715
|
-
return merged;
|
|
716
|
-
}
|
|
717
|
-
export function buildResponsesPayloadFromChat(payload, context) {
|
|
718
|
-
if (!payload || typeof payload !== 'object')
|
|
719
|
-
return payload;
|
|
720
|
-
const response = unwrapData(payload);
|
|
721
|
-
if (!response || typeof response !== 'object')
|
|
722
|
-
return payload;
|
|
723
|
-
if (response.object === 'response' && Array.isArray(response.output)) {
|
|
724
|
-
return response;
|
|
725
|
-
}
|
|
726
|
-
const snapshotLookupKey = resolveSnapshotLookupKey(response, context);
|
|
727
|
-
const snapshotPayload = snapshotLookupKey ? consumeResponsesPayloadSnapshot(snapshotLookupKey) : undefined;
|
|
728
|
-
const passthroughPayload = snapshotLookupKey ? consumeResponsesPassthrough(snapshotLookupKey) : undefined;
|
|
729
|
-
const sourceForRetention = (passthroughPayload && typeof passthroughPayload === 'object' ? passthroughPayload : undefined) ??
|
|
730
|
-
(snapshotPayload && typeof snapshotPayload === 'object' ? snapshotPayload : undefined);
|
|
731
|
-
const hasChoicesArray = Array.isArray(response.choices);
|
|
732
|
-
const choicesLength = hasChoicesArray ? response.choices.length : 0;
|
|
733
|
-
// Graceful fallback for provider payloads that do not contain a valid
|
|
734
|
-
// ChatCompletion-style choices array (e.g. certain compat error envelopes).
|
|
735
|
-
if (!hasChoicesArray || choicesLength === 0) {
|
|
736
|
-
const rawStatus = response.status;
|
|
737
|
-
const statusCode = typeof rawStatus === 'string' && rawStatus.trim().length
|
|
738
|
-
? rawStatus.trim()
|
|
739
|
-
: typeof rawStatus === 'number'
|
|
740
|
-
? String(rawStatus)
|
|
741
|
-
: undefined;
|
|
742
|
-
const message = typeof response.msg === 'string' && response.msg.trim().length
|
|
743
|
-
? response.msg.trim()
|
|
744
|
-
: typeof response.message === 'string' && response.message.trim().length
|
|
745
|
-
? response.message.trim()
|
|
746
|
-
: 'Upstream returned non-standard Chat completion payload (missing choices).';
|
|
747
|
-
const out = {
|
|
748
|
-
id: response.id || `resp-${Date.now()}`,
|
|
749
|
-
object: 'response',
|
|
750
|
-
created_at: response.created_at || response.created || Math.floor(Date.now() / 1000),
|
|
751
|
-
model: response.model,
|
|
752
|
-
status: 'failed',
|
|
753
|
-
output: []
|
|
754
|
-
};
|
|
755
|
-
if (message) {
|
|
756
|
-
out.output_text = message;
|
|
757
|
-
out.error = {
|
|
758
|
-
type: 'provider_error',
|
|
759
|
-
code: statusCode,
|
|
760
|
-
message
|
|
761
|
-
};
|
|
762
|
-
}
|
|
763
|
-
if (context) {
|
|
764
|
-
for (const k of ['metadata', 'parallel_tool_calls', 'tool_choice', 'include']) {
|
|
765
|
-
if (context[k] !== undefined)
|
|
766
|
-
out[k] = context[k];
|
|
767
|
-
}
|
|
768
|
-
if (!shouldStripHostManagedFields(context) && context.store !== undefined) {
|
|
769
|
-
out.store = context.store;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (typeof response.request_id === 'string') {
|
|
773
|
-
out.request_id = response.request_id;
|
|
774
|
-
}
|
|
775
|
-
else if (typeof response.id === 'string') {
|
|
776
|
-
out.request_id = response.id;
|
|
777
|
-
}
|
|
778
|
-
else if (typeof context?.requestId === 'string') {
|
|
779
|
-
out.request_id = context.requestId;
|
|
780
|
-
}
|
|
781
|
-
const mergedFallback = sourceForRetention && typeof sourceForRetention === 'object'
|
|
782
|
-
? mergeResponseTopLevelFields({
|
|
783
|
-
base: out,
|
|
784
|
-
source: sourceForRetention,
|
|
785
|
-
sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
|
|
786
|
-
})
|
|
787
|
-
: out;
|
|
788
|
-
if (mergedFallback.metadata) {
|
|
789
|
-
stripInternalToolingMetadata(mergedFallback.metadata);
|
|
790
|
-
}
|
|
791
|
-
return mergedFallback;
|
|
792
|
-
}
|
|
793
|
-
const canonical = canonicalizeChatResponseTools(response);
|
|
794
|
-
const choices = Array.isArray(canonical?.choices) ? canonical.choices : [];
|
|
795
|
-
const primaryChoice = choices[0] && typeof choices[0] === 'object' ? choices[0] : undefined;
|
|
796
|
-
const message = primaryChoice && typeof primaryChoice.message === 'object' ? primaryChoice.message : undefined;
|
|
797
|
-
if (!message) {
|
|
798
|
-
throw new ProviderProtocolError('Responses bridge could not locate assistant message in Chat completion', {
|
|
799
|
-
code: 'MALFORMED_RESPONSE',
|
|
800
|
-
protocol: 'openai-chat',
|
|
801
|
-
providerType: 'openai',
|
|
802
|
-
details: {
|
|
803
|
-
context: 'buildResponsesPayloadFromChat',
|
|
804
|
-
choicesLength: choices.length,
|
|
805
|
-
requestId: context?.requestId
|
|
806
|
-
}
|
|
807
|
-
});
|
|
808
|
-
}
|
|
809
|
-
if (message) {
|
|
810
|
-
try {
|
|
811
|
-
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
812
|
-
const policyActions = resolvePolicyActions(bridgePolicy, 'response_outbound');
|
|
813
|
-
if (policyActions?.length) {
|
|
814
|
-
const actionState = createBridgeActionState({ messages: [message] });
|
|
815
|
-
runBridgeActionPipeline({
|
|
816
|
-
stage: 'response_outbound',
|
|
817
|
-
actions: policyActions,
|
|
818
|
-
protocol: bridgePolicy?.protocol ?? 'openai-responses',
|
|
819
|
-
moduleType: bridgePolicy?.moduleType ?? 'openai-responses',
|
|
820
|
-
requestId: context?.requestId,
|
|
821
|
-
state: actionState
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
catch (error) {
|
|
826
|
-
const message = error instanceof Error
|
|
827
|
-
? error.message
|
|
828
|
-
: typeof error === 'string'
|
|
829
|
-
? error
|
|
830
|
-
: 'unknown_error';
|
|
831
|
-
try {
|
|
832
|
-
// eslint-disable-next-line no-console
|
|
833
|
-
console.error(`\x1b[31m[responses-bridge][response_outbound] bridge action pipeline failed requestId=${context?.requestId ?? 'unknown'} error=${message}\x1b[0m`);
|
|
834
|
-
}
|
|
835
|
-
catch {
|
|
836
|
-
// ignore logging failures
|
|
837
|
-
}
|
|
838
|
-
// Do not fail-close here: preserve protocol-correct payload shape and let the client
|
|
839
|
-
// handle any downstream validation errors. The bridge policy pipeline is best-effort.
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
if (message) {
|
|
843
|
-
try {
|
|
844
|
-
normalizeMessageReasoningTools(message, {
|
|
845
|
-
idPrefix: `responses_reasoning_${context?.requestId ?? 'canonical'}`
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
catch {
|
|
849
|
-
// best-effort reasoning normalization
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
const outputBuild = buildResponsesOutputFromChat({
|
|
853
|
-
response: response,
|
|
854
|
-
message,
|
|
855
|
-
requestId: context?.requestId,
|
|
856
|
-
sanitizeFunctionName: sanitizeResponsesFunctionName
|
|
857
|
-
});
|
|
858
|
-
const out = {
|
|
859
|
-
id: response.id || `resp-${Date.now()}`,
|
|
860
|
-
object: 'response',
|
|
861
|
-
created_at: response.created_at || response.created || Math.floor(Date.now() / 1000),
|
|
862
|
-
model: response.model,
|
|
863
|
-
status: outputBuild.status,
|
|
864
|
-
output: outputBuild.outputItems
|
|
865
|
-
};
|
|
866
|
-
if (typeof outputBuild.outputText === 'string') {
|
|
867
|
-
out.output_text = outputBuild.outputText;
|
|
868
|
-
}
|
|
869
|
-
if (outputBuild.usage !== undefined)
|
|
870
|
-
out.usage = outputBuild.usage;
|
|
871
|
-
if (outputBuild.requiredAction)
|
|
872
|
-
out.required_action = outputBuild.requiredAction;
|
|
873
|
-
// Normalize tool_call arguments for client-declared tool formats (e.g. format:"freeform").
|
|
874
|
-
// If we cannot repair/match the declared schema/format, we fall back to returning the
|
|
875
|
-
// original tool-call arguments and let the client surface the error.
|
|
876
|
-
normalizeResponsesToolCallArgumentsForClient(out, context);
|
|
877
|
-
// Do not inject captured tool results here; keep Chat back-half behavior standard.
|
|
878
|
-
if (context) {
|
|
879
|
-
for (const k of ['metadata', 'parallel_tool_calls', 'tool_choice', 'include']) {
|
|
880
|
-
if (context[k] !== undefined)
|
|
881
|
-
out[k] = context[k];
|
|
882
|
-
}
|
|
883
|
-
if (!shouldStripHostManagedFields(context) && context.store !== undefined) {
|
|
884
|
-
out.store = context.store;
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
if (typeof response.request_id === 'string') {
|
|
888
|
-
out.request_id = response.request_id;
|
|
889
|
-
}
|
|
890
|
-
else if (typeof response.id === 'string') {
|
|
891
|
-
out.request_id = response.id;
|
|
892
|
-
}
|
|
893
|
-
else if (typeof context?.requestId === 'string') {
|
|
894
|
-
out.request_id = context.requestId;
|
|
895
|
-
}
|
|
896
|
-
const merged = sourceForRetention && typeof sourceForRetention === 'object'
|
|
897
|
-
? mergeResponseTopLevelFields({
|
|
898
|
-
base: out,
|
|
899
|
-
source: sourceForRetention,
|
|
900
|
-
sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
|
|
901
|
-
})
|
|
902
|
-
: out;
|
|
903
|
-
if (merged.metadata) {
|
|
904
|
-
stripInternalToolingMetadata(merged.metadata);
|
|
905
|
-
}
|
|
906
|
-
return merged;
|
|
907
|
-
}
|
|
908
|
-
function normalizeResponsesToolCallArgumentsForClient(responsesPayload, context) {
|
|
909
|
-
const toolsRaw = Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [];
|
|
910
|
-
if (!toolsRaw.length) {
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
const toolIndex = buildClientToolIndex(toolsRaw);
|
|
914
|
-
if (!toolIndex.size) {
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
const normalizeCallArgs = (toolName, argsRaw) => {
|
|
918
|
-
const spec = toolIndex.get(toolName);
|
|
919
|
-
if (!spec)
|
|
920
|
-
return argsRaw;
|
|
921
|
-
// format:"freeform" means client expects raw text (not JSON) in the arguments field.
|
|
922
|
-
if (typeof spec.format === 'string' && spec.format.trim().toLowerCase() === 'freeform') {
|
|
923
|
-
const rawText = extractFreeformTextFromArgs(argsRaw);
|
|
924
|
-
if (rawText && rawText.trim().length) {
|
|
925
|
-
return rawText;
|
|
926
|
-
}
|
|
927
|
-
return argsRaw;
|
|
928
|
-
}
|
|
929
|
-
// Schema-aware key alignment for common server-side tools.
|
|
930
|
-
// If we cannot repair, keep original args and let the client surface the error.
|
|
931
|
-
const params = spec.parameters;
|
|
932
|
-
if (!params)
|
|
933
|
-
return argsRaw;
|
|
934
|
-
const schema = extractJsonSchemaLike(params);
|
|
935
|
-
if (!schema)
|
|
936
|
-
return argsRaw;
|
|
937
|
-
const parsed = tryParseJson(argsRaw);
|
|
938
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
939
|
-
return argsRaw;
|
|
940
|
-
}
|
|
941
|
-
const record = parsed;
|
|
942
|
-
const repaired = repairToolArgsBySchemaKeys(toolName, record, schema);
|
|
943
|
-
if (!repaired) {
|
|
944
|
-
return argsRaw;
|
|
945
|
-
}
|
|
946
|
-
try {
|
|
947
|
-
return JSON.stringify(repaired);
|
|
948
|
-
}
|
|
949
|
-
catch {
|
|
950
|
-
return argsRaw;
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
// 1) output[] function_call items
|
|
954
|
-
const output = Array.isArray(responsesPayload.output) ? responsesPayload.output : [];
|
|
955
|
-
for (const item of output) {
|
|
956
|
-
if (!item || typeof item !== 'object')
|
|
957
|
-
continue;
|
|
958
|
-
const type = typeof item.type === 'string' ? String(item.type).trim().toLowerCase() : '';
|
|
959
|
-
if (type !== 'function_call')
|
|
960
|
-
continue;
|
|
961
|
-
const name = typeof item.name === 'string' ? String(item.name).trim() : '';
|
|
962
|
-
if (!name)
|
|
963
|
-
continue;
|
|
964
|
-
if (!toolIndex.has(name))
|
|
965
|
-
continue;
|
|
966
|
-
item.arguments = normalizeCallArgs(name, item.arguments);
|
|
967
|
-
}
|
|
968
|
-
// 2) required_action.submit_tool_outputs.tool_calls[] tool calls
|
|
969
|
-
const toolCalls = responsesPayload?.required_action?.submit_tool_outputs?.tool_calls;
|
|
970
|
-
const calls = Array.isArray(toolCalls) ? toolCalls : [];
|
|
971
|
-
for (const call of calls) {
|
|
972
|
-
if (!call || typeof call !== 'object')
|
|
973
|
-
continue;
|
|
974
|
-
const fn = call.function && typeof call.function === 'object' && !Array.isArray(call.function) ? call.function : null;
|
|
975
|
-
const name = typeof fn?.name === 'string' ? String(fn.name).trim() : '';
|
|
976
|
-
if (!name)
|
|
977
|
-
continue;
|
|
978
|
-
if (!toolIndex.has(name))
|
|
979
|
-
continue;
|
|
980
|
-
fn.arguments = normalizeCallArgs(name, fn.arguments);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
function buildClientToolIndex(toolsRaw) {
|
|
984
|
-
const index = new Map();
|
|
985
|
-
for (const tool of toolsRaw) {
|
|
986
|
-
if (!tool || typeof tool !== 'object' || Array.isArray(tool))
|
|
987
|
-
continue;
|
|
988
|
-
const t = tool;
|
|
989
|
-
const fn = t.function && typeof t.function === 'object' && !Array.isArray(t.function) ? t.function : undefined;
|
|
990
|
-
const nameRaw = (fn && typeof fn.name === 'string' ? fn.name : undefined) ?? (typeof t.name === 'string' ? t.name : undefined);
|
|
991
|
-
const name = typeof nameRaw === 'string' ? nameRaw.trim() : '';
|
|
992
|
-
if (!name)
|
|
993
|
-
continue;
|
|
994
|
-
const formatRaw = (typeof t.format === 'string' ? t.format : undefined) ??
|
|
995
|
-
(fn && typeof fn.format === 'string' ? fn.format : undefined);
|
|
996
|
-
const parametersRaw = (fn && fn.parameters && typeof fn.parameters === 'object' && !Array.isArray(fn.parameters) ? fn.parameters : undefined) ??
|
|
997
|
-
(t.parameters && typeof t.parameters === 'object' && !Array.isArray(t.parameters) ? t.parameters : undefined) ??
|
|
998
|
-
undefined;
|
|
999
|
-
index.set(name, {
|
|
1000
|
-
name,
|
|
1001
|
-
...(typeof formatRaw === 'string' && formatRaw.trim().length ? { format: formatRaw.trim() } : {}),
|
|
1002
|
-
...(parametersRaw ? { parameters: parametersRaw } : {})
|
|
1003
|
-
});
|
|
1004
|
-
}
|
|
1005
|
-
return index;
|
|
1006
|
-
}
|
|
1007
|
-
function tryParseJson(value) {
|
|
1008
|
-
if (typeof value !== 'string')
|
|
1009
|
-
return null;
|
|
1010
|
-
const trimmed = value.trim();
|
|
1011
|
-
if (!trimmed)
|
|
1012
|
-
return null;
|
|
1013
|
-
if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
|
|
1014
|
-
return null;
|
|
1015
|
-
try {
|
|
1016
|
-
return JSON.parse(trimmed);
|
|
1017
|
-
}
|
|
1018
|
-
catch {
|
|
1019
|
-
return null;
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
function looksLikeApplyPatchText(value) {
|
|
1023
|
-
const trimmed = value.trim();
|
|
1024
|
-
if (!trimmed)
|
|
1025
|
-
return false;
|
|
1026
|
-
return trimmed.includes('*** Begin Patch') && trimmed.includes('*** End Patch');
|
|
1027
|
-
}
|
|
1028
|
-
function extractFreeformTextFromArgs(argsRaw) {
|
|
1029
|
-
if (typeof argsRaw === 'string') {
|
|
1030
|
-
const trimmed = argsRaw.trim();
|
|
1031
|
-
if (!trimmed)
|
|
1032
|
-
return null;
|
|
1033
|
-
// If JSON wrapper, extract common fields first.
|
|
1034
|
-
const parsed = tryParseJson(trimmed);
|
|
1035
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1036
|
-
const rec = parsed;
|
|
1037
|
-
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
1038
|
-
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
1039
|
-
return String(rec[key]).trim();
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
// If it's already freeform (not a JSON wrapper), keep.
|
|
1044
|
-
if (!trimmed.startsWith('{') && !trimmed.startsWith('[') && looksLikeApplyPatchText(trimmed)) {
|
|
1045
|
-
return trimmed;
|
|
1046
|
-
}
|
|
1047
|
-
// Unknown freeform: return as-is so client can error.
|
|
1048
|
-
return trimmed;
|
|
1049
|
-
}
|
|
1050
|
-
// If arguments is an object (malformed), try to extract patch-like fields.
|
|
1051
|
-
if (argsRaw && typeof argsRaw === 'object' && !Array.isArray(argsRaw)) {
|
|
1052
|
-
const rec = argsRaw;
|
|
1053
|
-
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
1054
|
-
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
1055
|
-
return String(rec[key]).trim();
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
return null;
|
|
1060
|
-
}
|
|
1061
|
-
function extractJsonSchemaLike(parameters) {
|
|
1062
|
-
const required = Array.isArray(parameters.required)
|
|
1063
|
-
? parameters.required.filter((v) => typeof v === 'string' && v.trim().length > 0).map((v) => v.trim())
|
|
1064
|
-
: [];
|
|
1065
|
-
const props = parameters.properties && typeof parameters.properties === 'object' && !Array.isArray(parameters.properties)
|
|
1066
|
-
? parameters.properties
|
|
1067
|
-
: {};
|
|
1068
|
-
const properties = Object.keys(props);
|
|
1069
|
-
if (!required.length && !properties.length) {
|
|
1070
|
-
return null;
|
|
1071
|
-
}
|
|
1072
|
-
const additionalProperties = typeof parameters.additionalProperties === 'boolean' ? parameters.additionalProperties : true;
|
|
1073
|
-
return { required, properties, additionalProperties };
|
|
1074
|
-
}
|
|
1075
|
-
function repairToolArgsBySchemaKeys(toolName, record, schema) {
|
|
1076
|
-
// Apply known alias mappings only if the target keys are present in schema properties.
|
|
1077
|
-
const wants = new Set(schema.properties);
|
|
1078
|
-
const out = { ...record };
|
|
1079
|
-
if (toolName === 'exec_command') {
|
|
1080
|
-
if (wants.has('cmd') && out.command !== undefined && out.cmd === undefined)
|
|
1081
|
-
out.cmd = out.command;
|
|
1082
|
-
if (wants.has('command') && out.cmd !== undefined && out.command === undefined)
|
|
1083
|
-
out.command = out.cmd;
|
|
1084
|
-
}
|
|
1085
|
-
if (toolName === 'write_stdin') {
|
|
1086
|
-
if (wants.has('chars') && out.text !== undefined && out.chars === undefined)
|
|
1087
|
-
out.chars = out.text;
|
|
1088
|
-
if (wants.has('text') && out.chars !== undefined && out.text === undefined)
|
|
1089
|
-
out.text = out.chars;
|
|
1090
|
-
}
|
|
1091
|
-
if (toolName === 'apply_patch') {
|
|
1092
|
-
if (wants.has('instructions') && out.instructions === undefined) {
|
|
1093
|
-
if (typeof out.patch === 'string' && out.patch.trim().length)
|
|
1094
|
-
out.instructions = out.patch;
|
|
1095
|
-
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
1096
|
-
out.instructions = out.input;
|
|
1097
|
-
}
|
|
1098
|
-
if (wants.has('patch') && out.patch === undefined) {
|
|
1099
|
-
if (typeof out.instructions === 'string' && out.instructions.trim().length)
|
|
1100
|
-
out.patch = out.instructions;
|
|
1101
|
-
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
1102
|
-
out.patch = out.input;
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
for (const key of schema.required) {
|
|
1106
|
-
if (!Object.prototype.hasOwnProperty.call(out, key)) {
|
|
1107
|
-
return null;
|
|
1108
|
-
}
|
|
1109
|
-
}
|
|
1110
|
-
if (schema.additionalProperties === false && schema.properties.length > 0) {
|
|
1111
|
-
for (const key of Object.keys(out)) {
|
|
1112
|
-
if (!wants.has(key))
|
|
1113
|
-
delete out[key];
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return out;
|
|
1117
|
-
}
|
|
1118
814
|
function unwrapData(value) {
|
|
1119
815
|
let current = value;
|
|
1120
816
|
const seen = new Set();
|
|
@@ -1146,13 +842,6 @@ function shouldStripHostManagedFields(context) {
|
|
|
1146
842
|
const result = evaluateResponsesHostPolicy(context, typeof context?.targetProtocol === 'string' ? context?.targetProtocol : undefined);
|
|
1147
843
|
return result.shouldStripHostManagedFields;
|
|
1148
844
|
}
|
|
1149
|
-
export
|
|
1150
|
-
if (response && typeof response === 'object' && 'metadata' in response && response.metadata && typeof response.metadata === 'object') {
|
|
1151
|
-
const meta = response.metadata;
|
|
1152
|
-
if (typeof meta.requestId === 'string')
|
|
1153
|
-
return meta.requestId;
|
|
1154
|
-
}
|
|
1155
|
-
return undefined;
|
|
1156
|
-
}
|
|
845
|
+
export { buildResponsesPayloadFromChat, extractRequestIdFromResponse } from './responses-openai-bridge/response-payload.js';
|
|
1157
846
|
export { buildChatResponseFromResponses } from '../shared/responses-response-utils.js';
|
|
1158
847
|
// (imports moved to top)
|