@jsonstudio/llms 0.4.4 → 0.4.6
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-governor.js +5 -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
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ResponsesRequestContext } from './bridge-request-adapter.js';
|
|
2
|
+
declare class BridgeConversationStore {
|
|
3
|
+
private requestMap;
|
|
4
|
+
private responseIndex;
|
|
5
|
+
captureRequestContext(args: {
|
|
6
|
+
requestId: string;
|
|
7
|
+
payload: Record<string, unknown>;
|
|
8
|
+
context: ResponsesRequestContext;
|
|
9
|
+
}): void;
|
|
10
|
+
recordResponse(args: {
|
|
11
|
+
requestId: string;
|
|
12
|
+
response: Record<string, unknown>;
|
|
13
|
+
}): void;
|
|
14
|
+
resumeConversation(responseId: string, submitPayload: Record<string, unknown>, options?: {
|
|
15
|
+
requestId?: string;
|
|
16
|
+
}): {
|
|
17
|
+
payload: Record<string, unknown>;
|
|
18
|
+
meta: Record<string, unknown>;
|
|
19
|
+
};
|
|
20
|
+
clearRequest(requestId: string): void;
|
|
21
|
+
private cleanupEntry;
|
|
22
|
+
private prune;
|
|
23
|
+
}
|
|
24
|
+
export declare const bridgeConversationStore: BridgeConversationStore;
|
|
25
|
+
export declare function captureBridgeRequestContext(args: {
|
|
26
|
+
requestId: string;
|
|
27
|
+
payload: Record<string, unknown>;
|
|
28
|
+
context: ResponsesRequestContext;
|
|
29
|
+
}): void;
|
|
30
|
+
export declare function recordBridgeResponse(args: {
|
|
31
|
+
requestId: string;
|
|
32
|
+
response: Record<string, unknown>;
|
|
33
|
+
}): void;
|
|
34
|
+
export declare function resumeBridgeConversation(responseId: string, submitPayload: Record<string, unknown>, options?: {
|
|
35
|
+
requestId?: string;
|
|
36
|
+
}): {
|
|
37
|
+
payload: Record<string, unknown>;
|
|
38
|
+
meta: Record<string, unknown>;
|
|
39
|
+
};
|
|
40
|
+
export declare function clearBridgeConversationByRequestId(requestId: string): void;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const TTL_MS = 1000 * 60 * 30; // 30 minutes safety window
|
|
2
|
+
function cloneDeep(value) {
|
|
3
|
+
try {
|
|
4
|
+
if (typeof globalThis.structuredClone === 'function') {
|
|
5
|
+
return globalThis.structuredClone(value);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
/* ignore */
|
|
10
|
+
}
|
|
11
|
+
return JSON.parse(JSON.stringify(value ?? null));
|
|
12
|
+
}
|
|
13
|
+
function isRecord(value) {
|
|
14
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
15
|
+
}
|
|
16
|
+
function coerceInputArray(input) {
|
|
17
|
+
if (!Array.isArray(input)) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
return cloneDeep(input);
|
|
21
|
+
}
|
|
22
|
+
function coerceTools(tools) {
|
|
23
|
+
if (!Array.isArray(tools)) {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return cloneDeep(tools);
|
|
27
|
+
}
|
|
28
|
+
function pickPersistedFields(payload) {
|
|
29
|
+
const fields = [
|
|
30
|
+
'model',
|
|
31
|
+
'instructions',
|
|
32
|
+
'metadata',
|
|
33
|
+
'include',
|
|
34
|
+
'store',
|
|
35
|
+
'tool_choice',
|
|
36
|
+
'parallel_tool_calls',
|
|
37
|
+
'response_format',
|
|
38
|
+
'temperature',
|
|
39
|
+
'top_p',
|
|
40
|
+
'max_output_tokens',
|
|
41
|
+
'max_tokens',
|
|
42
|
+
'stop',
|
|
43
|
+
'user',
|
|
44
|
+
'modal',
|
|
45
|
+
'truncation_strategy',
|
|
46
|
+
'previous_response_id',
|
|
47
|
+
'reasoning',
|
|
48
|
+
'attachments',
|
|
49
|
+
'input_audio',
|
|
50
|
+
'output_audio'
|
|
51
|
+
];
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const key of fields) {
|
|
54
|
+
if (payload[key] !== undefined) {
|
|
55
|
+
result[key] = cloneDeep(payload[key]);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
function normalizeOutputItemToInput(item) {
|
|
61
|
+
const type = typeof item.type === 'string' ? item.type : '';
|
|
62
|
+
if (type === 'message' || type === 'reasoning') {
|
|
63
|
+
const role = typeof item.role === 'string' ? item.role : 'assistant';
|
|
64
|
+
const content = Array.isArray(item.content) ? cloneDeep(item.content) : (typeof item.text === 'string' ? [{ type: 'text', text: item.text }] : []);
|
|
65
|
+
return {
|
|
66
|
+
type: 'message',
|
|
67
|
+
role,
|
|
68
|
+
content,
|
|
69
|
+
message: { role, content }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (type === 'function_call') {
|
|
73
|
+
const callId = typeof item.call_id === 'string' ? item.call_id : (typeof item.id === 'string' ? item.id : undefined);
|
|
74
|
+
return {
|
|
75
|
+
type: 'function_call',
|
|
76
|
+
role: 'assistant',
|
|
77
|
+
id: typeof item.id === 'string' ? item.id : undefined,
|
|
78
|
+
call_id: callId,
|
|
79
|
+
name: typeof item.name === 'string' ? item.name : undefined,
|
|
80
|
+
arguments: item.arguments,
|
|
81
|
+
function: isRecord(item.function)
|
|
82
|
+
? cloneDeep(item.function)
|
|
83
|
+
: (typeof item.name === 'string'
|
|
84
|
+
? { name: item.name, arguments: item.arguments }
|
|
85
|
+
: undefined)
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function convertOutputToInputItems(response) {
|
|
91
|
+
const output = Array.isArray(response.output) ? response.output : [];
|
|
92
|
+
const items = [];
|
|
93
|
+
for (const entry of output) {
|
|
94
|
+
if (!entry || typeof entry !== 'object')
|
|
95
|
+
continue;
|
|
96
|
+
const mapped = normalizeOutputItemToInput(entry);
|
|
97
|
+
if (mapped)
|
|
98
|
+
items.push(mapped);
|
|
99
|
+
}
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
function normalizeSubmittedToolOutputs(toolOutputs) {
|
|
103
|
+
const items = [];
|
|
104
|
+
const submitted = [];
|
|
105
|
+
toolOutputs.forEach((entry, index) => {
|
|
106
|
+
if (!entry || typeof entry !== 'object')
|
|
107
|
+
return;
|
|
108
|
+
const rec = entry;
|
|
109
|
+
const rawId = typeof rec.tool_call_id === 'string'
|
|
110
|
+
? rec.tool_call_id
|
|
111
|
+
: typeof rec.call_id === 'string'
|
|
112
|
+
? rec.call_id
|
|
113
|
+
: typeof rec.id === 'string'
|
|
114
|
+
? rec.id
|
|
115
|
+
: undefined;
|
|
116
|
+
const trimmed = typeof rawId === 'string' ? rawId.trim() : '';
|
|
117
|
+
const callId = trimmed.length ? trimmed : `fc_resume_${index}`;
|
|
118
|
+
const outputValue = rec.output ?? null;
|
|
119
|
+
const normalizedOutput = typeof outputValue === 'string'
|
|
120
|
+
? outputValue
|
|
121
|
+
: (() => {
|
|
122
|
+
try {
|
|
123
|
+
return JSON.stringify(outputValue ?? null);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return String(outputValue ?? '');
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
items.push({
|
|
130
|
+
type: 'function_call_output',
|
|
131
|
+
id: callId,
|
|
132
|
+
call_id: callId,
|
|
133
|
+
output: normalizedOutput
|
|
134
|
+
});
|
|
135
|
+
submitted.push({ callId, originalId: rawId ?? callId, outputText: normalizedOutput });
|
|
136
|
+
});
|
|
137
|
+
return { items, submitted };
|
|
138
|
+
}
|
|
139
|
+
class BridgeConversationStore {
|
|
140
|
+
requestMap = new Map();
|
|
141
|
+
responseIndex = new Map();
|
|
142
|
+
captureRequestContext(args) {
|
|
143
|
+
const { requestId, payload, context } = args;
|
|
144
|
+
if (!requestId || !payload)
|
|
145
|
+
return;
|
|
146
|
+
this.prune();
|
|
147
|
+
const entry = {
|
|
148
|
+
requestId,
|
|
149
|
+
basePayload: pickPersistedFields(payload),
|
|
150
|
+
input: coerceInputArray(context.input),
|
|
151
|
+
tools: coerceTools(context.toolsRaw || (Array.isArray(payload.tools) ? payload.tools : undefined)),
|
|
152
|
+
createdAt: Date.now(),
|
|
153
|
+
updatedAt: Date.now()
|
|
154
|
+
};
|
|
155
|
+
if (typeof payload.model === 'string' && payload.model.trim()) {
|
|
156
|
+
entry.basePayload.model = payload.model;
|
|
157
|
+
}
|
|
158
|
+
if (typeof payload.stream === 'boolean') {
|
|
159
|
+
entry.basePayload.stream = payload.stream;
|
|
160
|
+
}
|
|
161
|
+
if (entry.tools) {
|
|
162
|
+
entry.basePayload.tools = entry.tools;
|
|
163
|
+
}
|
|
164
|
+
this.requestMap.set(requestId, entry);
|
|
165
|
+
}
|
|
166
|
+
recordResponse(args) {
|
|
167
|
+
const entry = this.requestMap.get(args.requestId);
|
|
168
|
+
if (!entry)
|
|
169
|
+
return;
|
|
170
|
+
const response = args.response;
|
|
171
|
+
const responseId = typeof response.id === 'string' ? response.id : undefined;
|
|
172
|
+
if (!responseId)
|
|
173
|
+
return;
|
|
174
|
+
const assistantBlocks = convertOutputToInputItems(response);
|
|
175
|
+
if (assistantBlocks.length) {
|
|
176
|
+
entry.input.push(...assistantBlocks);
|
|
177
|
+
}
|
|
178
|
+
entry.lastResponseId = responseId;
|
|
179
|
+
entry.updatedAt = Date.now();
|
|
180
|
+
this.responseIndex.set(responseId, entry);
|
|
181
|
+
}
|
|
182
|
+
resumeConversation(responseId, submitPayload, options) {
|
|
183
|
+
if (typeof responseId !== 'string' || !responseId.trim()) {
|
|
184
|
+
throw new Error('Bridge conversation requires valid response_id');
|
|
185
|
+
}
|
|
186
|
+
this.prune();
|
|
187
|
+
const entry = this.responseIndex.get(responseId);
|
|
188
|
+
if (!entry) {
|
|
189
|
+
throw new Error('Bridge conversation expired or not found');
|
|
190
|
+
}
|
|
191
|
+
const toolOutputs = Array.isArray(submitPayload.tool_outputs)
|
|
192
|
+
? submitPayload.tool_outputs
|
|
193
|
+
: [];
|
|
194
|
+
if (!toolOutputs.length) {
|
|
195
|
+
throw new Error('tool_outputs array is required when submitting tool results');
|
|
196
|
+
}
|
|
197
|
+
const mergedInput = coerceInputArray(entry.input);
|
|
198
|
+
const normalizedOutputs = normalizeSubmittedToolOutputs(toolOutputs);
|
|
199
|
+
mergedInput.push(...normalizedOutputs.items);
|
|
200
|
+
const payload = cloneDeep(entry.basePayload);
|
|
201
|
+
payload.input = mergedInput;
|
|
202
|
+
payload.stream = true;
|
|
203
|
+
payload.previous_response_id = responseId;
|
|
204
|
+
if (Array.isArray(entry.tools) && entry.tools.length) {
|
|
205
|
+
payload.tools = cloneDeep(entry.tools);
|
|
206
|
+
}
|
|
207
|
+
if (typeof submitPayload.model === 'string' && submitPayload.model.trim()) {
|
|
208
|
+
payload.model = submitPayload.model.trim();
|
|
209
|
+
}
|
|
210
|
+
if (submitPayload.metadata && isRecord(submitPayload.metadata)) {
|
|
211
|
+
payload.metadata = { ...payload.metadata, ...cloneDeep(submitPayload.metadata) };
|
|
212
|
+
}
|
|
213
|
+
delete payload.tool_outputs;
|
|
214
|
+
delete payload.response_id;
|
|
215
|
+
this.cleanupEntry(entry, responseId);
|
|
216
|
+
return {
|
|
217
|
+
payload,
|
|
218
|
+
meta: {
|
|
219
|
+
restoredFromResponseId: responseId,
|
|
220
|
+
previousRequestId: entry.requestId,
|
|
221
|
+
toolOutputs: toolOutputs.length,
|
|
222
|
+
toolOutputsDetailed: normalizedOutputs.submitted,
|
|
223
|
+
requestId: options?.requestId
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
clearRequest(requestId) {
|
|
228
|
+
const entry = this.requestMap.get(requestId);
|
|
229
|
+
if (!entry)
|
|
230
|
+
return;
|
|
231
|
+
this.requestMap.delete(requestId);
|
|
232
|
+
if (entry.lastResponseId) {
|
|
233
|
+
this.responseIndex.delete(entry.lastResponseId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
cleanupEntry(entry, responseId) {
|
|
237
|
+
this.responseIndex.delete(responseId);
|
|
238
|
+
this.requestMap.delete(entry.requestId);
|
|
239
|
+
}
|
|
240
|
+
prune() {
|
|
241
|
+
const now = Date.now();
|
|
242
|
+
for (const [requestId, entry] of this.requestMap.entries()) {
|
|
243
|
+
if (now - entry.updatedAt > TTL_MS) {
|
|
244
|
+
this.requestMap.delete(requestId);
|
|
245
|
+
if (entry.lastResponseId) {
|
|
246
|
+
this.responseIndex.delete(entry.lastResponseId);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
for (const [respId, entry] of this.responseIndex.entries()) {
|
|
251
|
+
if (!this.requestMap.has(entry.requestId)) {
|
|
252
|
+
this.responseIndex.delete(respId);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
export const bridgeConversationStore = new BridgeConversationStore();
|
|
258
|
+
export function captureBridgeRequestContext(args) {
|
|
259
|
+
try {
|
|
260
|
+
bridgeConversationStore.captureRequestContext(args);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
/* ignore capture failures */
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
export function recordBridgeResponse(args) {
|
|
267
|
+
try {
|
|
268
|
+
bridgeConversationStore.recordResponse(args);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
/* ignore */
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
export function resumeBridgeConversation(responseId, submitPayload, options) {
|
|
275
|
+
return bridgeConversationStore.resumeConversation(responseId, submitPayload, options);
|
|
276
|
+
}
|
|
277
|
+
export function clearBridgeConversationByRequestId(requestId) {
|
|
278
|
+
bridgeConversationStore.clearRequest(requestId);
|
|
279
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
function sanitizeCore(value) {
|
|
2
|
+
return value
|
|
3
|
+
.replace(/[^A-Za-z0-9_-]/g, '_')
|
|
4
|
+
.replace(/_{2,}/g, '_')
|
|
5
|
+
.replace(/^_+/, '')
|
|
6
|
+
.replace(/_+$/, '');
|
|
7
|
+
}
|
|
8
|
+
function stripPrefix(value) {
|
|
9
|
+
if (typeof value !== 'string')
|
|
10
|
+
return null;
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
if (!trimmed)
|
|
13
|
+
return null;
|
|
14
|
+
let sanitized = sanitizeCore(trimmed);
|
|
15
|
+
if (!sanitized)
|
|
16
|
+
return null;
|
|
17
|
+
if (/^fc[_-]/i.test(sanitized)) {
|
|
18
|
+
sanitized = sanitized.replace(/^fc[_-]?/i, '');
|
|
19
|
+
}
|
|
20
|
+
else if (/^call[_-]/i.test(sanitized)) {
|
|
21
|
+
sanitized = sanitized.replace(/^call[_-]?/i, '');
|
|
22
|
+
}
|
|
23
|
+
if (!sanitized) {
|
|
24
|
+
sanitized = Math.random().toString(36).slice(2, 10);
|
|
25
|
+
}
|
|
26
|
+
return `fc_${sanitized}`;
|
|
27
|
+
}
|
|
28
|
+
function normalizeWithFallback(options) {
|
|
29
|
+
const normalized = stripPrefix(options.callId);
|
|
30
|
+
if (normalized)
|
|
31
|
+
return normalized;
|
|
32
|
+
const fallbackNormalized = stripPrefix(options.fallback);
|
|
33
|
+
if (fallbackNormalized)
|
|
34
|
+
return fallbackNormalized;
|
|
35
|
+
return `fc_${Math.random().toString(36).slice(2, 10)}`;
|
|
36
|
+
}
|
|
37
|
+
export function normalizeFunctionCallId(options) {
|
|
38
|
+
return normalizeWithFallback(options);
|
|
39
|
+
}
|
|
40
|
+
export function normalizeFunctionCallOutputId(options) {
|
|
41
|
+
return normalizeWithFallback(options);
|
|
42
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ensureBridgeInstructions(payload: Record<string, unknown>): string | undefined;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
function collectTextFromBlocks(blocks) {
|
|
2
|
+
const parts = [];
|
|
3
|
+
for (const block of blocks) {
|
|
4
|
+
if (!block || typeof block !== 'object')
|
|
5
|
+
continue;
|
|
6
|
+
const type = typeof block.type === 'string' ? block.type.toLowerCase() : '';
|
|
7
|
+
if ((type === 'input_text' || type === 'output_text' || type === 'text' || type === 'commentary') && typeof block.text === 'string') {
|
|
8
|
+
const text = block.text.trim();
|
|
9
|
+
if (text)
|
|
10
|
+
parts.push(text);
|
|
11
|
+
continue;
|
|
12
|
+
}
|
|
13
|
+
if (type === 'message' && Array.isArray(block.content)) {
|
|
14
|
+
const nested = collectTextFromBlocks(block.content);
|
|
15
|
+
if (nested)
|
|
16
|
+
parts.push(nested);
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (typeof block.content === 'string') {
|
|
20
|
+
const text = block.content.trim();
|
|
21
|
+
if (text)
|
|
22
|
+
parts.push(text);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (Array.isArray(block.content)) {
|
|
26
|
+
const nested = collectTextFromBlocks(block.content);
|
|
27
|
+
if (nested)
|
|
28
|
+
parts.push(nested);
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (typeof block.text === 'string') {
|
|
32
|
+
const text = block.text.trim();
|
|
33
|
+
if (text)
|
|
34
|
+
parts.push(text);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return parts.join('\n');
|
|
38
|
+
}
|
|
39
|
+
function extractSystemInstruction(entry) {
|
|
40
|
+
if (!entry || typeof entry !== 'object')
|
|
41
|
+
return null;
|
|
42
|
+
const roleSource = entry.role ?? entry.message?.role;
|
|
43
|
+
const role = typeof roleSource === 'string' ? roleSource.toLowerCase() : '';
|
|
44
|
+
if (role !== 'system')
|
|
45
|
+
return null;
|
|
46
|
+
const collected = [];
|
|
47
|
+
if (Array.isArray(entry.content)) {
|
|
48
|
+
const text = collectTextFromBlocks(entry.content);
|
|
49
|
+
if (text.trim())
|
|
50
|
+
collected.push(text.trim());
|
|
51
|
+
}
|
|
52
|
+
else if (typeof entry.content === 'string') {
|
|
53
|
+
const text = entry.content.trim();
|
|
54
|
+
if (text)
|
|
55
|
+
collected.push(text);
|
|
56
|
+
}
|
|
57
|
+
if (typeof entry.text === 'string') {
|
|
58
|
+
const text = entry.text.trim();
|
|
59
|
+
if (text)
|
|
60
|
+
collected.push(text);
|
|
61
|
+
}
|
|
62
|
+
const message = entry.message;
|
|
63
|
+
if (message && typeof message === 'object') {
|
|
64
|
+
if (Array.isArray(message.content)) {
|
|
65
|
+
const text = collectTextFromBlocks(message.content);
|
|
66
|
+
if (text.trim())
|
|
67
|
+
collected.push(text.trim());
|
|
68
|
+
}
|
|
69
|
+
else if (typeof message.content === 'string') {
|
|
70
|
+
const text = message.content.trim();
|
|
71
|
+
if (text)
|
|
72
|
+
collected.push(text);
|
|
73
|
+
}
|
|
74
|
+
if (typeof message.text === 'string') {
|
|
75
|
+
const text = String(message.text).trim();
|
|
76
|
+
if (text)
|
|
77
|
+
collected.push(text);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const merged = collected.join('\n').trim();
|
|
81
|
+
return merged ? merged : null;
|
|
82
|
+
}
|
|
83
|
+
export function ensureBridgeInstructions(payload) {
|
|
84
|
+
let instructions = typeof payload.instructions === 'string' ? payload.instructions : '';
|
|
85
|
+
const hasClientInstruction = typeof payload.instructions === 'string' && payload.instructions.length > 0;
|
|
86
|
+
const input = Array.isArray(payload.input) ? payload.input : undefined;
|
|
87
|
+
if (input && input.length) {
|
|
88
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
89
|
+
const entry = input[i];
|
|
90
|
+
const roleSource = entry?.role ?? entry?.message?.role;
|
|
91
|
+
const role = typeof roleSource === 'string' ? roleSource.toLowerCase() : '';
|
|
92
|
+
if (role === 'system') {
|
|
93
|
+
const text = extractSystemInstruction(entry);
|
|
94
|
+
input.splice(i, 1);
|
|
95
|
+
i -= 1;
|
|
96
|
+
if (text && text.trim()) {
|
|
97
|
+
if (!hasClientInstruction && !instructions) {
|
|
98
|
+
instructions = text.trim();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (instructions) {
|
|
106
|
+
payload.instructions = instructions;
|
|
107
|
+
return instructions;
|
|
108
|
+
}
|
|
109
|
+
if (typeof payload.instructions !== 'undefined') {
|
|
110
|
+
delete payload.instructions;
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export type BridgeContentPart = {
|
|
2
|
+
type: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
content?: unknown;
|
|
5
|
+
};
|
|
6
|
+
export type BridgeInputItem = {
|
|
7
|
+
type: string;
|
|
8
|
+
role?: string;
|
|
9
|
+
content?: Array<BridgeContentPart> | null;
|
|
10
|
+
name?: string;
|
|
11
|
+
arguments?: unknown;
|
|
12
|
+
call_id?: string;
|
|
13
|
+
output?: unknown;
|
|
14
|
+
function?: {
|
|
15
|
+
name?: string;
|
|
16
|
+
arguments?: unknown;
|
|
17
|
+
};
|
|
18
|
+
message?: {
|
|
19
|
+
role?: string;
|
|
20
|
+
content?: Array<BridgeContentPart>;
|
|
21
|
+
};
|
|
22
|
+
id?: string;
|
|
23
|
+
tool_call_id?: string;
|
|
24
|
+
tool_use_id?: string;
|
|
25
|
+
text?: string;
|
|
26
|
+
};
|
|
27
|
+
export type BridgeToolDefinition = {
|
|
28
|
+
type: string;
|
|
29
|
+
name?: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
strict?: boolean;
|
|
32
|
+
parameters?: unknown;
|
|
33
|
+
function?: {
|
|
34
|
+
name?: string;
|
|
35
|
+
description?: string;
|
|
36
|
+
strict?: boolean;
|
|
37
|
+
parameters?: unknown;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BridgeInputItem } from './bridge-message-types.js';
|
|
2
|
+
export declare function coerceBridgeRole(role: unknown): string;
|
|
3
|
+
export declare function serializeToolArguments(argsStringOrObj: unknown, _functionName: string | undefined, _tools: unknown): string;
|
|
4
|
+
export declare function serializeToolOutput(entry: BridgeInputItem): string | null;
|
|
5
|
+
export interface BridgeInputBuildOptions {
|
|
6
|
+
messages: Array<Record<string, unknown>>;
|
|
7
|
+
tools?: Array<Record<string, unknown>> | undefined;
|
|
8
|
+
}
|
|
9
|
+
export interface BridgeInputBuildResult {
|
|
10
|
+
input: BridgeInputItem[];
|
|
11
|
+
combinedSystemInstruction?: string;
|
|
12
|
+
latestUserInstruction?: string;
|
|
13
|
+
originalSystemMessages: string[];
|
|
14
|
+
}
|
|
15
|
+
export declare function convertMessagesToBridgeInput(options: BridgeInputBuildOptions): BridgeInputBuildResult;
|
|
16
|
+
export interface BridgeInputToChatOptions {
|
|
17
|
+
input?: BridgeInputItem[];
|
|
18
|
+
tools?: Array<Record<string, unknown>>;
|
|
19
|
+
normalizeFunctionName?: (raw: unknown) => string | undefined;
|
|
20
|
+
toolResultFallbackText?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare function convertBridgeInputToChatMessages(options: BridgeInputToChatOptions): Array<Record<string, unknown>>;
|