@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,11 +1,10 @@
|
|
|
1
|
-
import { ProtocolConversionPipeline } from '../../index.js';
|
|
2
|
-
import { chatEnvelopeToStandardized } from '../../../hub/standardized-bridge.js';
|
|
3
|
-
import { ResponsesFormatAdapter } from '../../../hub/format-adapters/responses-format-adapter.js';
|
|
4
|
-
import { ResponsesSemanticMapper } from '../../../hub/semantic-mappers/responses-mapper.js';
|
|
5
|
-
import { captureResponsesContext, buildChatRequestFromResponses, buildResponsesPayloadFromChat } from '../../../responses/responses-openai-bridge.js';
|
|
6
|
-
import { buildAdapterContextFromPipeline } from '../../hooks/adapter-context.js';
|
|
7
1
|
import { runStandardChatRequestFilters } from '../../../index.js';
|
|
2
|
+
import { buildChatRequestFromResponses, buildResponsesPayloadFromChat, captureResponsesContext } from '../../../responses/responses-openai-bridge.js';
|
|
3
|
+
import { ConversionMetaBag } from '../../meta/meta-bag.js';
|
|
8
4
|
import { canonicalizeOpenAIChatResponse, convertStandardizedToOpenAIChat as convertCanonicalToOpenAIChat, OPENAI_PROTOCOL } from './shared/openai-chat-helpers.js';
|
|
5
|
+
import { buildAdapterContextFromPipeline } from '../../hooks/adapter-context.js';
|
|
6
|
+
import { chatEnvelopeToStandardizedWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-inbound-semantics.js';
|
|
7
|
+
import { standardizedToChatEnvelopeWithNative } from '../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
9
8
|
const DEFAULT_RESPONSES_ENDPOINT = '/v1/responses';
|
|
10
9
|
const RESPONSES_PROTOCOL = 'openai-responses';
|
|
11
10
|
function assertJsonObject(value, stage) {
|
|
@@ -117,49 +116,6 @@ function captureToolResults(payload) {
|
|
|
117
116
|
}
|
|
118
117
|
return results;
|
|
119
118
|
}
|
|
120
|
-
function createResponsesHooks() {
|
|
121
|
-
const formatAdapter = new ResponsesFormatAdapter();
|
|
122
|
-
const semanticMapper = new ResponsesSemanticMapper();
|
|
123
|
-
return {
|
|
124
|
-
id: 'responses-openai-v2',
|
|
125
|
-
protocol: RESPONSES_PROTOCOL,
|
|
126
|
-
inbound: {
|
|
127
|
-
parse: async ({ wire, context, meta }) => {
|
|
128
|
-
const adapterContext = buildAdapterContextFromPipeline(context, {
|
|
129
|
-
defaultEntryEndpoint: DEFAULT_RESPONSES_ENDPOINT,
|
|
130
|
-
overrideProtocol: RESPONSES_PROTOCOL
|
|
131
|
-
});
|
|
132
|
-
const formatEnvelope = await formatAdapter.parseRequest(wire, adapterContext);
|
|
133
|
-
const chatEnvelope = await semanticMapper.toChat(formatEnvelope, adapterContext);
|
|
134
|
-
const canonical = chatEnvelopeToStandardized(chatEnvelope, {
|
|
135
|
-
adapterContext,
|
|
136
|
-
endpoint: adapterContext.entryEndpoint,
|
|
137
|
-
requestId: adapterContext.requestId
|
|
138
|
-
});
|
|
139
|
-
// Capture Responses-specific context for outbound mapping parity
|
|
140
|
-
const responsesContext = captureResponsesContext(wire, { route: { requestId: adapterContext.requestId } });
|
|
141
|
-
const built = buildChatRequestFromResponses(wire, responsesContext);
|
|
142
|
-
if (built.toolsNormalized) {
|
|
143
|
-
responsesContext.toolsNormalized = built.toolsNormalized;
|
|
144
|
-
}
|
|
145
|
-
const captured = captureToolResults(wire);
|
|
146
|
-
if (captured.length) {
|
|
147
|
-
responsesContext.__captured_tool_results = captured;
|
|
148
|
-
}
|
|
149
|
-
meta.set('responsesContext', cloneResponsesContext(responsesContext));
|
|
150
|
-
return { canonical };
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
outbound: {
|
|
154
|
-
serialize: async ({ canonical, meta }) => {
|
|
155
|
-
const stored = meta.consume('responsesContext');
|
|
156
|
-
const responsesContext = restoreResponsesContext(stored);
|
|
157
|
-
const payload = buildResponsesPayloadFromChat(canonical, responsesContext);
|
|
158
|
-
return { payload: assertJsonObject(payload, 'responses_outbound_serialize') };
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
119
|
function buildPipelineContext(profile, context) {
|
|
164
120
|
return {
|
|
165
121
|
requestId: context.requestId,
|
|
@@ -173,12 +129,8 @@ function buildPipelineContext(profile, context) {
|
|
|
173
129
|
}
|
|
174
130
|
export class ResponsesOpenAIPipelineCodec {
|
|
175
131
|
id = 'responses-openai-v2';
|
|
176
|
-
pipeline;
|
|
177
132
|
requestMetaStore = new Map();
|
|
178
133
|
initialized = false;
|
|
179
|
-
constructor() {
|
|
180
|
-
this.pipeline = new ProtocolConversionPipeline(createResponsesHooks());
|
|
181
|
-
}
|
|
182
134
|
async initialize() {
|
|
183
135
|
this.initialized = true;
|
|
184
136
|
}
|
|
@@ -195,13 +147,44 @@ export class ResponsesOpenAIPipelineCodec {
|
|
|
195
147
|
const inboundContext = buildPipelineContext(profile, context);
|
|
196
148
|
const requestId = context.requestId ?? inboundContext.requestId ?? `req_${Date.now()}`;
|
|
197
149
|
inboundContext.requestId = requestId;
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
150
|
+
const wire = assertJsonObject(payload, 'responses_inbound_request');
|
|
151
|
+
const responsesContext = captureResponsesContext(wire, { route: { requestId } });
|
|
152
|
+
const built = buildChatRequestFromResponses(wire, responsesContext);
|
|
153
|
+
if (built.toolsNormalized) {
|
|
154
|
+
responsesContext.toolsNormalized = built.toolsNormalized;
|
|
155
|
+
}
|
|
156
|
+
const captured = captureToolResults(wire);
|
|
157
|
+
if (captured.length) {
|
|
158
|
+
responsesContext.__captured_tool_results = captured;
|
|
159
|
+
}
|
|
160
|
+
const adapterContext = buildAdapterContextFromPipeline(inboundContext, {
|
|
161
|
+
defaultEntryEndpoint: DEFAULT_RESPONSES_ENDPOINT,
|
|
162
|
+
overrideProtocol: RESPONSES_PROTOCOL
|
|
163
|
+
});
|
|
164
|
+
const chatEnvelope = standardizedToChatEnvelopeWithNative({
|
|
165
|
+
request: {
|
|
166
|
+
model: built.request.model,
|
|
167
|
+
messages: built.request.messages,
|
|
168
|
+
tools: built.request.tools ?? [],
|
|
169
|
+
parameters: {
|
|
170
|
+
...(typeof built.request.model === 'string'
|
|
171
|
+
? { model: built.request.model }
|
|
172
|
+
: {})
|
|
173
|
+
},
|
|
174
|
+
metadata: {}
|
|
175
|
+
},
|
|
176
|
+
adapterContext: adapterContext
|
|
177
|
+
});
|
|
178
|
+
const canonical = chatEnvelopeToStandardizedWithNative({
|
|
179
|
+
chatEnvelope: chatEnvelope,
|
|
180
|
+
adapterContext: adapterContext,
|
|
181
|
+
endpoint: adapterContext.entryEndpoint ?? DEFAULT_RESPONSES_ENDPOINT,
|
|
182
|
+
requestId
|
|
183
|
+
});
|
|
184
|
+
const meta = new ConversionMetaBag();
|
|
185
|
+
meta.set('responsesContext', cloneResponsesContext(responsesContext));
|
|
186
|
+
this.stashMeta(requestId, meta);
|
|
187
|
+
const openaiPayload = await convertCanonicalToOpenAIChat(canonical, inboundContext);
|
|
205
188
|
const filterContext = {
|
|
206
189
|
...context,
|
|
207
190
|
requestId,
|
|
@@ -221,13 +204,9 @@ export class ResponsesOpenAIPipelineCodec {
|
|
|
221
204
|
if (storedMeta) {
|
|
222
205
|
this.requestMetaStore.delete(requestId);
|
|
223
206
|
}
|
|
207
|
+
const stored = storedMeta?.responsesContext;
|
|
208
|
+
const responsesContext = restoreResponsesContext(stored);
|
|
224
209
|
const sanitized = await canonicalizeOpenAIChatResponse(assertJsonObject(payload, 'responses_openai_response'), context);
|
|
225
|
-
|
|
226
|
-
canonical: sanitized,
|
|
227
|
-
context: pipelineContext,
|
|
228
|
-
meta: storedMeta
|
|
229
|
-
};
|
|
230
|
-
const outbound = await this.pipeline.convertOutbound(outboundOptions);
|
|
231
|
-
return outbound.payload;
|
|
210
|
+
return assertJsonObject(buildResponsesPayloadFromChat(sanitized, responsesContext), 'responses_outbound_serialize');
|
|
232
211
|
}
|
|
233
212
|
}
|
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
import { standardizedToChatEnvelope } from '../../../../hub/standardized-bridge.js';
|
|
2
1
|
import { buildAdapterContextFromPipeline } from '../../../hooks/adapter-context.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { normalizeChatResponseReasoningTools } from '../../../../shared/reasoning-tool-normalizer.js';
|
|
2
|
+
import { standardizedToChatEnvelopeWithNative } from '../../../../../router/virtual-router/engine-selection/native-hub-pipeline-req-outbound-semantics.js';
|
|
3
|
+
import { mapOpenaiChatFromChatWithNative } from '../../../../../router/virtual-router/engine-selection/native-hub-pipeline-semantic-mappers.js';
|
|
4
|
+
import { runOpenAIResponseCodecWithNative } from '../../../../../router/virtual-router/engine-selection/native-compat-action-semantics.js';
|
|
7
5
|
export const DEFAULT_OPENAI_ENDPOINT = '/v1/chat/completions';
|
|
8
6
|
export const OPENAI_PROTOCOL = 'openai-chat';
|
|
9
|
-
const sharedChatFormatAdapter = new ChatFormatAdapter();
|
|
10
|
-
const sharedChatSemanticMapper = new ChatSemanticMapper();
|
|
11
7
|
function ensureJsonObject(value, stage) {
|
|
12
8
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
13
9
|
throw new Error(`OpenAI chat helper requires JSON object payload at ${stage}`);
|
|
@@ -19,41 +15,20 @@ export async function convertStandardizedToOpenAIChat(standardized, context, opt
|
|
|
19
15
|
defaultEntryEndpoint: options?.defaultEndpoint ?? DEFAULT_OPENAI_ENDPOINT,
|
|
20
16
|
overrideProtocol: OPENAI_PROTOCOL
|
|
21
17
|
});
|
|
22
|
-
const chatEnvelope =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
const chatEnvelope = standardizedToChatEnvelopeWithNative({
|
|
19
|
+
request: standardized,
|
|
20
|
+
adapterContext: adapterContext
|
|
21
|
+
});
|
|
22
|
+
const formatEnvelope = mapOpenaiChatFromChatWithNative(chatEnvelope, adapterContext);
|
|
23
|
+
return ensureJsonObject(formatEnvelope.payload, 'openai_request_build');
|
|
26
24
|
}
|
|
27
25
|
export async function canonicalizeOpenAIChatResponse(payload, context, options) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
metadata: {
|
|
31
|
-
requestId: context.requestId ?? `req_${Date.now()}`,
|
|
32
|
-
pipelineId: context.metadata?.pipelineId ?? 'conversion-router',
|
|
33
|
-
processingTime: 0,
|
|
34
|
-
stages: []
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
const filterContext = {
|
|
38
|
-
requestId: dto.metadata.requestId,
|
|
39
|
-
model: typeof payload.model === 'string' ? payload.model : undefined,
|
|
26
|
+
return ensureJsonObject(runOpenAIResponseCodecWithNative(payload, {
|
|
27
|
+
requestId: context.requestId ?? `req_${Date.now()}`,
|
|
40
28
|
endpoint: context.entryEndpoint ?? context.endpoint ?? options?.defaultEndpoint ?? DEFAULT_OPENAI_ENDPOINT,
|
|
29
|
+
stream: context.stream === true,
|
|
30
|
+
reasoningMode: context.metadata?.reasoningMode,
|
|
41
31
|
profile: options?.profile ?? OPENAI_PROTOCOL,
|
|
42
|
-
|
|
43
|
-
};
|
|
44
|
-
const engine = new FilterEngine();
|
|
45
|
-
engine.registerFilter(new ResponseToolTextCanonicalizeFilter());
|
|
46
|
-
engine.registerFilter(new ResponseToolArgumentsStringifyFilter());
|
|
47
|
-
engine.registerFilter(new ResponseFinishInvariantsFilter());
|
|
48
|
-
const stage1 = await engine.run('response_pre', dto.data, filterContext);
|
|
49
|
-
const stage2 = await engine.run('response_map', stage1, filterContext);
|
|
50
|
-
const stage3 = await engine.run('response_post', stage2, filterContext);
|
|
51
|
-
const normalized = ensureJsonObject(stage3, 'openai_response_filters');
|
|
52
|
-
try {
|
|
53
|
-
normalizeChatResponseReasoningTools(normalized, { idPrefixBase: 'reasoning_choice' });
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// reasoning normalization best-effort
|
|
57
|
-
}
|
|
58
|
-
return normalized;
|
|
32
|
+
idPrefixBase: 'reasoning_choice'
|
|
33
|
+
}), 'openai_response_filters');
|
|
59
34
|
}
|
|
@@ -2,270 +2,11 @@ import { evaluateResponsesHostPolicy } from '../responses-host-policy.js';
|
|
|
2
2
|
import { normalizeMessageReasoningTools } from '../../shared/reasoning-tool-normalizer.js';
|
|
3
3
|
import { createBridgeActionState, runBridgeActionPipeline } from '../../bridge-actions.js';
|
|
4
4
|
import { resolveBridgePolicy, resolvePolicyActions } from '../../bridge-policies.js';
|
|
5
|
-
import { buildResponsesOutputFromChat } from '../../shared/responses-output-builder.js';
|
|
6
5
|
import { consumeResponsesPayloadSnapshot, consumeResponsesPassthrough } from '../../shared/responses-reasoning-registry.js';
|
|
7
|
-
import {
|
|
6
|
+
import { stripInternalToolingMetadata } from '../../shared/responses-tool-utils.js';
|
|
8
7
|
import { ProviderProtocolError } from '../../provider-protocol-error.js';
|
|
9
|
-
import { normalizeResponsesToolCallArgumentsForClientWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
|
|
8
|
+
import { buildResponsesPayloadFromChatWithNative, normalizeResponsesToolCallArgumentsForClientWithNative } from '../../../router/virtual-router/engine-selection/native-hub-pipeline-resp-semantics.js';
|
|
10
9
|
import { normalizeChatResponseReasoningToolsWithNative } from '../../../router/virtual-router/engine-selection/native-hub-bridge-action-semantics.js';
|
|
11
|
-
function isPlainObject(value) {
|
|
12
|
-
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
13
|
-
}
|
|
14
|
-
function deepCloneRecord(value) {
|
|
15
|
-
try {
|
|
16
|
-
const structuredCloneImpl = globalThis.structuredClone;
|
|
17
|
-
if (typeof structuredCloneImpl === 'function') {
|
|
18
|
-
return structuredCloneImpl(value);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
// ignore structuredClone failures
|
|
23
|
-
}
|
|
24
|
-
try {
|
|
25
|
-
return JSON.parse(JSON.stringify(value));
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return { ...value };
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function isMissingResponseField(value) {
|
|
32
|
-
if (value === undefined || value === null) {
|
|
33
|
-
return true;
|
|
34
|
-
}
|
|
35
|
-
if (Array.isArray(value)) {
|
|
36
|
-
return value.length === 0;
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
function mergeResponseOutputItems(baseOutput, sourceOutput) {
|
|
41
|
-
if (!Array.isArray(baseOutput) || !Array.isArray(sourceOutput)) {
|
|
42
|
-
return baseOutput;
|
|
43
|
-
}
|
|
44
|
-
const sourceById = new Map();
|
|
45
|
-
sourceOutput.forEach((entry) => {
|
|
46
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const id = typeof entry.id === 'string' ? entry.id.trim() : '';
|
|
50
|
-
if (!id.length) {
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
sourceById.set(id, entry);
|
|
54
|
-
});
|
|
55
|
-
return baseOutput.map((entry, index) => {
|
|
56
|
-
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
57
|
-
return entry;
|
|
58
|
-
}
|
|
59
|
-
const baseItem = deepCloneRecord(entry);
|
|
60
|
-
const baseId = typeof baseItem.id === 'string' ? String(baseItem.id).trim() : '';
|
|
61
|
-
let sourceItem;
|
|
62
|
-
if (baseId.length) {
|
|
63
|
-
sourceItem = sourceById.get(baseId);
|
|
64
|
-
}
|
|
65
|
-
if (!sourceItem) {
|
|
66
|
-
const candidate = sourceOutput[index];
|
|
67
|
-
if (candidate && typeof candidate === 'object' && !Array.isArray(candidate)) {
|
|
68
|
-
sourceItem = candidate;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (!sourceItem) {
|
|
72
|
-
return baseItem;
|
|
73
|
-
}
|
|
74
|
-
if (isMissingResponseField(baseItem.content) && sourceItem.content !== undefined) {
|
|
75
|
-
baseItem.content = deepCloneRecord({ value: sourceItem.content }).value;
|
|
76
|
-
}
|
|
77
|
-
if (isMissingResponseField(baseItem.summary) && sourceItem.summary !== undefined) {
|
|
78
|
-
baseItem.summary = deepCloneRecord({ value: sourceItem.summary }).value;
|
|
79
|
-
}
|
|
80
|
-
if (isMissingResponseField(baseItem.encrypted_content) && sourceItem.encrypted_content !== undefined) {
|
|
81
|
-
baseItem.encrypted_content = deepCloneRecord({ value: sourceItem.encrypted_content }).value;
|
|
82
|
-
}
|
|
83
|
-
return baseItem;
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
function mergeResponseTopLevelFields(options) {
|
|
87
|
-
const merged = deepCloneRecord(options.base);
|
|
88
|
-
const source = options.source;
|
|
89
|
-
const sourceWins = options.sourceWinsKeys ?? [];
|
|
90
|
-
for (const key of sourceWins) {
|
|
91
|
-
if (source[key] !== undefined) {
|
|
92
|
-
merged[key] = deepCloneRecord({ value: source[key] }).value;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
const passthroughKeys = ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning'];
|
|
96
|
-
for (const key of passthroughKeys) {
|
|
97
|
-
if (source[key] !== undefined && merged[key] === undefined) {
|
|
98
|
-
merged[key] = deepCloneRecord({ value: source[key] }).value;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
if (merged.output !== undefined && source.output !== undefined) {
|
|
102
|
-
merged.output = mergeResponseOutputItems(merged.output, source.output);
|
|
103
|
-
}
|
|
104
|
-
return merged;
|
|
105
|
-
}
|
|
106
|
-
function tryParseJson(value) {
|
|
107
|
-
if (typeof value !== 'string')
|
|
108
|
-
return null;
|
|
109
|
-
const trimmed = value.trim();
|
|
110
|
-
if (!trimmed)
|
|
111
|
-
return null;
|
|
112
|
-
if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
|
|
113
|
-
return null;
|
|
114
|
-
try {
|
|
115
|
-
return JSON.parse(trimmed);
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
function looksLikeApplyPatchText(value) {
|
|
122
|
-
const trimmed = value.trim();
|
|
123
|
-
if (!trimmed)
|
|
124
|
-
return false;
|
|
125
|
-
return trimmed.includes('*** Begin Patch') && trimmed.includes('*** End Patch');
|
|
126
|
-
}
|
|
127
|
-
function extractFreeformTextFromArgs(argsRaw) {
|
|
128
|
-
if (typeof argsRaw === 'string') {
|
|
129
|
-
const trimmed = argsRaw.trim();
|
|
130
|
-
if (!trimmed)
|
|
131
|
-
return null;
|
|
132
|
-
// If JSON wrapper, extract common fields first.
|
|
133
|
-
const parsed = tryParseJson(trimmed);
|
|
134
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
135
|
-
const rec = parsed;
|
|
136
|
-
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
137
|
-
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
138
|
-
return String(rec[key]).trim();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
// If it's already freeform (not a JSON wrapper), keep.
|
|
143
|
-
if (!trimmed.startsWith('{') && !trimmed.startsWith('[') && looksLikeApplyPatchText(trimmed)) {
|
|
144
|
-
return trimmed;
|
|
145
|
-
}
|
|
146
|
-
// Unknown freeform: return as-is so client can error.
|
|
147
|
-
return trimmed;
|
|
148
|
-
}
|
|
149
|
-
// If arguments is an object (malformed), try to extract patch-like fields.
|
|
150
|
-
if (argsRaw && typeof argsRaw === 'object' && !Array.isArray(argsRaw)) {
|
|
151
|
-
const rec = argsRaw;
|
|
152
|
-
for (const key of ['instructions', 'patch', 'input', 'text']) {
|
|
153
|
-
if (typeof rec[key] === 'string' && rec[key].trim().length) {
|
|
154
|
-
return String(rec[key]).trim();
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
function extractJsonSchemaLike(parameters) {
|
|
161
|
-
const required = Array.isArray(parameters.required)
|
|
162
|
-
? parameters.required.filter((v) => typeof v === 'string' && v.trim().length > 0).map((v) => v.trim())
|
|
163
|
-
: [];
|
|
164
|
-
const props = parameters.properties && typeof parameters.properties === 'object' && !Array.isArray(parameters.properties)
|
|
165
|
-
? parameters.properties
|
|
166
|
-
: {};
|
|
167
|
-
const properties = Object.keys(props);
|
|
168
|
-
if (!required.length && !properties.length) {
|
|
169
|
-
return null;
|
|
170
|
-
}
|
|
171
|
-
const additionalProperties = typeof parameters.additionalProperties === 'boolean' ? parameters.additionalProperties : true;
|
|
172
|
-
return { required, properties, additionalProperties };
|
|
173
|
-
}
|
|
174
|
-
function repairToolArgsBySchemaKeys(toolName, record, schema) {
|
|
175
|
-
// Apply known alias mappings only if the target keys are present in schema properties.
|
|
176
|
-
const wants = new Set(schema.properties);
|
|
177
|
-
const out = { ...record };
|
|
178
|
-
if (toolName === 'exec_command') {
|
|
179
|
-
if (wants.has('cmd') && out.command !== undefined && out.cmd === undefined)
|
|
180
|
-
out.cmd = out.command;
|
|
181
|
-
if (wants.has('command') && out.cmd !== undefined && out.command === undefined)
|
|
182
|
-
out.command = out.cmd;
|
|
183
|
-
}
|
|
184
|
-
if (toolName === 'write_stdin') {
|
|
185
|
-
if (wants.has('chars') && out.text !== undefined && out.chars === undefined)
|
|
186
|
-
out.chars = out.text;
|
|
187
|
-
if (wants.has('text') && out.chars !== undefined && out.text === undefined)
|
|
188
|
-
out.text = out.chars;
|
|
189
|
-
}
|
|
190
|
-
if (toolName === 'apply_patch') {
|
|
191
|
-
if (wants.has('instructions') && out.instructions === undefined) {
|
|
192
|
-
if (typeof out.patch === 'string' && out.patch.trim().length)
|
|
193
|
-
out.instructions = out.patch;
|
|
194
|
-
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
195
|
-
out.instructions = out.input;
|
|
196
|
-
}
|
|
197
|
-
if (wants.has('patch') && out.patch === undefined) {
|
|
198
|
-
if (typeof out.instructions === 'string' && out.instructions.trim().length)
|
|
199
|
-
out.patch = out.instructions;
|
|
200
|
-
else if (typeof out.input === 'string' && out.input.trim().length)
|
|
201
|
-
out.patch = out.input;
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
for (const key of schema.required) {
|
|
205
|
-
if (!Object.prototype.hasOwnProperty.call(out, key)) {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
if (schema.additionalProperties === false && schema.properties.length > 0) {
|
|
210
|
-
for (const key of Object.keys(out)) {
|
|
211
|
-
if (!wants.has(key))
|
|
212
|
-
delete out[key];
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return out;
|
|
216
|
-
}
|
|
217
|
-
function buildClientToolIndex(toolsRaw) {
|
|
218
|
-
const index = new Map();
|
|
219
|
-
for (const tool of toolsRaw) {
|
|
220
|
-
if (!tool || typeof tool !== 'object' || Array.isArray(tool))
|
|
221
|
-
continue;
|
|
222
|
-
const t = tool;
|
|
223
|
-
const fn = t.function && typeof t.function === 'object' && !Array.isArray(t.function) ? t.function : undefined;
|
|
224
|
-
const nameRaw = (fn && typeof fn.name === 'string' ? fn.name : undefined) ?? (typeof t.name === 'string' ? t.name : undefined);
|
|
225
|
-
const name = typeof nameRaw === 'string' ? nameRaw.trim() : '';
|
|
226
|
-
if (!name)
|
|
227
|
-
continue;
|
|
228
|
-
const formatRaw = (typeof t.format === 'string' ? t.format : undefined) ??
|
|
229
|
-
(fn && typeof fn.format === 'string' ? fn.format : undefined);
|
|
230
|
-
const parametersRaw = (fn && fn.parameters && typeof fn.parameters === 'object' && !Array.isArray(fn.parameters) ? fn.parameters : undefined) ??
|
|
231
|
-
(t.parameters && typeof t.parameters === 'object' && !Array.isArray(t.parameters) ? t.parameters : undefined) ??
|
|
232
|
-
undefined;
|
|
233
|
-
index.set(name, {
|
|
234
|
-
name,
|
|
235
|
-
...(typeof formatRaw === 'string' && formatRaw.trim().length ? { format: formatRaw.trim() } : {}),
|
|
236
|
-
...(parametersRaw ? { parameters: parametersRaw } : {})
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
return index;
|
|
240
|
-
}
|
|
241
|
-
function resolveClientToolName(toolIndex, rawToolName) {
|
|
242
|
-
const trimmed = rawToolName.trim();
|
|
243
|
-
if (!trimmed.length)
|
|
244
|
-
return undefined;
|
|
245
|
-
if (toolIndex.has(trimmed))
|
|
246
|
-
return trimmed;
|
|
247
|
-
const lower = trimmed.toLowerCase();
|
|
248
|
-
for (const key of toolIndex.keys()) {
|
|
249
|
-
if (key.toLowerCase() === lower) {
|
|
250
|
-
return key;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
const hasBash = Array.from(toolIndex.keys()).some((key) => key.toLowerCase() === 'bash');
|
|
254
|
-
if (hasBash)
|
|
255
|
-
return undefined;
|
|
256
|
-
if (!['shell_command', 'exec_command', 'shell', 'terminal', 'bash'].includes(lower)) {
|
|
257
|
-
return undefined;
|
|
258
|
-
}
|
|
259
|
-
const preferred = ['shell_command', 'exec_command', 'shell', 'terminal'];
|
|
260
|
-
for (const candidate of preferred) {
|
|
261
|
-
for (const key of toolIndex.keys()) {
|
|
262
|
-
if (key.toLowerCase() === candidate) {
|
|
263
|
-
return key;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
return undefined;
|
|
268
|
-
}
|
|
269
10
|
function normalizeResponsesToolCallArgumentsForClient(responsesPayload, context) {
|
|
270
11
|
const toolsRaw = Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [];
|
|
271
12
|
if (!toolsRaw.length) {
|
|
@@ -310,6 +51,23 @@ function shouldStripHostManagedFields(context) {
|
|
|
310
51
|
const result = evaluateResponsesHostPolicy(context, typeof context?.targetProtocol === 'string' ? context?.targetProtocol : undefined);
|
|
311
52
|
return result.shouldStripHostManagedFields;
|
|
312
53
|
}
|
|
54
|
+
function collectRetentionContext(context) {
|
|
55
|
+
const stripHostManagedFields = shouldStripHostManagedFields(context);
|
|
56
|
+
return {
|
|
57
|
+
metadata: context?.metadata,
|
|
58
|
+
parallelToolCalls: context?.parallel_tool_calls,
|
|
59
|
+
toolChoice: context?.tool_choice,
|
|
60
|
+
include: context?.include,
|
|
61
|
+
store: context?.store,
|
|
62
|
+
stripHostManagedFields
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function readInlineRetentionPayload(response, key) {
|
|
66
|
+
const candidate = response[key];
|
|
67
|
+
return candidate && typeof candidate === 'object' && !Array.isArray(candidate)
|
|
68
|
+
? candidate
|
|
69
|
+
: undefined;
|
|
70
|
+
}
|
|
313
71
|
export function buildResponsesPayloadFromChat(payload, context) {
|
|
314
72
|
if (!payload || typeof payload !== 'object')
|
|
315
73
|
return payload;
|
|
@@ -323,7 +81,10 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
323
81
|
const snapshotPayload = snapshotLookupKey ? consumeResponsesPayloadSnapshot(snapshotLookupKey) : undefined;
|
|
324
82
|
const passthroughPayload = snapshotLookupKey ? consumeResponsesPassthrough(snapshotLookupKey) : undefined;
|
|
325
83
|
const sourceForRetention = (passthroughPayload && typeof passthroughPayload === 'object' ? passthroughPayload : undefined) ??
|
|
326
|
-
(
|
|
84
|
+
readInlineRetentionPayload(response, '__responses_passthrough') ??
|
|
85
|
+
(snapshotPayload && typeof snapshotPayload === 'object' ? snapshotPayload : undefined) ??
|
|
86
|
+
readInlineRetentionPayload(response, '__responses_payload_snapshot');
|
|
87
|
+
const retentionContext = collectRetentionContext(context);
|
|
327
88
|
const hasChoicesArray = Array.isArray(response.choices);
|
|
328
89
|
const choicesLength = hasChoicesArray ? response.choices.length : 0;
|
|
329
90
|
if (!hasChoicesArray || choicesLength === 0) {
|
|
@@ -338,47 +99,17 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
338
99
|
: typeof response.message === 'string' && response.message.trim().length
|
|
339
100
|
? response.message.trim()
|
|
340
101
|
: 'Upstream returned non-standard Chat completion payload (missing choices).';
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
type: 'provider_error',
|
|
353
|
-
code: statusCode,
|
|
354
|
-
message
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
if (context) {
|
|
358
|
-
for (const k of ['metadata', 'parallel_tool_calls', 'tool_choice', 'include']) {
|
|
359
|
-
if (context[k] !== undefined)
|
|
360
|
-
out[k] = context[k];
|
|
361
|
-
}
|
|
362
|
-
if (!shouldStripHostManagedFields(context) && context.store !== undefined) {
|
|
363
|
-
out.store = context.store;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
if (typeof response.request_id === 'string') {
|
|
367
|
-
out.request_id = response.request_id;
|
|
368
|
-
}
|
|
369
|
-
else if (typeof response.id === 'string') {
|
|
370
|
-
out.request_id = response.id;
|
|
371
|
-
}
|
|
372
|
-
else if (typeof context?.requestId === 'string') {
|
|
373
|
-
out.request_id = context.requestId;
|
|
374
|
-
}
|
|
375
|
-
const mergedFallback = sourceForRetention && typeof sourceForRetention === 'object'
|
|
376
|
-
? mergeResponseTopLevelFields({
|
|
377
|
-
base: out,
|
|
378
|
-
source: sourceForRetention,
|
|
379
|
-
sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
|
|
380
|
-
})
|
|
381
|
-
: out;
|
|
102
|
+
const mergedFallback = buildResponsesPayloadFromChatWithNative(response, {
|
|
103
|
+
requestId: context?.requestId,
|
|
104
|
+
toolsRaw: Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [],
|
|
105
|
+
metadata: retentionContext.metadata,
|
|
106
|
+
parallelToolCalls: retentionContext.parallelToolCalls,
|
|
107
|
+
toolChoice: retentionContext.toolChoice,
|
|
108
|
+
include: retentionContext.include,
|
|
109
|
+
store: retentionContext.store,
|
|
110
|
+
stripHostManagedFields: retentionContext.stripHostManagedFields,
|
|
111
|
+
sourceForRetention: sourceForRetention
|
|
112
|
+
});
|
|
382
113
|
if (mergedFallback.metadata) {
|
|
383
114
|
stripInternalToolingMetadata(mergedFallback.metadata);
|
|
384
115
|
}
|
|
@@ -441,57 +172,25 @@ export function buildResponsesPayloadFromChat(payload, context) {
|
|
|
441
172
|
// best-effort reasoning normalization
|
|
442
173
|
}
|
|
443
174
|
}
|
|
444
|
-
const
|
|
445
|
-
response: response,
|
|
446
|
-
message,
|
|
175
|
+
const nativeBuilt = buildResponsesPayloadFromChatWithNative(response, {
|
|
447
176
|
requestId: context?.requestId,
|
|
448
|
-
|
|
177
|
+
toolsRaw: Array.isArray(context?.toolsRaw) ? context?.toolsRaw : [],
|
|
178
|
+
metadata: retentionContext.metadata,
|
|
179
|
+
parallelToolCalls: retentionContext.parallelToolCalls,
|
|
180
|
+
toolChoice: retentionContext.toolChoice,
|
|
181
|
+
include: retentionContext.include,
|
|
182
|
+
store: retentionContext.store,
|
|
183
|
+
stripHostManagedFields: retentionContext.stripHostManagedFields,
|
|
184
|
+
sourceForRetention: sourceForRetention
|
|
449
185
|
});
|
|
450
186
|
const out = {
|
|
451
|
-
|
|
452
|
-
object: 'response',
|
|
453
|
-
created_at: response.created_at || response.created || Math.floor(Date.now() / 1000),
|
|
454
|
-
model: response.model,
|
|
455
|
-
status: outputBuild.status,
|
|
456
|
-
output: outputBuild.outputItems
|
|
187
|
+
...nativeBuilt
|
|
457
188
|
};
|
|
458
|
-
if (typeof outputBuild.outputText === 'string') {
|
|
459
|
-
out.output_text = outputBuild.outputText;
|
|
460
|
-
}
|
|
461
|
-
if (outputBuild.usage !== undefined)
|
|
462
|
-
out.usage = outputBuild.usage;
|
|
463
|
-
if (outputBuild.requiredAction)
|
|
464
|
-
out.required_action = outputBuild.requiredAction;
|
|
465
189
|
normalizeResponsesToolCallArgumentsForClient(out, context);
|
|
466
|
-
if (
|
|
467
|
-
|
|
468
|
-
if (context[k] !== undefined)
|
|
469
|
-
out[k] = context[k];
|
|
470
|
-
}
|
|
471
|
-
if (!shouldStripHostManagedFields(context) && context.store !== undefined) {
|
|
472
|
-
out.store = context.store;
|
|
473
|
-
}
|
|
190
|
+
if (out.metadata) {
|
|
191
|
+
stripInternalToolingMetadata(out.metadata);
|
|
474
192
|
}
|
|
475
|
-
|
|
476
|
-
out.request_id = response.request_id;
|
|
477
|
-
}
|
|
478
|
-
else if (typeof response.id === 'string') {
|
|
479
|
-
out.request_id = response.id;
|
|
480
|
-
}
|
|
481
|
-
else if (typeof context?.requestId === 'string') {
|
|
482
|
-
out.request_id = context.requestId;
|
|
483
|
-
}
|
|
484
|
-
const merged = sourceForRetention && typeof sourceForRetention === 'object'
|
|
485
|
-
? mergeResponseTopLevelFields({
|
|
486
|
-
base: out,
|
|
487
|
-
source: sourceForRetention,
|
|
488
|
-
sourceWinsKeys: ['metadata', 'temperature', 'top_p', 'prompt_cache_key', 'reasoning']
|
|
489
|
-
})
|
|
490
|
-
: out;
|
|
491
|
-
if (merged.metadata) {
|
|
492
|
-
stripInternalToolingMetadata(merged.metadata);
|
|
493
|
-
}
|
|
494
|
-
return merged;
|
|
193
|
+
return out;
|
|
495
194
|
}
|
|
496
195
|
export function extractRequestIdFromResponse(response) {
|
|
497
196
|
if (response && typeof response === 'object' && 'metadata' in response && response.metadata && typeof response.metadata === 'object') {
|