@jsonstudio/llms 0.4.4 → 0.4.5
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/codec-registry.js +11 -1
- package/dist/conversion/codecs/anthropic-openai-codec.d.ts +13 -0
- package/dist/conversion/codecs/anthropic-openai-codec.js +18 -473
- package/dist/conversion/codecs/gemini-openai-codec.js +91 -48
- package/dist/conversion/codecs/responses-openai-codec.js +9 -2
- package/dist/conversion/hub/format-adapters/anthropic-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/chat-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/gemini-format-adapter.js +3 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.d.ts +19 -0
- package/dist/conversion/hub/format-adapters/responses-format-adapter.js +9 -0
- package/dist/conversion/hub/node-support.js +3 -1
- package/dist/conversion/hub/pipeline/hub-pipeline.js +37 -32
- package/dist/conversion/hub/response/provider-response.js +1 -1
- package/dist/conversion/hub/response/response-mappers.js +1 -1
- package/dist/conversion/hub/response/response-runtime.js +109 -10
- package/dist/conversion/hub/semantic-mappers/anthropic-mapper.js +70 -156
- package/dist/conversion/hub/semantic-mappers/chat-mapper.js +63 -52
- package/dist/conversion/hub/semantic-mappers/gemini-mapper.js +76 -143
- package/dist/conversion/hub/semantic-mappers/responses-mapper.js +40 -160
- package/dist/conversion/hub/standardized-bridge.js +3 -0
- package/dist/conversion/hub/tool-governance/rules.js +2 -2
- package/dist/conversion/index.d.ts +5 -0
- package/dist/conversion/index.js +5 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.d.ts +12 -0
- package/dist/conversion/pipeline/codecs/v2/anthropic-openai-pipeline.js +100 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.d.ts +15 -0
- package/dist/conversion/pipeline/codecs/v2/openai-openai-pipeline.js +174 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.d.ts +14 -0
- package/dist/conversion/pipeline/codecs/v2/responses-openai-pipeline.js +166 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.d.ts +13 -0
- package/dist/conversion/pipeline/codecs/v2/shared/openai-chat-helpers.js +66 -0
- package/dist/conversion/pipeline/hooks/adapter-context.d.ts +7 -0
- package/dist/conversion/pipeline/hooks/adapter-context.js +18 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.d.ts +67 -0
- package/dist/conversion/pipeline/hooks/protocol-hooks.js +1 -0
- package/dist/conversion/pipeline/index.d.ts +35 -0
- package/dist/conversion/pipeline/index.js +103 -0
- package/dist/conversion/pipeline/meta/meta-bag.d.ts +20 -0
- package/dist/conversion/pipeline/meta/meta-bag.js +81 -0
- package/dist/conversion/pipeline/schema/canonical-chat.d.ts +18 -0
- package/dist/conversion/pipeline/schema/canonical-chat.js +1 -0
- package/dist/conversion/pipeline/schema/index.d.ts +1 -0
- package/dist/conversion/pipeline/schema/index.js +1 -0
- package/dist/conversion/responses/responses-openai-bridge.d.ts +48 -0
- package/dist/conversion/responses/responses-openai-bridge.js +157 -1146
- package/dist/conversion/shared/anthropic-message-utils.d.ts +12 -0
- package/dist/conversion/shared/anthropic-message-utils.js +587 -0
- package/dist/conversion/shared/bridge-actions.d.ts +39 -0
- package/dist/conversion/shared/bridge-actions.js +709 -0
- package/dist/conversion/shared/bridge-conversation-store.d.ts +41 -0
- package/dist/conversion/shared/bridge-conversation-store.js +279 -0
- package/dist/conversion/shared/bridge-id-utils.d.ts +7 -0
- package/dist/conversion/shared/bridge-id-utils.js +42 -0
- package/dist/conversion/shared/bridge-instructions.d.ts +1 -0
- package/dist/conversion/shared/bridge-instructions.js +113 -0
- package/dist/conversion/shared/bridge-message-types.d.ts +39 -0
- package/dist/conversion/shared/bridge-message-types.js +1 -0
- package/dist/conversion/shared/bridge-message-utils.d.ts +22 -0
- package/dist/conversion/shared/bridge-message-utils.js +473 -0
- package/dist/conversion/shared/bridge-metadata.d.ts +1 -0
- package/dist/conversion/shared/bridge-metadata.js +1 -0
- package/dist/conversion/shared/bridge-policies.d.ts +18 -0
- package/dist/conversion/shared/bridge-policies.js +276 -0
- package/dist/conversion/shared/bridge-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/bridge-request-adapter.js +430 -0
- package/dist/conversion/shared/chat-output-normalizer.d.ts +4 -0
- package/dist/conversion/shared/chat-output-normalizer.js +56 -0
- package/dist/conversion/shared/chat-request-filters.js +24 -1
- package/dist/conversion/shared/gemini-tool-utils.d.ts +5 -0
- package/dist/conversion/shared/gemini-tool-utils.js +130 -0
- package/dist/conversion/shared/metadata-passthrough.d.ts +11 -0
- package/dist/conversion/shared/metadata-passthrough.js +57 -0
- package/dist/conversion/shared/output-content-normalizer.d.ts +12 -0
- package/dist/conversion/shared/output-content-normalizer.js +119 -0
- package/dist/conversion/shared/reasoning-normalizer.d.ts +21 -0
- package/dist/conversion/shared/reasoning-normalizer.js +368 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.d.ts +12 -0
- package/dist/conversion/shared/reasoning-tool-normalizer.js +132 -0
- package/dist/conversion/shared/reasoning-tool-parser.d.ts +10 -0
- package/dist/conversion/shared/reasoning-tool-parser.js +95 -0
- package/dist/conversion/shared/reasoning-utils.d.ts +2 -0
- package/dist/conversion/shared/reasoning-utils.js +42 -0
- package/dist/conversion/shared/responses-conversation-store.js +5 -11
- package/dist/conversion/shared/responses-message-utils.d.ts +15 -0
- package/dist/conversion/shared/responses-message-utils.js +206 -0
- package/dist/conversion/shared/responses-output-builder.d.ts +15 -0
- package/dist/conversion/shared/responses-output-builder.js +179 -0
- package/dist/conversion/shared/responses-output-utils.d.ts +7 -0
- package/dist/conversion/shared/responses-output-utils.js +108 -0
- package/dist/conversion/shared/responses-request-adapter.d.ts +28 -0
- package/dist/conversion/shared/responses-request-adapter.js +9 -40
- package/dist/conversion/shared/responses-response-utils.d.ts +3 -0
- package/dist/conversion/shared/responses-response-utils.js +209 -0
- package/dist/conversion/shared/responses-tool-utils.d.ts +12 -0
- package/dist/conversion/shared/responses-tool-utils.js +90 -0
- package/dist/conversion/shared/responses-types.d.ts +33 -0
- package/dist/conversion/shared/responses-types.js +1 -0
- package/dist/conversion/shared/tool-call-utils.d.ts +11 -0
- package/dist/conversion/shared/tool-call-utils.js +56 -0
- package/dist/conversion/shared/tool-mapping.d.ts +19 -0
- package/dist/conversion/shared/tool-mapping.js +124 -0
- package/dist/conversion/shared/tool-normalizers.d.ts +4 -0
- package/dist/conversion/shared/tool-normalizers.js +84 -0
- package/dist/router/virtual-router/bootstrap.js +18 -3
- package/dist/router/virtual-router/provider-registry.js +4 -2
- package/dist/router/virtual-router/types.d.ts +212 -0
- package/dist/sse/index.d.ts +38 -2
- package/dist/sse/index.js +27 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/anthropic-json-to-sse-converter.js +106 -73
- package/dist/sse/json-to-sse/chat-json-to-sse-converter.js +6 -2
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.d.ts +14 -0
- package/dist/sse/json-to-sse/gemini-json-to-sse-converter.js +99 -0
- package/dist/sse/json-to-sse/index.d.ts +7 -0
- package/dist/sse/json-to-sse/index.js +2 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.d.ts +13 -0
- package/dist/sse/json-to-sse/sequencers/anthropic-sequencer.js +150 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.d.ts +39 -0
- package/dist/sse/json-to-sse/sequencers/chat-sequencer.js +49 -3
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.d.ts +10 -0
- package/dist/sse/json-to-sse/sequencers/gemini-sequencer.js +95 -0
- package/dist/sse/json-to-sse/sequencers/responses-sequencer.js +31 -5
- package/dist/sse/registry/sse-codec-registry.d.ts +32 -0
- package/dist/sse/registry/sse-codec-registry.js +30 -1
- package/dist/sse/shared/reasoning-dispatcher.d.ts +10 -0
- package/dist/sse/shared/reasoning-dispatcher.js +25 -0
- package/dist/sse/shared/responses-output-normalizer.d.ts +12 -0
- package/dist/sse/shared/responses-output-normalizer.js +45 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/anthropic-event-serializer.js +9 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.d.ts +2 -0
- package/dist/sse/shared/serializers/gemini-event-serializer.js +5 -0
- package/dist/sse/shared/serializers/index.d.ts +41 -0
- package/dist/sse/shared/serializers/index.js +2 -0
- package/dist/sse/shared/writer.d.ts +127 -0
- package/dist/sse/shared/writer.js +37 -1
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.d.ts +11 -0
- package/dist/sse/sse-to-json/anthropic-sse-to-json-converter.js +92 -127
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.d.ts +16 -0
- package/dist/sse/sse-to-json/builders/anthropic-response-builder.js +151 -0
- package/dist/sse/sse-to-json/builders/response-builder.d.ts +165 -0
- package/dist/sse/sse-to-json/builders/response-builder.js +27 -6
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.d.ts +114 -0
- package/dist/sse/sse-to-json/chat-sse-to-json-converter.js +79 -3
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.d.ts +13 -0
- package/dist/sse/sse-to-json/gemini-sse-to-json-converter.js +160 -0
- package/dist/sse/sse-to-json/index.d.ts +7 -0
- package/dist/sse/sse-to-json/index.js +2 -0
- package/dist/sse/sse-to-json/parsers/sse-parser.js +53 -1
- package/dist/sse/types/anthropic-types.d.ts +170 -0
- package/dist/sse/types/anthropic-types.js +8 -5
- package/dist/sse/types/chat-types.d.ts +10 -0
- package/dist/sse/types/chat-types.js +2 -1
- package/dist/sse/types/core-interfaces.d.ts +1 -1
- package/dist/sse/types/gemini-types.d.ts +116 -0
- package/dist/sse/types/gemini-types.js +5 -0
- package/dist/sse/types/index.d.ts +5 -2
- package/dist/sse/types/index.js +2 -0
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
2
2
|
import { buildOpenAIChatFromGeminiRequest } from '../../codecs/gemini-openai-codec.js';
|
|
3
|
+
import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
|
|
4
|
+
import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
|
|
5
|
+
import { encodeMetadataPassthrough, extractMetadataPassthrough } from '../../shared/metadata-passthrough.js';
|
|
6
|
+
import { mapBridgeToolsToChat, mapChatToolsToBridge } from '../../shared/tool-mapping.js';
|
|
7
|
+
import { prepareGeminiToolsForBridge, buildGeminiToolsFromBridge } from '../../shared/gemini-tool-utils.js';
|
|
3
8
|
const GENERATION_CONFIG_KEYS = [
|
|
4
9
|
{ source: 'temperature', target: 'temperature' },
|
|
5
10
|
{ source: 'topP', target: 'top_p' },
|
|
@@ -9,65 +14,8 @@ const GENERATION_CONFIG_KEYS = [
|
|
|
9
14
|
{ source: 'responseMimeType', target: 'response_mime_type' },
|
|
10
15
|
{ source: 'stopSequences', target: 'stop_sequences' }
|
|
11
16
|
];
|
|
12
|
-
const GEMINI_TOP_LEVEL_FIELDS = new Set([
|
|
13
|
-
'model',
|
|
14
|
-
'contents',
|
|
15
|
-
'tools',
|
|
16
|
-
'systemInstruction',
|
|
17
|
-
'generationConfig',
|
|
18
|
-
'safetySettings',
|
|
19
|
-
'metadata',
|
|
20
|
-
'toolConfig'
|
|
21
|
-
]);
|
|
22
17
|
const PASSTHROUGH_METADATA_PREFIX = 'rcc_passthrough_';
|
|
23
18
|
const PASSTHROUGH_PARAMETERS = ['tool_choice'];
|
|
24
|
-
function encodePassthroughParametersFromChat(chat) {
|
|
25
|
-
if (!chat.parameters)
|
|
26
|
-
return undefined;
|
|
27
|
-
const encoded = {};
|
|
28
|
-
for (const key of PASSTHROUGH_PARAMETERS) {
|
|
29
|
-
const value = chat.parameters[key];
|
|
30
|
-
if (value === undefined)
|
|
31
|
-
continue;
|
|
32
|
-
try {
|
|
33
|
-
encoded[`${PASSTHROUGH_METADATA_PREFIX}${key}`] = JSON.stringify(value);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return Object.keys(encoded).length ? encoded : undefined;
|
|
40
|
-
}
|
|
41
|
-
function extractPassthroughParametersFromMetadata(metadataField) {
|
|
42
|
-
if (!metadataField)
|
|
43
|
-
return {};
|
|
44
|
-
const cloned = jsonClone(metadataField);
|
|
45
|
-
const passthrough = {};
|
|
46
|
-
let mutated = false;
|
|
47
|
-
for (const key of Object.keys(cloned)) {
|
|
48
|
-
if (!key.startsWith(PASSTHROUGH_METADATA_PREFIX))
|
|
49
|
-
continue;
|
|
50
|
-
const paramKey = key.slice(PASSTHROUGH_METADATA_PREFIX.length);
|
|
51
|
-
if (!PASSTHROUGH_PARAMETERS.includes(paramKey))
|
|
52
|
-
continue;
|
|
53
|
-
const rawValue = cloned[key];
|
|
54
|
-
if (typeof rawValue !== 'string')
|
|
55
|
-
continue;
|
|
56
|
-
try {
|
|
57
|
-
const parsed = rawValue ? JSON.parse(rawValue) : undefined;
|
|
58
|
-
passthrough[paramKey] = parsed;
|
|
59
|
-
delete cloned[key];
|
|
60
|
-
mutated = true;
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
cleaned: mutated ? (Object.keys(cloned).length ? cloned : undefined) : cloned,
|
|
68
|
-
passthrough: Object.keys(passthrough).length ? passthrough : undefined
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
19
|
function normalizeToolOutputs(messages, missing) {
|
|
72
20
|
const outputs = [];
|
|
73
21
|
messages.forEach((msg, index) => {
|
|
@@ -119,53 +67,6 @@ function collectSystemSegments(systemInstruction) {
|
|
|
119
67
|
const text = flatten(systemInstruction).trim();
|
|
120
68
|
return text ? [text] : [];
|
|
121
69
|
}
|
|
122
|
-
function normalizeGeminiTools(rawTools, missing) {
|
|
123
|
-
if (!rawTools)
|
|
124
|
-
return undefined;
|
|
125
|
-
const arr = Array.isArray(rawTools) ? rawTools : [rawTools];
|
|
126
|
-
const tools = [];
|
|
127
|
-
arr.forEach((entry, index) => {
|
|
128
|
-
if (!entry || typeof entry !== 'object') {
|
|
129
|
-
missing.push({ path: `tools[${index}]`, reason: 'invalid_entry', originalValue: jsonClone(entry) });
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const obj = entry;
|
|
133
|
-
const declarations = Array.isArray(obj.functionDeclarations) ? obj.functionDeclarations : undefined;
|
|
134
|
-
if (declarations && declarations.length) {
|
|
135
|
-
declarations.forEach((decl, declIndex) => {
|
|
136
|
-
if (!decl || typeof decl !== 'object') {
|
|
137
|
-
missing.push({ path: `tools[${index}].functionDeclarations[${declIndex}]`, reason: 'invalid_entry' });
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const def = decl;
|
|
141
|
-
const name = typeof def.name === 'string' ? def.name : undefined;
|
|
142
|
-
if (!name)
|
|
143
|
-
return;
|
|
144
|
-
tools.push({
|
|
145
|
-
type: 'function',
|
|
146
|
-
function: {
|
|
147
|
-
name,
|
|
148
|
-
description: typeof def.description === 'string' ? def.description : undefined,
|
|
149
|
-
parameters: isJsonObject(def.parameters) ? jsonClone(def.parameters) : { type: 'object', properties: {} }
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const name = typeof obj.name === 'string' ? obj.name : undefined;
|
|
156
|
-
if (!name)
|
|
157
|
-
return;
|
|
158
|
-
tools.push({
|
|
159
|
-
type: 'function',
|
|
160
|
-
function: {
|
|
161
|
-
name,
|
|
162
|
-
description: typeof obj.description === 'string' ? obj.description : undefined,
|
|
163
|
-
parameters: isJsonObject(obj.parameters) ? jsonClone(obj.parameters) : { type: 'object', properties: {} }
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
return tools.length ? tools : undefined;
|
|
168
|
-
}
|
|
169
70
|
function collectParameters(payload) {
|
|
170
71
|
const params = {};
|
|
171
72
|
if (typeof payload.model === 'string') {
|
|
@@ -189,15 +90,6 @@ function collectParameters(payload) {
|
|
|
189
90
|
}
|
|
190
91
|
return Object.keys(params).length ? params : undefined;
|
|
191
92
|
}
|
|
192
|
-
function collectExtraFields(payload) {
|
|
193
|
-
const extras = {};
|
|
194
|
-
for (const key of Object.keys(payload)) {
|
|
195
|
-
if (GEMINI_TOP_LEVEL_FIELDS.has(key))
|
|
196
|
-
continue;
|
|
197
|
-
extras[key] = payload[key];
|
|
198
|
-
}
|
|
199
|
-
return Object.keys(extras).length ? extras : undefined;
|
|
200
|
-
}
|
|
201
93
|
function buildGeminiRequestFromChat(chat, metadata) {
|
|
202
94
|
const contents = [];
|
|
203
95
|
for (const message of chat.messages) {
|
|
@@ -276,7 +168,11 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
276
168
|
}
|
|
277
169
|
}
|
|
278
170
|
if (chat.tools && chat.tools.length) {
|
|
279
|
-
|
|
171
|
+
const bridgeDefs = mapChatToolsToBridge(chat.tools);
|
|
172
|
+
const geminiTools = buildGeminiToolsFromBridge(bridgeDefs);
|
|
173
|
+
if (geminiTools) {
|
|
174
|
+
request.tools = geminiTools;
|
|
175
|
+
}
|
|
280
176
|
}
|
|
281
177
|
const generationConfig = buildGenerationConfigFromParameters(chat.parameters || {});
|
|
282
178
|
if (Object.keys(generationConfig).length) {
|
|
@@ -309,7 +205,10 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
309
205
|
request.metadata = request.metadata ?? {};
|
|
310
206
|
request.metadata.__rcc_tools_field_present = '1';
|
|
311
207
|
}
|
|
312
|
-
const passthrough =
|
|
208
|
+
const passthrough = encodeMetadataPassthrough(chat.parameters, {
|
|
209
|
+
prefix: PASSTHROUGH_METADATA_PREFIX,
|
|
210
|
+
keys: PASSTHROUGH_PARAMETERS
|
|
211
|
+
});
|
|
313
212
|
if (passthrough) {
|
|
314
213
|
request.metadata = request.metadata ?? {};
|
|
315
214
|
for (const [key, value] of Object.entries(passthrough)) {
|
|
@@ -318,26 +217,6 @@ function buildGeminiRequestFromChat(chat, metadata) {
|
|
|
318
217
|
}
|
|
319
218
|
return request;
|
|
320
219
|
}
|
|
321
|
-
function buildGeminiToolsFromChat(tools) {
|
|
322
|
-
const geminiTools = [];
|
|
323
|
-
tools.forEach((tool) => {
|
|
324
|
-
if (!tool || tool.type !== 'function')
|
|
325
|
-
return;
|
|
326
|
-
const fn = tool.function;
|
|
327
|
-
if (!fn || typeof fn.name !== 'string')
|
|
328
|
-
return;
|
|
329
|
-
geminiTools.push({
|
|
330
|
-
functionDeclarations: [
|
|
331
|
-
{
|
|
332
|
-
name: fn.name,
|
|
333
|
-
description: fn.description,
|
|
334
|
-
parameters: cloneAsJsonValue(fn.parameters ?? { type: 'object', properties: {} })
|
|
335
|
-
}
|
|
336
|
-
]
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
return geminiTools;
|
|
340
|
-
}
|
|
341
220
|
function buildGenerationConfigFromParameters(parameters) {
|
|
342
221
|
const config = {};
|
|
343
222
|
for (const { source, target } of GENERATION_CONFIG_KEYS) {
|
|
@@ -391,12 +270,13 @@ export class GeminiSemanticMapper {
|
|
|
391
270
|
async toChat(format, ctx) {
|
|
392
271
|
const payload = (format.payload ?? {});
|
|
393
272
|
const missing = [];
|
|
394
|
-
const { messages } = buildOpenAIChatFromGeminiRequest(payload);
|
|
273
|
+
const { messages: builtMessages } = buildOpenAIChatFromGeminiRequest(payload);
|
|
274
|
+
let messages = Array.isArray(builtMessages) ? builtMessages : [];
|
|
395
275
|
if (!Array.isArray(payload.contents)) {
|
|
396
276
|
missing.push({ path: 'contents', reason: 'absent' });
|
|
397
277
|
}
|
|
398
|
-
const
|
|
399
|
-
const tools =
|
|
278
|
+
const bridgeTools = prepareGeminiToolsForBridge(payload.tools, missing);
|
|
279
|
+
const tools = bridgeTools ? mapBridgeToolsToChat(bridgeTools) : undefined;
|
|
400
280
|
let parameters = collectParameters(payload);
|
|
401
281
|
const metadata = { context: ctx };
|
|
402
282
|
const systemSegments = collectSystemSegments(payload.systemInstruction);
|
|
@@ -414,15 +294,38 @@ export class GeminiSemanticMapper {
|
|
|
414
294
|
if (missing.length) {
|
|
415
295
|
metadata.missingFields = missing;
|
|
416
296
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
297
|
+
try {
|
|
298
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
|
|
299
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
300
|
+
if (actions?.length) {
|
|
301
|
+
const actionState = createBridgeActionState({
|
|
302
|
+
messages: messages,
|
|
303
|
+
rawRequest: payload,
|
|
304
|
+
metadata: metadata
|
|
305
|
+
});
|
|
306
|
+
runBridgeActionPipeline({
|
|
307
|
+
stage: 'request_inbound',
|
|
308
|
+
actions,
|
|
309
|
+
protocol: bridgePolicy?.protocol ?? 'gemini-chat',
|
|
310
|
+
moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
|
|
311
|
+
requestId: ctx.requestId,
|
|
312
|
+
state: actionState
|
|
313
|
+
});
|
|
314
|
+
messages = actionState.messages;
|
|
315
|
+
}
|
|
420
316
|
}
|
|
421
|
-
|
|
317
|
+
catch {
|
|
318
|
+
// best-effort policy execution
|
|
319
|
+
}
|
|
320
|
+
const toolOutputs = normalizeToolOutputs(messages, missing);
|
|
321
|
+
const passthrough = extractMetadataPassthrough(payload.metadata, {
|
|
322
|
+
prefix: PASSTHROUGH_METADATA_PREFIX,
|
|
323
|
+
keys: PASSTHROUGH_PARAMETERS
|
|
324
|
+
});
|
|
422
325
|
if (passthrough.passthrough) {
|
|
423
326
|
parameters = { ...(parameters || {}), ...passthrough.passthrough };
|
|
424
327
|
}
|
|
425
|
-
const providerMetadataSource = passthrough.
|
|
328
|
+
const providerMetadataSource = passthrough.metadata ?? payload.metadata;
|
|
426
329
|
if (providerMetadataSource) {
|
|
427
330
|
const providerMetadata = jsonClone(providerMetadataSource);
|
|
428
331
|
let toolsFieldPresent = false;
|
|
@@ -465,6 +368,36 @@ export class GeminiSemanticMapper {
|
|
|
465
368
|
}
|
|
466
369
|
async fromChat(chat, ctx) {
|
|
467
370
|
const envelopePayload = buildGeminiRequestFromChat(chat, chat.metadata);
|
|
371
|
+
try {
|
|
372
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'gemini-chat' });
|
|
373
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
|
|
374
|
+
if (actions?.length) {
|
|
375
|
+
const capturedToolResults = Array.isArray(chat.toolOutputs)
|
|
376
|
+
? chat.toolOutputs.map((entry) => ({
|
|
377
|
+
tool_call_id: entry.tool_call_id,
|
|
378
|
+
output: entry.content,
|
|
379
|
+
name: entry.name
|
|
380
|
+
}))
|
|
381
|
+
: undefined;
|
|
382
|
+
const actionState = createBridgeActionState({
|
|
383
|
+
messages: Array.isArray(chat.messages) ? chat.messages : [],
|
|
384
|
+
rawRequest: envelopePayload,
|
|
385
|
+
metadata: chat.metadata,
|
|
386
|
+
capturedToolResults
|
|
387
|
+
});
|
|
388
|
+
runBridgeActionPipeline({
|
|
389
|
+
stage: 'request_outbound',
|
|
390
|
+
actions,
|
|
391
|
+
protocol: bridgePolicy?.protocol ?? 'gemini-chat',
|
|
392
|
+
moduleType: bridgePolicy?.moduleType ?? 'gemini-chat',
|
|
393
|
+
requestId: ctx.requestId,
|
|
394
|
+
state: actionState
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
catch {
|
|
399
|
+
// ignore policy failures
|
|
400
|
+
}
|
|
468
401
|
return {
|
|
469
402
|
protocol: 'gemini-chat',
|
|
470
403
|
direction: 'response',
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { isJsonObject, jsonClone } from '../types/json.js';
|
|
2
|
+
import { createBridgeActionState, runBridgeActionPipeline } from '../../shared/bridge-actions.js';
|
|
3
|
+
import { resolveBridgePolicy, resolvePolicyActions } from '../../shared/bridge-policies.js';
|
|
2
4
|
import { captureResponsesContext, buildChatRequestFromResponses, buildResponsesRequestFromChat } from '../../responses/responses-openai-bridge.js';
|
|
3
5
|
const RESPONSES_PARAMETER_KEYS = [
|
|
4
6
|
'model',
|
|
@@ -20,34 +22,6 @@ const RESPONSES_PARAMETER_KEYS = [
|
|
|
20
22
|
'stop_sequences',
|
|
21
23
|
'modalities'
|
|
22
24
|
];
|
|
23
|
-
const RESPONSES_TOP_LEVEL_FIELDS = new Set([
|
|
24
|
-
'input',
|
|
25
|
-
'tools',
|
|
26
|
-
'tool_outputs',
|
|
27
|
-
'instructions',
|
|
28
|
-
'response_id',
|
|
29
|
-
'previous_response_id',
|
|
30
|
-
'conversation',
|
|
31
|
-
'parallel_tool_calls',
|
|
32
|
-
'tool_choice',
|
|
33
|
-
'stream',
|
|
34
|
-
'metadata',
|
|
35
|
-
'include',
|
|
36
|
-
'store',
|
|
37
|
-
'user',
|
|
38
|
-
'response_format',
|
|
39
|
-
'model',
|
|
40
|
-
'temperature',
|
|
41
|
-
'top_p',
|
|
42
|
-
'top_k',
|
|
43
|
-
'max_tokens',
|
|
44
|
-
'max_output_tokens',
|
|
45
|
-
'logit_bias',
|
|
46
|
-
'seed',
|
|
47
|
-
'modalities',
|
|
48
|
-
'instructions_is_raw'
|
|
49
|
-
]);
|
|
50
|
-
const RESPONSES_METADATA_SENTINEL_TOOLS = '__rcc_tools_field_present';
|
|
51
25
|
function mapToolOutputs(entries, missing) {
|
|
52
26
|
if (!entries || !entries.length)
|
|
53
27
|
return undefined;
|
|
@@ -146,15 +120,6 @@ function normalizeMessages(value, missing) {
|
|
|
146
120
|
});
|
|
147
121
|
return messages;
|
|
148
122
|
}
|
|
149
|
-
function collectExtraFields(payload) {
|
|
150
|
-
const extras = {};
|
|
151
|
-
for (const key of Object.keys(payload)) {
|
|
152
|
-
if (RESPONSES_TOP_LEVEL_FIELDS.has(key) || RESPONSES_PARAMETER_KEYS.includes(key))
|
|
153
|
-
continue;
|
|
154
|
-
extras[key] = payload[key];
|
|
155
|
-
}
|
|
156
|
-
return Object.keys(extras).length ? extras : undefined;
|
|
157
|
-
}
|
|
158
123
|
function serializeSystemContent(message) {
|
|
159
124
|
if (!message)
|
|
160
125
|
return undefined;
|
|
@@ -192,40 +157,37 @@ export class ResponsesSemanticMapper {
|
|
|
192
157
|
const responsesContext = captureResponsesContext(payload, { route: { requestId: ctx.requestId } });
|
|
193
158
|
const { request, toolsNormalized } = buildChatRequestFromResponses(payload, responsesContext);
|
|
194
159
|
const missingFields = [];
|
|
195
|
-
if (!responsesContext.instructions) {
|
|
196
|
-
missingFields.push({ path: 'instructions', reason: 'absent' });
|
|
197
|
-
}
|
|
198
160
|
const messages = normalizeMessages(request.messages, missingFields);
|
|
199
161
|
const toolOutputs = mapToolOutputs(payload.tool_outputs, missingFields);
|
|
200
162
|
const parameters = collectParameters(payload, responsesContext.stream);
|
|
201
163
|
const metadata = { context: ctx };
|
|
202
|
-
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
164
|
+
try {
|
|
165
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
166
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_inbound');
|
|
167
|
+
if (actions?.length) {
|
|
168
|
+
const actionState = createBridgeActionState({
|
|
169
|
+
rawRequest: payload,
|
|
170
|
+
metadata: metadata
|
|
171
|
+
});
|
|
172
|
+
runBridgeActionPipeline({
|
|
173
|
+
stage: 'request_inbound',
|
|
174
|
+
actions,
|
|
175
|
+
protocol: bridgePolicy?.protocol ?? 'openai-responses',
|
|
176
|
+
moduleType: bridgePolicy?.moduleType ?? 'openai-responses',
|
|
177
|
+
requestId: ctx.requestId,
|
|
178
|
+
state: actionState
|
|
179
|
+
});
|
|
212
180
|
}
|
|
213
|
-
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
// ignore policy hook failures
|
|
214
184
|
}
|
|
215
185
|
if (missingFields.length) {
|
|
216
186
|
metadata.missingFields = missingFields;
|
|
217
187
|
}
|
|
218
|
-
const extraFields = collectExtraFields(payload);
|
|
219
|
-
if (extraFields) {
|
|
220
|
-
metadata.extraFields = extraFields;
|
|
221
|
-
}
|
|
222
188
|
if (responsesContext.responseFormat) {
|
|
223
189
|
metadata.responseFormat = jsonClone(responsesContext.responseFormat);
|
|
224
190
|
}
|
|
225
|
-
const hasSystemMessage = messages.some(message => message?.role === 'system');
|
|
226
|
-
if (!hasSystemMessage && typeof responsesContext.instructions === 'string' && responsesContext.instructions.length) {
|
|
227
|
-
messages.unshift({ role: 'system', content: responsesContext.instructions });
|
|
228
|
-
}
|
|
229
191
|
return {
|
|
230
192
|
messages,
|
|
231
193
|
tools: normalizeTools(toolsNormalized, missingFields),
|
|
@@ -254,100 +216,6 @@ export class ResponsesSemanticMapper {
|
|
|
254
216
|
: { originalSystemMessages };
|
|
255
217
|
const responsesResult = buildResponsesRequestFromChat(requestShape, responsesContext);
|
|
256
218
|
const responses = responsesResult.request;
|
|
257
|
-
const normalizeUserInstructionFromMessage = (message) => {
|
|
258
|
-
if (!message)
|
|
259
|
-
return undefined;
|
|
260
|
-
const content = message.content;
|
|
261
|
-
if (typeof content === 'string') {
|
|
262
|
-
const trimmed = content.trim();
|
|
263
|
-
return trimmed.length ? trimmed : undefined;
|
|
264
|
-
}
|
|
265
|
-
if (Array.isArray(content)) {
|
|
266
|
-
const fragments = [];
|
|
267
|
-
for (const fragment of content) {
|
|
268
|
-
if (typeof fragment === 'string') {
|
|
269
|
-
fragments.push(fragment);
|
|
270
|
-
}
|
|
271
|
-
else if (fragment && typeof fragment === 'object') {
|
|
272
|
-
const partText = typeof fragment.text === 'string'
|
|
273
|
-
? String(fragment.text)
|
|
274
|
-
: (typeof fragment.content === 'string'
|
|
275
|
-
? String(fragment.content)
|
|
276
|
-
: undefined);
|
|
277
|
-
if (partText && partText.trim().length) {
|
|
278
|
-
fragments.push(partText);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
if (fragments.length) {
|
|
283
|
-
const joined = fragments.join('').trim();
|
|
284
|
-
return joined.length ? joined : undefined;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return undefined;
|
|
288
|
-
};
|
|
289
|
-
const normalizeUserInstructionFromInput = (payload) => {
|
|
290
|
-
const entries = Array.isArray(payload.input) ? payload.input : [];
|
|
291
|
-
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
292
|
-
const entry = entries[i];
|
|
293
|
-
const roleSource = entry.role ?? entry?.message?.role;
|
|
294
|
-
const role = typeof roleSource === 'string' ? roleSource.toLowerCase() : '';
|
|
295
|
-
if (role !== 'user')
|
|
296
|
-
continue;
|
|
297
|
-
const content = entry?.content ?? entry?.message?.content;
|
|
298
|
-
if (typeof content === 'string') {
|
|
299
|
-
const trimmed = content.trim();
|
|
300
|
-
if (trimmed.length)
|
|
301
|
-
return trimmed;
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
if (Array.isArray(content)) {
|
|
305
|
-
const parts = [];
|
|
306
|
-
for (const fragment of content) {
|
|
307
|
-
if (typeof fragment === 'string') {
|
|
308
|
-
parts.push(fragment);
|
|
309
|
-
}
|
|
310
|
-
else if (fragment && typeof fragment === 'object') {
|
|
311
|
-
const text = typeof fragment.text === 'string'
|
|
312
|
-
? String(fragment.text)
|
|
313
|
-
: (typeof fragment.content === 'string'
|
|
314
|
-
? String(fragment.content)
|
|
315
|
-
: undefined);
|
|
316
|
-
if (text && text.trim().length) {
|
|
317
|
-
parts.push(text);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (parts.length) {
|
|
322
|
-
const joined = parts.join('').trim();
|
|
323
|
-
if (joined.length)
|
|
324
|
-
return joined;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
return undefined;
|
|
329
|
-
};
|
|
330
|
-
const latestUserMessage = [...(chat.messages || [])]
|
|
331
|
-
.reverse()
|
|
332
|
-
.find((message) => typeof message?.role === 'string' && message.role.toLowerCase() === 'user');
|
|
333
|
-
const existingInstruction = typeof responses.instructions === 'string'
|
|
334
|
-
? responses.instructions.trim()
|
|
335
|
-
: undefined;
|
|
336
|
-
if (!existingInstruction || !existingInstruction.length) {
|
|
337
|
-
const messageInstruction = normalizeUserInstructionFromMessage(latestUserMessage);
|
|
338
|
-
if (messageInstruction && messageInstruction.trim().length) {
|
|
339
|
-
responses.instructions = messageInstruction.trim();
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
const inputInstruction = normalizeUserInstructionFromInput(responses);
|
|
343
|
-
if (inputInstruction && inputInstruction.trim().length) {
|
|
344
|
-
responses.instructions = inputInstruction.trim();
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
responses.instructions = existingInstruction;
|
|
350
|
-
}
|
|
351
219
|
if (chat.parameters && chat.parameters.stream !== undefined) {
|
|
352
220
|
responses.stream = chat.parameters.stream;
|
|
353
221
|
}
|
|
@@ -358,15 +226,27 @@ export class ResponsesSemanticMapper {
|
|
|
358
226
|
name: entry.name
|
|
359
227
|
}));
|
|
360
228
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
responses
|
|
367
|
-
|
|
229
|
+
try {
|
|
230
|
+
const bridgePolicy = resolveBridgePolicy({ protocol: 'openai-responses', moduleType: 'openai-responses' });
|
|
231
|
+
const actions = resolvePolicyActions(bridgePolicy, 'request_outbound');
|
|
232
|
+
if (actions?.length) {
|
|
233
|
+
const actionState = createBridgeActionState({
|
|
234
|
+
rawRequest: responses,
|
|
235
|
+
metadata: chat.metadata
|
|
236
|
+
});
|
|
237
|
+
runBridgeActionPipeline({
|
|
238
|
+
stage: 'request_outbound',
|
|
239
|
+
actions,
|
|
240
|
+
protocol: bridgePolicy?.protocol ?? 'openai-responses',
|
|
241
|
+
moduleType: bridgePolicy?.moduleType ?? 'openai-responses',
|
|
242
|
+
requestId: ctx.requestId,
|
|
243
|
+
state: actionState
|
|
244
|
+
});
|
|
368
245
|
}
|
|
369
246
|
}
|
|
247
|
+
catch {
|
|
248
|
+
// ignore metadata propagation failures
|
|
249
|
+
}
|
|
370
250
|
return {
|
|
371
251
|
protocol: 'openai-responses',
|
|
372
252
|
direction: 'response',
|
|
@@ -56,6 +56,9 @@ export function standardizedToChatEnvelope(request, options) {
|
|
|
56
56
|
const metadata = {
|
|
57
57
|
context: adapterContext
|
|
58
58
|
};
|
|
59
|
+
if (typeof adapterContext.toolCallIdStyle === 'string' && adapterContext.toolCallIdStyle.length) {
|
|
60
|
+
metadata.toolCallIdStyle = adapterContext.toolCallIdStyle;
|
|
61
|
+
}
|
|
59
62
|
if (hubState?.missingFields) {
|
|
60
63
|
metadata.missingFields = hubState.missingFields;
|
|
61
64
|
}
|
|
@@ -27,3 +27,8 @@ export { governTools } from './shared/tool-governor.js';
|
|
|
27
27
|
export * from './shared/streaming-text-extractor.js';
|
|
28
28
|
export * from './shared/tool-harvester.js';
|
|
29
29
|
export * from './responses/responses-openai-bridge.js';
|
|
30
|
+
export { ProtocolConversionPipeline, PipelineValidationError, type ProtocolInboundPipelineOptions, type ProtocolInboundPipelineResult, type ProtocolOutboundPipelineOptions, type ProtocolOutboundPipelineResult } from './pipeline/index.js';
|
|
31
|
+
export * from './pipeline/schema/index.js';
|
|
32
|
+
export * from './pipeline/hooks/protocol-hooks.js';
|
|
33
|
+
export * from './pipeline/hooks/adapter-context.js';
|
|
34
|
+
export * from './pipeline/meta/meta-bag.js';
|
package/dist/conversion/index.js
CHANGED
|
@@ -27,3 +27,8 @@ export { governTools } from './shared/tool-governor.js';
|
|
|
27
27
|
export * from './shared/streaming-text-extractor.js';
|
|
28
28
|
export * from './shared/tool-harvester.js';
|
|
29
29
|
export * from './responses/responses-openai-bridge.js';
|
|
30
|
+
export { ProtocolConversionPipeline, PipelineValidationError } from './pipeline/index.js';
|
|
31
|
+
export * from './pipeline/schema/index.js';
|
|
32
|
+
export * from './pipeline/hooks/protocol-hooks.js';
|
|
33
|
+
export * from './pipeline/hooks/adapter-context.js';
|
|
34
|
+
export * from './pipeline/meta/meta-bag.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConversionCodec, ConversionContext, ConversionProfile } from '../../../types.js';
|
|
2
|
+
import type { JsonObject } from '../../../hub/types/json.js';
|
|
3
|
+
export declare class AnthropicOpenAIPipelineCodec implements ConversionCodec {
|
|
4
|
+
readonly id = "anthropic-openai-v2";
|
|
5
|
+
private readonly pipeline;
|
|
6
|
+
private initialized;
|
|
7
|
+
constructor();
|
|
8
|
+
initialize(): Promise<void>;
|
|
9
|
+
private ensureInitialized;
|
|
10
|
+
convertRequest(payload: unknown, profile: ConversionProfile, context: ConversionContext): Promise<JsonObject>;
|
|
11
|
+
convertResponse(payload: unknown, profile: ConversionProfile, context: ConversionContext): Promise<JsonObject>;
|
|
12
|
+
}
|