llm-messages 0.1.0 → 0.3.0
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/CHANGELOG.md +28 -0
- package/README.md +48 -4
- package/dist/index.cjs +242 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +82 -6
- package/dist/index.d.ts +82 -6
- package/dist/index.js +236 -16
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,34 @@ All notable changes to this project are documented here. The format is based on
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres
|
|
5
5
|
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [0.3.0] - 2026-06-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Response normalization. `responseFromOpenAI`, `responseFromAnthropic`,
|
|
12
|
+
`responseFromGemini` and `normalizeResponse(body, { from })` parse a provider
|
|
13
|
+
response body into a canonical OpenAI assistant message plus a neutral
|
|
14
|
+
`finishReason` and `usage` (`inputTokens` / `outputTokens`). Tool-call arguments
|
|
15
|
+
are serialized to JSON strings; `finishReason` becomes `tool_calls` whenever a
|
|
16
|
+
tool was called (including Gemini, which reports `STOP`); Gemini tool calls
|
|
17
|
+
without an id get a deterministic one. (#4)
|
|
18
|
+
|
|
19
|
+
## [0.2.0] - 2026-06-01
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Multimodal image support. Image parts convert across all three providers:
|
|
24
|
+
OpenAI `image_url` (data URL or remote), Anthropic `image` blocks (base64 or
|
|
25
|
+
url source), and Gemini `inlineData` (base64) or `fileData` (remote). Base64
|
|
26
|
+
data URLs round trip losslessly.
|
|
27
|
+
- `parseDataUrl` / `toDataUrl` helpers and a `NormalizedImage` type.
|
|
28
|
+
|
|
29
|
+
### Notes
|
|
30
|
+
|
|
31
|
+
- A remote image URL sent to Gemini is emitted as `fileData.fileUri` and flagged
|
|
32
|
+
with a `gemini-url-image` warning, since Gemini may require the Files API for
|
|
33
|
+
non-Google URIs.
|
|
34
|
+
|
|
7
35
|
## [0.1.0] - 2026-06-01
|
|
8
36
|
|
|
9
37
|
### Added
|
package/README.md
CHANGED
|
@@ -102,6 +102,27 @@ toGemini(messages, {
|
|
|
102
102
|
Warning codes: `generated-id`, `unmapped-tool-result`, `merged-role`,
|
|
103
103
|
`dropped-content`, `invalid-json-arguments`, `system-midstream`.
|
|
104
104
|
|
|
105
|
+
## Reading responses
|
|
106
|
+
|
|
107
|
+
The same idea applies to the read side. Normalize a provider's response body into
|
|
108
|
+
a canonical OpenAI assistant message, plus a neutral finish reason and token usage:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { responseFromAnthropic, normalizeResponse } from 'llm-messages';
|
|
112
|
+
|
|
113
|
+
const { message, finishReason, usage } = responseFromAnthropic(anthropicResponseBody);
|
|
114
|
+
// message -> { role: 'assistant', content, tool_calls? } (tool input re-serialized to a JSON string)
|
|
115
|
+
// finishReason -> 'stop' | 'tool_calls' | 'length' | 'content_filter' | 'unknown'
|
|
116
|
+
// usage -> { inputTokens, outputTokens }
|
|
117
|
+
|
|
118
|
+
// Or dispatch by provider:
|
|
119
|
+
normalizeResponse(geminiResponseBody, { from: 'gemini' });
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`finishReason` is normalized to `tool_calls` whenever the model called a tool, even
|
|
123
|
+
for Gemini (which reports `STOP`). Gemini tool calls without an id get a
|
|
124
|
+
deterministic one.
|
|
125
|
+
|
|
105
126
|
## Format cheatsheet
|
|
106
127
|
|
|
107
128
|
| | OpenAI | Anthropic | Gemini |
|
|
@@ -114,12 +135,35 @@ Warning codes: `generated-id`, `unmapped-tool-result`, `merged-role`,
|
|
|
114
135
|
| Match key | `tool_call_id` | `tool_use_id` | function `name` (id optional) |
|
|
115
136
|
| Role alternation | not required | strict | strict |
|
|
116
137
|
|
|
138
|
+
## Images
|
|
139
|
+
|
|
140
|
+
Image parts convert across all three providers:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const messages = [
|
|
144
|
+
{
|
|
145
|
+
role: 'user',
|
|
146
|
+
content: [
|
|
147
|
+
{ type: 'text', text: 'What is in this image?' },
|
|
148
|
+
{ type: 'image_url', image_url: { url: 'data:image/png;base64,iVBORw0KGgo...' } },
|
|
149
|
+
],
|
|
150
|
+
},
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
toAnthropic(messages); // -> { type: 'image', source: { type: 'base64', media_type: 'image/png', data: '...' } }
|
|
154
|
+
toGemini(messages); // -> { inlineData: { mimeType: 'image/png', data: '...' } }
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Base64 data URLs round trip losslessly. A remote `https` URL maps to an Anthropic
|
|
158
|
+
`url` source; for Gemini it is emitted as `fileData.fileUri` with a
|
|
159
|
+
`gemini-url-image` warning, since Gemini may require the Files API for non-Google
|
|
160
|
+
URIs.
|
|
161
|
+
|
|
117
162
|
## Scope
|
|
118
163
|
|
|
119
|
-
Version 0.x covers text, system prompts,
|
|
120
|
-
core of every agent loop.
|
|
121
|
-
where possible and reported as `dropped-content` otherwise
|
|
122
|
-
multimodal mapping is on the roadmap.
|
|
164
|
+
Version 0.x covers text, system prompts, tool calls/results and images, which is
|
|
165
|
+
the core of every agent loop. Other modalities (audio, files) are passed through
|
|
166
|
+
where possible and reported as `dropped-content` otherwise.
|
|
123
167
|
|
|
124
168
|
## Part of a set
|
|
125
169
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,7 +23,13 @@ __export(index_exports, {
|
|
|
23
23
|
convert: () => convert,
|
|
24
24
|
fromAnthropic: () => fromAnthropic,
|
|
25
25
|
fromGemini: () => fromGemini,
|
|
26
|
+
normalizeResponse: () => normalizeResponse,
|
|
27
|
+
parseDataUrl: () => parseDataUrl,
|
|
28
|
+
responseFromAnthropic: () => responseFromAnthropic,
|
|
29
|
+
responseFromGemini: () => responseFromGemini,
|
|
30
|
+
responseFromOpenAI: () => responseFromOpenAI,
|
|
26
31
|
toAnthropic: () => toAnthropic,
|
|
32
|
+
toDataUrl: () => toDataUrl,
|
|
27
33
|
toGemini: () => toGemini
|
|
28
34
|
});
|
|
29
35
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -81,6 +87,68 @@ function unwrapResponse(response) {
|
|
|
81
87
|
return JSON.stringify(response);
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
// src/image.ts
|
|
91
|
+
var DATA_URL = /^data:([^;,]+);base64,(.*)$/s;
|
|
92
|
+
function parseDataUrl(url) {
|
|
93
|
+
const match = DATA_URL.exec(url);
|
|
94
|
+
if (!match) return null;
|
|
95
|
+
return { mediaType: match[1], data: match[2] };
|
|
96
|
+
}
|
|
97
|
+
function toDataUrl(mediaType, data) {
|
|
98
|
+
return `data:${mediaType};base64,${data}`;
|
|
99
|
+
}
|
|
100
|
+
function imageFromOpenAI(part) {
|
|
101
|
+
if (!isRecord(part) || part.type !== "image_url" || !isRecord(part.image_url)) return null;
|
|
102
|
+
const url = part.image_url.url;
|
|
103
|
+
if (typeof url !== "string") return null;
|
|
104
|
+
const parsed = parseDataUrl(url);
|
|
105
|
+
return parsed ? { kind: "base64", ...parsed } : { kind: "url", url };
|
|
106
|
+
}
|
|
107
|
+
function imageToOpenAI(image) {
|
|
108
|
+
const url = image.kind === "base64" ? toDataUrl(image.mediaType, image.data) : image.url;
|
|
109
|
+
return { type: "image_url", image_url: { url } };
|
|
110
|
+
}
|
|
111
|
+
function imageFromAnthropic(block) {
|
|
112
|
+
if (!isRecord(block) || block.type !== "image" || !isRecord(block.source)) return null;
|
|
113
|
+
const source = block.source;
|
|
114
|
+
if (source.type === "base64" && typeof source.media_type === "string" && typeof source.data === "string") {
|
|
115
|
+
return { kind: "base64", mediaType: source.media_type, data: source.data };
|
|
116
|
+
}
|
|
117
|
+
if (source.type === "url" && typeof source.url === "string") {
|
|
118
|
+
return { kind: "url", url: source.url };
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function imageToAnthropic(image) {
|
|
123
|
+
if (image.kind === "base64") {
|
|
124
|
+
return { type: "image", source: { type: "base64", media_type: image.mediaType, data: image.data } };
|
|
125
|
+
}
|
|
126
|
+
return { type: "image", source: { type: "url", url: image.url } };
|
|
127
|
+
}
|
|
128
|
+
function imageFromGemini(part) {
|
|
129
|
+
if (isRecord(part) && isRecord(part.inlineData)) {
|
|
130
|
+
const data = part.inlineData;
|
|
131
|
+
if (typeof data.mimeType === "string" && typeof data.data === "string") {
|
|
132
|
+
return { kind: "base64", mediaType: data.mimeType, data: data.data };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (isRecord(part) && isRecord(part.fileData)) {
|
|
136
|
+
const data = part.fileData;
|
|
137
|
+
if (typeof data.fileUri === "string") return { kind: "url", url: data.fileUri };
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function imageToGemini(image, reporter) {
|
|
142
|
+
if (image.kind === "base64") {
|
|
143
|
+
return { inlineData: { mimeType: image.mediaType, data: image.data } };
|
|
144
|
+
}
|
|
145
|
+
reporter.warn(
|
|
146
|
+
"gemini-url-image",
|
|
147
|
+
"A remote image URL was emitted as Gemini fileData.fileUri; Gemini may require the Files API for non-Google URIs."
|
|
148
|
+
);
|
|
149
|
+
return { fileData: { fileUri: image.url } };
|
|
150
|
+
}
|
|
151
|
+
|
|
84
152
|
// src/providers/openai.ts
|
|
85
153
|
function isSystem(message) {
|
|
86
154
|
return message.role === "system" || message.role === "developer";
|
|
@@ -142,9 +210,14 @@ function userContent(content, reporter) {
|
|
|
142
210
|
for (const part of content) {
|
|
143
211
|
if (isRecord(part) && part.type === "text" && typeof part.text === "string") {
|
|
144
212
|
blocks.push({ type: "text", text: part.text });
|
|
145
|
-
|
|
146
|
-
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const image = imageFromOpenAI(part);
|
|
216
|
+
if (image) {
|
|
217
|
+
blocks.push(imageToAnthropic(image));
|
|
218
|
+
continue;
|
|
147
219
|
}
|
|
220
|
+
reporter.warn("dropped-content", "Dropped an unsupported user content part.");
|
|
148
221
|
}
|
|
149
222
|
return blocks;
|
|
150
223
|
}
|
|
@@ -194,7 +267,7 @@ function fromAnthropic(conversation, options = {}) {
|
|
|
194
267
|
const blocks = asBlocks(message.content);
|
|
195
268
|
if (message.role === "user") {
|
|
196
269
|
const toolResults = blocks.filter((b) => b.type === "tool_result");
|
|
197
|
-
const
|
|
270
|
+
const contentBlocks = blocks.filter((b) => b.type !== "tool_result");
|
|
198
271
|
for (const block of toolResults) {
|
|
199
272
|
out.push({
|
|
200
273
|
role: "tool",
|
|
@@ -202,8 +275,8 @@ function fromAnthropic(conversation, options = {}) {
|
|
|
202
275
|
content: textOf(block.content)
|
|
203
276
|
});
|
|
204
277
|
}
|
|
205
|
-
if (
|
|
206
|
-
out.push({ role: "user", content:
|
|
278
|
+
if (contentBlocks.length > 0) {
|
|
279
|
+
out.push({ role: "user", content: userContentToOpenAI(contentBlocks) });
|
|
207
280
|
}
|
|
208
281
|
continue;
|
|
209
282
|
}
|
|
@@ -220,6 +293,20 @@ function fromAnthropic(conversation, options = {}) {
|
|
|
220
293
|
}
|
|
221
294
|
return out;
|
|
222
295
|
}
|
|
296
|
+
function userContentToOpenAI(blocks) {
|
|
297
|
+
const hasImage = blocks.some((block) => imageFromAnthropic(block) !== null);
|
|
298
|
+
if (!hasImage) return textOf(blocks);
|
|
299
|
+
const parts = [];
|
|
300
|
+
for (const block of blocks) {
|
|
301
|
+
const image = imageFromAnthropic(block);
|
|
302
|
+
if (image) {
|
|
303
|
+
parts.push(imageToOpenAI(image));
|
|
304
|
+
} else if (isRecord(block) && block.type === "text" && typeof block.text === "string") {
|
|
305
|
+
parts.push({ type: "text", text: block.text });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return parts;
|
|
309
|
+
}
|
|
223
310
|
|
|
224
311
|
// src/providers/gemini.ts
|
|
225
312
|
function toGemini(messages, options = {}) {
|
|
@@ -274,9 +361,14 @@ function userParts(content, reporter) {
|
|
|
274
361
|
for (const part of content) {
|
|
275
362
|
if (isRecord(part) && part.type === "text" && typeof part.text === "string") {
|
|
276
363
|
parts.push({ text: part.text });
|
|
277
|
-
|
|
278
|
-
reporter.warn("dropped-content", "Dropped a non-text user content part not supported by this converter.");
|
|
364
|
+
continue;
|
|
279
365
|
}
|
|
366
|
+
const image = imageFromOpenAI(part);
|
|
367
|
+
if (image) {
|
|
368
|
+
parts.push(imageToGemini(image, reporter));
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
reporter.warn("dropped-content", "Dropped an unsupported user content part.");
|
|
280
372
|
}
|
|
281
373
|
return parts.length > 0 ? parts : [{ text: "" }];
|
|
282
374
|
}
|
|
@@ -321,7 +413,7 @@ function fromGemini(conversation, options = {}) {
|
|
|
321
413
|
for (const content of conversation.contents) {
|
|
322
414
|
const parts = Array.isArray(content.parts) ? content.parts : [];
|
|
323
415
|
if (content.role === "model") {
|
|
324
|
-
const
|
|
416
|
+
const textPieces = [];
|
|
325
417
|
const toolCalls = [];
|
|
326
418
|
for (const part of parts) {
|
|
327
419
|
if (isRecord(part) && isRecord(part.functionCall)) {
|
|
@@ -335,27 +427,42 @@ function fromGemini(conversation, options = {}) {
|
|
|
335
427
|
});
|
|
336
428
|
pending.push({ id, name: fc.name });
|
|
337
429
|
} else if (isRecord(part) && typeof part.text === "string") {
|
|
338
|
-
|
|
430
|
+
textPieces.push(part.text);
|
|
339
431
|
}
|
|
340
432
|
}
|
|
341
|
-
const
|
|
342
|
-
const assistant = { role: "assistant", content:
|
|
433
|
+
const text = textPieces.join("");
|
|
434
|
+
const assistant = { role: "assistant", content: text || null };
|
|
343
435
|
if (toolCalls.length > 0) assistant.tool_calls = toolCalls;
|
|
344
436
|
out.push(assistant);
|
|
345
437
|
continue;
|
|
346
438
|
}
|
|
347
|
-
const
|
|
439
|
+
const contentParts = [];
|
|
440
|
+
let hasImage = false;
|
|
348
441
|
for (const part of parts) {
|
|
349
442
|
if (isRecord(part) && isRecord(part.functionResponse)) {
|
|
350
443
|
const fr = part.functionResponse;
|
|
351
444
|
const id = resolveResponseId(fr, pending, reporter, generateId);
|
|
352
445
|
out.push({ role: "tool", tool_call_id: id, content: unwrapResponse(fr.response ?? {}) });
|
|
353
|
-
|
|
354
|
-
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const image = imageFromGemini(part);
|
|
449
|
+
if (image) {
|
|
450
|
+
contentParts.push(imageToOpenAI(image));
|
|
451
|
+
hasImage = true;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (isRecord(part) && typeof part.text === "string") {
|
|
455
|
+
contentParts.push({ type: "text", text: part.text });
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (contentParts.length > 0) {
|
|
459
|
+
if (hasImage) {
|
|
460
|
+
out.push({ role: "user", content: contentParts });
|
|
461
|
+
} else {
|
|
462
|
+
const text = textOf(contentParts);
|
|
463
|
+
if (text) out.push({ role: "user", content: text });
|
|
355
464
|
}
|
|
356
465
|
}
|
|
357
|
-
const text = textPieces.join("");
|
|
358
|
-
if (text) out.push({ role: "user", content: text });
|
|
359
466
|
}
|
|
360
467
|
return out;
|
|
361
468
|
}
|
|
@@ -408,12 +515,131 @@ function fromCanonical(canonical, to, options) {
|
|
|
408
515
|
throw new Error(`Unknown target provider: ${String(to)}`);
|
|
409
516
|
}
|
|
410
517
|
}
|
|
518
|
+
|
|
519
|
+
// src/response.ts
|
|
520
|
+
var num = (value) => typeof value === "number" ? value : 0;
|
|
521
|
+
function buildMessage(text, toolCalls) {
|
|
522
|
+
const message = { role: "assistant", content: text ? text : null };
|
|
523
|
+
if (toolCalls.length > 0) message.tool_calls = toolCalls;
|
|
524
|
+
return message;
|
|
525
|
+
}
|
|
526
|
+
function finalReason(mapped, toolCalls) {
|
|
527
|
+
return toolCalls.length > 0 ? "tool_calls" : mapped;
|
|
528
|
+
}
|
|
529
|
+
var OPENAI_FINISH = {
|
|
530
|
+
stop: "stop",
|
|
531
|
+
length: "length",
|
|
532
|
+
tool_calls: "tool_calls",
|
|
533
|
+
content_filter: "content_filter",
|
|
534
|
+
function_call: "tool_calls"
|
|
535
|
+
};
|
|
536
|
+
function responseFromOpenAI(body) {
|
|
537
|
+
const root = isRecord(body) ? body : {};
|
|
538
|
+
const choice = Array.isArray(root.choices) && isRecord(root.choices[0]) ? root.choices[0] : {};
|
|
539
|
+
const message = isRecord(choice.message) ? choice.message : {};
|
|
540
|
+
const text = typeof message.content === "string" ? message.content : textOf(message.content);
|
|
541
|
+
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
542
|
+
const usage = isRecord(root.usage) ? root.usage : {};
|
|
543
|
+
return {
|
|
544
|
+
message: buildMessage(text, toolCalls),
|
|
545
|
+
finishReason: finalReason(OPENAI_FINISH[String(choice.finish_reason)] ?? "unknown", toolCalls),
|
|
546
|
+
usage: { inputTokens: num(usage.prompt_tokens), outputTokens: num(usage.completion_tokens) }
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
var ANTHROPIC_FINISH = {
|
|
550
|
+
end_turn: "stop",
|
|
551
|
+
stop_sequence: "stop",
|
|
552
|
+
tool_use: "tool_calls",
|
|
553
|
+
max_tokens: "length",
|
|
554
|
+
refusal: "content_filter",
|
|
555
|
+
pause_turn: "unknown"
|
|
556
|
+
};
|
|
557
|
+
function responseFromAnthropic(body) {
|
|
558
|
+
const root = isRecord(body) ? body : {};
|
|
559
|
+
const blocks = Array.isArray(root.content) ? root.content : [];
|
|
560
|
+
const textPieces = [];
|
|
561
|
+
const toolCalls = [];
|
|
562
|
+
for (const block of blocks) {
|
|
563
|
+
if (!isRecord(block)) continue;
|
|
564
|
+
if (block.type === "text" && typeof block.text === "string") {
|
|
565
|
+
textPieces.push(block.text);
|
|
566
|
+
} else if (block.type === "tool_use" && typeof block.name === "string") {
|
|
567
|
+
toolCalls.push({
|
|
568
|
+
id: typeof block.id === "string" ? block.id : "",
|
|
569
|
+
type: "function",
|
|
570
|
+
function: { name: block.name, arguments: JSON.stringify(block.input ?? {}) }
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
const usage = isRecord(root.usage) ? root.usage : {};
|
|
575
|
+
return {
|
|
576
|
+
message: buildMessage(textPieces.join(""), toolCalls),
|
|
577
|
+
finishReason: finalReason(ANTHROPIC_FINISH[String(root.stop_reason)] ?? "unknown", toolCalls),
|
|
578
|
+
usage: { inputTokens: num(usage.input_tokens), outputTokens: num(usage.output_tokens) }
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
var GEMINI_FINISH = {
|
|
582
|
+
STOP: "stop",
|
|
583
|
+
MAX_TOKENS: "length",
|
|
584
|
+
SAFETY: "content_filter",
|
|
585
|
+
RECITATION: "content_filter",
|
|
586
|
+
MALFORMED_FUNCTION_CALL: "content_filter"
|
|
587
|
+
};
|
|
588
|
+
function responseFromGemini(body, options = {}) {
|
|
589
|
+
const reporter = new Reporter(options);
|
|
590
|
+
const root = isRecord(body) ? body : {};
|
|
591
|
+
const candidate = Array.isArray(root.candidates) && isRecord(root.candidates[0]) ? root.candidates[0] : {};
|
|
592
|
+
const content = isRecord(candidate.content) ? candidate.content : {};
|
|
593
|
+
const parts = Array.isArray(content.parts) ? content.parts : [];
|
|
594
|
+
const textPieces = [];
|
|
595
|
+
const toolCalls = [];
|
|
596
|
+
let counter = 0;
|
|
597
|
+
for (const part of parts) {
|
|
598
|
+
if (!isRecord(part)) continue;
|
|
599
|
+
if (isRecord(part.functionCall)) {
|
|
600
|
+
const call = part.functionCall;
|
|
601
|
+
const name = typeof call.name === "string" ? call.name : "function";
|
|
602
|
+
let id = call.id;
|
|
603
|
+
if (!id) {
|
|
604
|
+
id = `call_${name.replace(/[^a-zA-Z0-9_-]/g, "_")}_${counter++}`;
|
|
605
|
+
reporter.warn("generated-id", `Gemini functionCall '${name}' had no id; generated '${id}'.`);
|
|
606
|
+
}
|
|
607
|
+
toolCalls.push({ id, type: "function", function: { name, arguments: JSON.stringify(call.args ?? {}) } });
|
|
608
|
+
} else if (typeof part.text === "string") {
|
|
609
|
+
textPieces.push(part.text);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const usage = isRecord(root.usageMetadata) ? root.usageMetadata : {};
|
|
613
|
+
return {
|
|
614
|
+
message: buildMessage(textPieces.join(""), toolCalls),
|
|
615
|
+
finishReason: finalReason(GEMINI_FINISH[String(candidate.finishReason)] ?? "unknown", toolCalls),
|
|
616
|
+
usage: { inputTokens: num(usage.promptTokenCount), outputTokens: num(usage.candidatesTokenCount) }
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
function normalizeResponse(body, route, options = {}) {
|
|
620
|
+
switch (route.from) {
|
|
621
|
+
case "openai":
|
|
622
|
+
return responseFromOpenAI(body);
|
|
623
|
+
case "anthropic":
|
|
624
|
+
return responseFromAnthropic(body);
|
|
625
|
+
case "gemini":
|
|
626
|
+
return responseFromGemini(body, options);
|
|
627
|
+
default:
|
|
628
|
+
throw new Error(`Unknown source provider: ${String(route.from)}`);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
411
631
|
// Annotate the CommonJS export names for ESM import in node:
|
|
412
632
|
0 && (module.exports = {
|
|
413
633
|
convert,
|
|
414
634
|
fromAnthropic,
|
|
415
635
|
fromGemini,
|
|
636
|
+
normalizeResponse,
|
|
637
|
+
parseDataUrl,
|
|
638
|
+
responseFromAnthropic,
|
|
639
|
+
responseFromGemini,
|
|
640
|
+
responseFromOpenAI,
|
|
416
641
|
toAnthropic,
|
|
642
|
+
toDataUrl,
|
|
417
643
|
toGemini
|
|
418
644
|
});
|
|
419
645
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/util.ts","../src/providers/openai.ts","../src/providers/anthropic.ts","../src/providers/gemini.ts","../src/convert.ts"],"sourcesContent":["export { toAnthropic, fromAnthropic } from './providers/anthropic.js';\nexport { toGemini, fromGemini } from './providers/gemini.js';\nexport { convert } from './convert.js';\nexport type { ConversationOf } from './convert.js';\n\nexport type {\n Provider,\n Warning,\n WarningCode,\n ConvertOptions,\n OpenAIMessage,\n OpenAISystemMessage,\n OpenAIUserMessage,\n OpenAIAssistantMessage,\n OpenAIToolMessage,\n OpenAIToolCall,\n OpenAIContentPart,\n OpenAITextPart,\n AnthropicConversation,\n AnthropicMessage,\n AnthropicContentBlock,\n AnthropicTextBlock,\n AnthropicToolUseBlock,\n AnthropicToolResultBlock,\n GeminiConversation,\n GeminiContent,\n GeminiPart,\n GeminiTextPart,\n GeminiFunctionCallPart,\n GeminiFunctionResponsePart,\n} from './types.js';\n","import type { ConvertOptions, OpenAITextPart, Warning, WarningCode } from './types.js';\n\n/** Thin wrapper around the optional `onWarning` callback. */\nexport class Reporter {\n constructor(private readonly options: ConvertOptions = {}) {}\n\n warn(code: WarningCode, message: string): void {\n this.options.onWarning?.({ code, message } satisfies Warning);\n }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Flattens any supported text content (a plain string or an array of text\n * parts/blocks) into a single string. Non text parts are ignored.\n */\nexport function textOf(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (isRecord(part) && typeof part.text === 'string') return part.text;\n return '';\n })\n .join('');\n }\n return '';\n}\n\n/** Wraps a string as a single OpenAI/Anthropic text part array. */\nexport function textPart(text: string): OpenAITextPart[] {\n return [{ type: 'text', text }];\n}\n\n/** Safely parses a JSON string, returning a discriminated result. */\nexport function tryParseJson(input: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(input) };\n } catch {\n return { ok: false };\n }\n}\n\n/**\n * Parses an OpenAI tool-call `arguments` JSON string into an object. Reports and\n * returns `{}` when the string is not valid JSON object syntax.\n */\nexport function parseArguments(args: string, reporter: Reporter, fnName: string): Record<string, unknown> {\n const parsed = tryParseJson(args);\n if (parsed.ok && isRecord(parsed.value)) return parsed.value;\n reporter.warn(\n 'invalid-json-arguments',\n `Tool call '${fnName}' had arguments that were not a JSON object; used an empty object instead.`,\n );\n return {};\n}\n\n/**\n * Converts an OpenAI tool result string into a Gemini `functionResponse.response`\n * object. A JSON object string is used directly; anything else is wrapped as\n * `{ result: <text> }`, which {@link unwrapResponse} reverses.\n */\nexport function wrapResponse(content: string): Record<string, unknown> {\n const parsed = tryParseJson(content);\n if (parsed.ok && isRecord(parsed.value)) return parsed.value;\n return { result: content };\n}\n\n/** Reverses {@link wrapResponse}: a lone `{ result: string }` becomes that string. */\nexport function unwrapResponse(response: Record<string, unknown>): string {\n const keys = Object.keys(response);\n if (keys.length === 1 && keys[0] === 'result' && typeof response.result === 'string') {\n return response.result;\n }\n return JSON.stringify(response);\n}\n","import type {\n OpenAIAssistantMessage,\n OpenAIMessage,\n OpenAISystemMessage,\n OpenAIToolMessage,\n OpenAIUserMessage,\n} from '../types.js';\nimport { Reporter, textOf } from '../util.js';\n\n/** Any canonical message other than a system/developer prompt. */\nexport type NonSystemMessage = OpenAIUserMessage | OpenAIAssistantMessage | OpenAIToolMessage;\n\nfunction isSystem(message: OpenAIMessage): message is OpenAISystemMessage {\n return message.role === 'system' || message.role === 'developer';\n}\n\n/**\n * Splits a canonical OpenAI conversation into its system prompt and the\n * remaining messages. All `system` / `developer` messages are folded into a\n * single string (joined by blank lines) because Anthropic and Gemini carry the\n * system prompt as one top-level field. A system message that appears after the\n * conversation has started is still folded in, but reported as `system-midstream`.\n */\nexport function splitSystem(\n messages: OpenAIMessage[],\n reporter: Reporter,\n): { system?: string; rest: NonSystemMessage[] } {\n const systemParts: string[] = [];\n const rest: NonSystemMessage[] = [];\n let started = false;\n\n for (const message of messages) {\n if (isSystem(message)) {\n if (started) {\n reporter.warn(\n 'system-midstream',\n 'A system message appeared mid conversation; it was merged into the top-level system prompt.',\n );\n }\n systemParts.push(textOf(message.content));\n continue;\n }\n started = true;\n rest.push(message);\n }\n\n const system = systemParts.length > 0 ? systemParts.join('\\n\\n') : undefined;\n return system === undefined ? { rest } : { system, rest };\n}\n","import type {\n AnthropicContentBlock,\n AnthropicConversation,\n AnthropicMessage,\n ConvertOptions,\n OpenAIAssistantMessage,\n OpenAIContentPart,\n OpenAIMessage,\n OpenAIToolMessage,\n} from '../types.js';\nimport { Reporter, isRecord, parseArguments, textOf } from '../util.js';\nimport { splitSystem } from './openai.js';\n\n/* ------------------------------------------------------------------ */\n/* OpenAI (canonical) -> Anthropic */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a canonical OpenAI conversation into an Anthropic request fragment\n * (`{ system, messages }`). System messages move to the top-level `system`\n * field, tool-call arguments are JSON parsed into `input` objects, tool results\n * are folded into `tool_result` blocks inside a user turn, and consecutive\n * same-role turns are merged to satisfy Anthropic's strict alternation.\n */\nexport function toAnthropic(messages: OpenAIMessage[], options: ConvertOptions = {}): AnthropicConversation {\n const reporter = new Reporter(options);\n const { system, rest } = splitSystem(messages, reporter);\n const out: AnthropicMessage[] = [];\n\n for (let i = 0; i < rest.length; i++) {\n const message = rest[i];\n\n if (message.role === 'tool') {\n const blocks: AnthropicContentBlock[] = [];\n let j = i;\n while (j < rest.length && rest[j].role === 'tool') {\n const tool = rest[j] as OpenAIToolMessage;\n blocks.push({ type: 'tool_result', tool_use_id: tool.tool_call_id, content: textOf(tool.content) });\n j++;\n }\n out.push({ role: 'user', content: blocks });\n i = j - 1;\n continue;\n }\n\n if (message.role === 'user') {\n out.push({ role: 'user', content: userContent(message.content, reporter) });\n continue;\n }\n\n out.push({ role: 'assistant', content: assistantContent(message, reporter) });\n }\n\n const merged = mergeConsecutive(out, reporter);\n return system === undefined ? { messages: merged } : { system, messages: merged };\n}\n\nfunction userContent(content: string | OpenAIContentPart[], reporter: Reporter): string | AnthropicContentBlock[] {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return textOf(content);\n const blocks: AnthropicContentBlock[] = [];\n for (const part of content) {\n if (isRecord(part) && part.type === 'text' && typeof part.text === 'string') {\n blocks.push({ type: 'text', text: part.text });\n } else {\n reporter.warn('dropped-content', 'Dropped a non-text user content part not supported by this converter.');\n }\n }\n return blocks;\n}\n\nfunction assistantContent(message: OpenAIAssistantMessage, reporter: Reporter): string | AnthropicContentBlock[] {\n const text = textOf(message.content ?? '');\n const toolCalls = message.tool_calls ?? [];\n if (toolCalls.length === 0) return text;\n\n const blocks: AnthropicContentBlock[] = [];\n if (text) blocks.push({ type: 'text', text });\n for (const call of toolCalls) {\n blocks.push({\n type: 'tool_use',\n id: call.id,\n name: call.function.name,\n input: parseArguments(call.function.arguments, reporter, call.function.name),\n });\n }\n return blocks;\n}\n\n/** Merges adjacent same-role messages by concatenating their content blocks. */\nfunction mergeConsecutive(messages: AnthropicMessage[], reporter: Reporter): AnthropicMessage[] {\n const result: AnthropicMessage[] = [];\n for (const message of messages) {\n const previous = result[result.length - 1];\n if (previous && previous.role === message.role) {\n previous.content = [...asBlocks(previous.content), ...asBlocks(message.content)];\n reporter.warn(\n 'merged-role',\n `Merged consecutive '${message.role}' turns (Anthropic requires alternating roles).`,\n );\n } else {\n result.push({ role: message.role, content: message.content });\n }\n }\n return result;\n}\n\nfunction asBlocks(content: string | AnthropicContentBlock[]): AnthropicContentBlock[] {\n if (typeof content !== 'string') return content;\n return content ? [{ type: 'text', text: content }] : [];\n}\n\n/* ------------------------------------------------------------------ */\n/* Anthropic -> OpenAI (canonical) */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts an Anthropic request fragment back into a canonical OpenAI message\n * array. The top-level `system` becomes a leading system message, `tool_use`\n * blocks become `tool_calls` (with `input` re-serialized to a JSON string), and\n * `tool_result` blocks become standalone `role: 'tool'` messages.\n */\nexport function fromAnthropic(conversation: AnthropicConversation, options: ConvertOptions = {}): OpenAIMessage[] {\n void options;\n const out: OpenAIMessage[] = [];\n if (conversation.system) {\n out.push({ role: 'system', content: textOf(conversation.system) });\n }\n\n for (const message of conversation.messages) {\n const blocks = asBlocks(message.content);\n\n if (message.role === 'user') {\n const toolResults = blocks.filter((b) => b.type === 'tool_result');\n const textBlocks = blocks.filter((b) => b.type !== 'tool_result');\n for (const block of toolResults) {\n out.push({\n role: 'tool',\n tool_call_id: String((block as { tool_use_id?: string }).tool_use_id ?? ''),\n content: textOf((block as { content?: unknown }).content),\n });\n }\n if (textBlocks.length > 0) {\n out.push({ role: 'user', content: textOf(textBlocks) });\n }\n continue;\n }\n\n // assistant\n const text = textOf(blocks.filter((b) => b.type === 'text'));\n const toolUses = blocks.filter((b) => b.type === 'tool_use');\n const assistant: OpenAIAssistantMessage = { role: 'assistant', content: text || null };\n if (toolUses.length > 0) {\n assistant.tool_calls = toolUses.map((block) => {\n const b = block as { id: string; name: string; input: Record<string, unknown> };\n return { id: b.id, type: 'function', function: { name: b.name, arguments: JSON.stringify(b.input ?? {}) } };\n });\n }\n out.push(assistant);\n }\n\n return out;\n}\n","import type {\n ConvertOptions,\n GeminiContent,\n GeminiConversation,\n GeminiPart,\n OpenAIAssistantMessage,\n OpenAIContentPart,\n OpenAIMessage,\n OpenAIToolCall,\n OpenAIToolMessage,\n} from '../types.js';\nimport { Reporter, isRecord, parseArguments, textOf, unwrapResponse, wrapResponse } from '../util.js';\nimport { splitSystem } from './openai.js';\n\n/* ------------------------------------------------------------------ */\n/* OpenAI (canonical) -> Gemini */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a canonical OpenAI conversation into a Gemini request fragment\n * (`{ systemInstruction, contents }`). The assistant role becomes `model`,\n * tool-call arguments are JSON parsed into `args` objects, tool results become\n * `functionResponse` parts whose `name` is recovered from the matching call, and\n * consecutive same-role turns are merged for Gemini's strict alternation.\n *\n * The OpenAI tool-call `id` is carried through as `functionCall.id` so that a\n * round trip (OpenAI -> Gemini -> OpenAI) preserves ids exactly.\n */\nexport function toGemini(messages: OpenAIMessage[], options: ConvertOptions = {}): GeminiConversation {\n const reporter = new Reporter(options);\n const { system, rest } = splitSystem(messages, reporter);\n\n const idToName = new Map<string, string>();\n for (const message of rest) {\n if (message.role === 'assistant' && message.tool_calls) {\n for (const call of message.tool_calls) idToName.set(call.id, call.function.name);\n }\n }\n\n const contents: GeminiContent[] = [];\n for (let i = 0; i < rest.length; i++) {\n const message = rest[i];\n\n if (message.role === 'tool') {\n const parts: GeminiPart[] = [];\n let j = i;\n while (j < rest.length && rest[j].role === 'tool') {\n const tool = rest[j] as OpenAIToolMessage;\n const name = idToName.get(tool.tool_call_id);\n if (!name) {\n reporter.warn(\n 'unmapped-tool-result',\n `Tool result '${tool.tool_call_id}' has no matching call; used the id as the function name.`,\n );\n }\n parts.push({\n functionResponse: {\n id: tool.tool_call_id,\n name: name ?? tool.tool_call_id,\n response: wrapResponse(textOf(tool.content)),\n },\n });\n j++;\n }\n contents.push({ role: 'user', parts });\n i = j - 1;\n continue;\n }\n\n if (message.role === 'user') {\n contents.push({ role: 'user', parts: userParts(message.content, reporter) });\n continue;\n }\n\n contents.push({ role: 'model', parts: assistantParts(message, reporter) });\n }\n\n const merged = mergeConsecutive(contents, reporter);\n return system === undefined\n ? { contents: merged }\n : { systemInstruction: { parts: [{ text: system }] }, contents: merged };\n}\n\nfunction userParts(content: string | OpenAIContentPart[], reporter: Reporter): GeminiPart[] {\n if (typeof content === 'string') return [{ text: content }];\n const parts: GeminiPart[] = [];\n for (const part of content) {\n if (isRecord(part) && part.type === 'text' && typeof part.text === 'string') {\n parts.push({ text: part.text });\n } else {\n reporter.warn('dropped-content', 'Dropped a non-text user content part not supported by this converter.');\n }\n }\n return parts.length > 0 ? parts : [{ text: '' }];\n}\n\nfunction assistantParts(message: OpenAIAssistantMessage, reporter: Reporter): GeminiPart[] {\n const parts: GeminiPart[] = [];\n const text = textOf(message.content ?? '');\n if (text) parts.push({ text });\n for (const call of message.tool_calls ?? []) {\n parts.push({\n functionCall: {\n id: call.id,\n name: call.function.name,\n args: parseArguments(call.function.arguments, reporter, call.function.name),\n },\n });\n }\n return parts.length > 0 ? parts : [{ text: '' }];\n}\n\n/** Merges adjacent same-role contents by concatenating their `parts` arrays. */\nfunction mergeConsecutive(contents: GeminiContent[], reporter: Reporter): GeminiContent[] {\n const result: GeminiContent[] = [];\n for (const content of contents) {\n const previous = result[result.length - 1];\n if (previous && previous.role === content.role) {\n previous.parts = [...previous.parts, ...content.parts];\n reporter.warn('merged-role', `Merged consecutive '${content.role}' turns (Gemini requires alternating roles).`);\n } else {\n result.push({ role: content.role, parts: [...content.parts] });\n }\n }\n return result;\n}\n\n/* ------------------------------------------------------------------ */\n/* Gemini -> OpenAI (canonical) */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a Gemini request fragment back into a canonical OpenAI message array.\n * Because Gemini matches tool calls and responses by function name (the `id`\n * field is optional), this maintains a queue of pending calls and resolves each\n * `functionResponse` by id when present, otherwise by name in call order,\n * generating a deterministic id as a last resort.\n */\nexport function fromGemini(conversation: GeminiConversation, options: ConvertOptions = {}): OpenAIMessage[] {\n const reporter = new Reporter(options);\n const out: OpenAIMessage[] = [];\n\n if (conversation.systemInstruction) {\n const text = textOf(conversation.systemInstruction.parts);\n if (text) out.push({ role: 'system', content: text });\n }\n\n const pending: { id: string; name: string }[] = [];\n let counter = 0;\n const generateId = (name: string): string => `call_${name.replace(/[^a-zA-Z0-9_-]/g, '_')}_${counter++}`;\n\n for (const content of conversation.contents) {\n const parts = Array.isArray(content.parts) ? content.parts : [];\n\n if (content.role === 'model') {\n const textPieces: string[] = [];\n const toolCalls: OpenAIToolCall[] = [];\n for (const part of parts) {\n if (isRecord(part) && isRecord(part.functionCall)) {\n const fc = part.functionCall as { id?: string; name: string; args?: Record<string, unknown> };\n const id = fc.id ?? generateId(fc.name);\n if (!fc.id) reporter.warn('generated-id', `Gemini functionCall '${fc.name}' had no id; generated '${id}'.`);\n toolCalls.push({\n id,\n type: 'function',\n function: { name: fc.name, arguments: JSON.stringify(fc.args ?? {}) },\n });\n pending.push({ id, name: fc.name });\n } else if (isRecord(part) && typeof part.text === 'string') {\n textPieces.push(part.text);\n }\n }\n const text = textPieces.join('');\n const assistant: OpenAIAssistantMessage = { role: 'assistant', content: text || null };\n if (toolCalls.length > 0) assistant.tool_calls = toolCalls;\n out.push(assistant);\n continue;\n }\n\n // role 'user' or unspecified\n const textPieces: string[] = [];\n for (const part of parts) {\n if (isRecord(part) && isRecord(part.functionResponse)) {\n const fr = part.functionResponse as { id?: string; name: string; response?: Record<string, unknown> };\n const id = resolveResponseId(fr, pending, reporter, generateId);\n out.push({ role: 'tool', tool_call_id: id, content: unwrapResponse(fr.response ?? {}) });\n } else if (isRecord(part) && typeof part.text === 'string') {\n textPieces.push(part.text);\n }\n }\n const text = textPieces.join('');\n if (text) out.push({ role: 'user', content: text });\n }\n\n return out;\n}\n\nfunction resolveResponseId(\n response: { id?: string; name: string },\n pending: { id: string; name: string }[],\n reporter: Reporter,\n generateId: (name: string) => string,\n): string {\n if (response.id) {\n const index = pending.findIndex((p) => p.id === response.id);\n if (index >= 0) pending.splice(index, 1);\n return response.id;\n }\n const index = pending.findIndex((p) => p.name === response.name);\n if (index >= 0) {\n const { id } = pending[index];\n pending.splice(index, 1);\n return id;\n }\n const id = generateId(response.name);\n reporter.warn(\n 'unmapped-tool-result',\n `Gemini functionResponse for '${response.name}' had no matching call; generated '${id}'.`,\n );\n return id;\n}\n","import type { AnthropicConversation, ConvertOptions, GeminiConversation, OpenAIMessage, Provider } from './types.js';\nimport { fromAnthropic, toAnthropic } from './providers/anthropic.js';\nimport { fromGemini, toGemini } from './providers/gemini.js';\n\n/** Maps a provider to the conversation shape it accepts and returns. */\nexport type ConversationOf<P extends Provider> = P extends 'openai'\n ? OpenAIMessage[]\n : P extends 'anthropic'\n ? AnthropicConversation\n : P extends 'gemini'\n ? GeminiConversation\n : never;\n\n/**\n * Converts a conversation from one provider format to another. Every conversion\n * routes through the canonical OpenAI representation, so any source/target pair\n * is supported, including same-provider normalization.\n *\n * @example\n * const gemini = convert(openaiMessages, { from: 'openai', to: 'gemini' });\n * const openai = convert(anthropicBody, { from: 'anthropic', to: 'openai' });\n */\nexport function convert<From extends Provider, To extends Provider>(\n conversation: ConversationOf<From>,\n route: { from: From; to: To },\n options: ConvertOptions = {},\n): ConversationOf<To> {\n const canonical = toCanonical(conversation, route.from, options);\n return fromCanonical(canonical, route.to, options) as ConversationOf<To>;\n}\n\nfunction toCanonical(conversation: unknown, from: Provider, options: ConvertOptions): OpenAIMessage[] {\n switch (from) {\n case 'openai':\n return conversation as OpenAIMessage[];\n case 'anthropic':\n return fromAnthropic(conversation as AnthropicConversation, options);\n case 'gemini':\n return fromGemini(conversation as GeminiConversation, options);\n default:\n throw new Error(`Unknown source provider: ${String(from)}`);\n }\n}\n\nfunction fromCanonical(canonical: OpenAIMessage[], to: Provider, options: ConvertOptions): ConversationOf<Provider> {\n switch (to) {\n case 'openai':\n return canonical;\n case 'anthropic':\n return toAnthropic(canonical, options);\n case 'gemini':\n return toGemini(canonical, options);\n default:\n throw new Error(`Unknown target provider: ${String(to)}`);\n }\n}\n"],"mappings":"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","names":["mergeConsecutive","textPieces","text","index","id"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/util.ts","../src/image.ts","../src/providers/openai.ts","../src/providers/anthropic.ts","../src/providers/gemini.ts","../src/convert.ts","../src/response.ts"],"sourcesContent":["export { toAnthropic, fromAnthropic } from './providers/anthropic.js';\nexport { toGemini, fromGemini } from './providers/gemini.js';\nexport { convert } from './convert.js';\nexport type { ConversationOf } from './convert.js';\nexport { parseDataUrl, toDataUrl } from './image.js';\nexport type { NormalizedImage } from './image.js';\nexport { responseFromOpenAI, responseFromAnthropic, responseFromGemini, normalizeResponse } from './response.js';\nexport type { NormalizedResponse, FinishReason, Usage } from './response.js';\n\nexport type {\n Provider,\n Warning,\n WarningCode,\n ConvertOptions,\n OpenAIMessage,\n OpenAISystemMessage,\n OpenAIUserMessage,\n OpenAIAssistantMessage,\n OpenAIToolMessage,\n OpenAIToolCall,\n OpenAIContentPart,\n OpenAITextPart,\n OpenAIImagePart,\n AnthropicConversation,\n AnthropicMessage,\n AnthropicContentBlock,\n AnthropicTextBlock,\n AnthropicToolUseBlock,\n AnthropicToolResultBlock,\n AnthropicImageBlock,\n GeminiConversation,\n GeminiContent,\n GeminiPart,\n GeminiTextPart,\n GeminiFunctionCallPart,\n GeminiFunctionResponsePart,\n GeminiInlineDataPart,\n GeminiFileDataPart,\n} from './types.js';\n","import type { ConvertOptions, OpenAITextPart, Warning, WarningCode } from './types.js';\n\n/** Thin wrapper around the optional `onWarning` callback. */\nexport class Reporter {\n constructor(private readonly options: ConvertOptions = {}) {}\n\n warn(code: WarningCode, message: string): void {\n this.options.onWarning?.({ code, message } satisfies Warning);\n }\n}\n\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/**\n * Flattens any supported text content (a plain string or an array of text\n * parts/blocks) into a single string. Non text parts are ignored.\n */\nexport function textOf(content: unknown): string {\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (isRecord(part) && typeof part.text === 'string') return part.text;\n return '';\n })\n .join('');\n }\n return '';\n}\n\n/** Wraps a string as a single OpenAI/Anthropic text part array. */\nexport function textPart(text: string): OpenAITextPart[] {\n return [{ type: 'text', text }];\n}\n\n/** Safely parses a JSON string, returning a discriminated result. */\nexport function tryParseJson(input: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(input) };\n } catch {\n return { ok: false };\n }\n}\n\n/**\n * Parses an OpenAI tool-call `arguments` JSON string into an object. Reports and\n * returns `{}` when the string is not valid JSON object syntax.\n */\nexport function parseArguments(args: string, reporter: Reporter, fnName: string): Record<string, unknown> {\n const parsed = tryParseJson(args);\n if (parsed.ok && isRecord(parsed.value)) return parsed.value;\n reporter.warn(\n 'invalid-json-arguments',\n `Tool call '${fnName}' had arguments that were not a JSON object; used an empty object instead.`,\n );\n return {};\n}\n\n/**\n * Converts an OpenAI tool result string into a Gemini `functionResponse.response`\n * object. A JSON object string is used directly; anything else is wrapped as\n * `{ result: <text> }`, which {@link unwrapResponse} reverses.\n */\nexport function wrapResponse(content: string): Record<string, unknown> {\n const parsed = tryParseJson(content);\n if (parsed.ok && isRecord(parsed.value)) return parsed.value;\n return { result: content };\n}\n\n/** Reverses {@link wrapResponse}: a lone `{ result: string }` becomes that string. */\nexport function unwrapResponse(response: Record<string, unknown>): string {\n const keys = Object.keys(response);\n if (keys.length === 1 && keys[0] === 'result' && typeof response.result === 'string') {\n return response.result;\n }\n return JSON.stringify(response);\n}\n","import type { AnthropicImageBlock, GeminiPart, OpenAIImagePart } from './types.js';\nimport { Reporter, isRecord } from './util.js';\n\n/**\n * A provider-neutral image. `base64` carries the raw bytes plus media type\n * (mapping to a data URL, an Anthropic base64 source or a Gemini `inlineData`\n * part); `url` carries a remote reference.\n */\nexport type NormalizedImage = { kind: 'base64'; mediaType: string; data: string } | { kind: 'url'; url: string };\n\nconst DATA_URL = /^data:([^;,]+);base64,(.*)$/s;\n\n/** Decomposes a `data:<mediaType>;base64,<data>` URL. Returns null otherwise. */\nexport function parseDataUrl(url: string): { mediaType: string; data: string } | null {\n const match = DATA_URL.exec(url);\n if (!match) return null;\n return { mediaType: match[1], data: match[2] };\n}\n\n/** Reassembles a base64 data URL. The inverse of {@link parseDataUrl}. */\nexport function toDataUrl(mediaType: string, data: string): string {\n return `data:${mediaType};base64,${data}`;\n}\n\n/* ------------------------------- OpenAI -------------------------------- */\n\nexport function imageFromOpenAI(part: unknown): NormalizedImage | null {\n if (!isRecord(part) || part.type !== 'image_url' || !isRecord(part.image_url)) return null;\n const url = part.image_url.url;\n if (typeof url !== 'string') return null;\n const parsed = parseDataUrl(url);\n return parsed ? { kind: 'base64', ...parsed } : { kind: 'url', url };\n}\n\nexport function imageToOpenAI(image: NormalizedImage): OpenAIImagePart {\n const url = image.kind === 'base64' ? toDataUrl(image.mediaType, image.data) : image.url;\n return { type: 'image_url', image_url: { url } };\n}\n\n/* ------------------------------ Anthropic ------------------------------ */\n\nexport function imageFromAnthropic(block: unknown): NormalizedImage | null {\n if (!isRecord(block) || block.type !== 'image' || !isRecord(block.source)) return null;\n const source = block.source;\n if (source.type === 'base64' && typeof source.media_type === 'string' && typeof source.data === 'string') {\n return { kind: 'base64', mediaType: source.media_type, data: source.data };\n }\n if (source.type === 'url' && typeof source.url === 'string') {\n return { kind: 'url', url: source.url };\n }\n return null;\n}\n\nexport function imageToAnthropic(image: NormalizedImage): AnthropicImageBlock {\n if (image.kind === 'base64') {\n return { type: 'image', source: { type: 'base64', media_type: image.mediaType, data: image.data } };\n }\n return { type: 'image', source: { type: 'url', url: image.url } };\n}\n\n/* -------------------------------- Gemini ------------------------------- */\n\nexport function imageFromGemini(part: unknown): NormalizedImage | null {\n if (isRecord(part) && isRecord(part.inlineData)) {\n const data = part.inlineData;\n if (typeof data.mimeType === 'string' && typeof data.data === 'string') {\n return { kind: 'base64', mediaType: data.mimeType, data: data.data };\n }\n }\n if (isRecord(part) && isRecord(part.fileData)) {\n const data = part.fileData;\n if (typeof data.fileUri === 'string') return { kind: 'url', url: data.fileUri };\n }\n return null;\n}\n\nexport function imageToGemini(image: NormalizedImage, reporter: Reporter): GeminiPart {\n if (image.kind === 'base64') {\n return { inlineData: { mimeType: image.mediaType, data: image.data } };\n }\n // Gemini's fileData.fileUri is intended for Files API URIs, not arbitrary\n // public URLs. We emit it as a best effort and flag it so the caller can\n // upload via the Files API or inline the bytes if Gemini rejects it.\n reporter.warn(\n 'gemini-url-image',\n 'A remote image URL was emitted as Gemini fileData.fileUri; Gemini may require the Files API for non-Google URIs.',\n );\n return { fileData: { fileUri: image.url } };\n}\n","import type {\n OpenAIAssistantMessage,\n OpenAIMessage,\n OpenAISystemMessage,\n OpenAIToolMessage,\n OpenAIUserMessage,\n} from '../types.js';\nimport { Reporter, textOf } from '../util.js';\n\n/** Any canonical message other than a system/developer prompt. */\nexport type NonSystemMessage = OpenAIUserMessage | OpenAIAssistantMessage | OpenAIToolMessage;\n\nfunction isSystem(message: OpenAIMessage): message is OpenAISystemMessage {\n return message.role === 'system' || message.role === 'developer';\n}\n\n/**\n * Splits a canonical OpenAI conversation into its system prompt and the\n * remaining messages. All `system` / `developer` messages are folded into a\n * single string (joined by blank lines) because Anthropic and Gemini carry the\n * system prompt as one top-level field. A system message that appears after the\n * conversation has started is still folded in, but reported as `system-midstream`.\n */\nexport function splitSystem(\n messages: OpenAIMessage[],\n reporter: Reporter,\n): { system?: string; rest: NonSystemMessage[] } {\n const systemParts: string[] = [];\n const rest: NonSystemMessage[] = [];\n let started = false;\n\n for (const message of messages) {\n if (isSystem(message)) {\n if (started) {\n reporter.warn(\n 'system-midstream',\n 'A system message appeared mid conversation; it was merged into the top-level system prompt.',\n );\n }\n systemParts.push(textOf(message.content));\n continue;\n }\n started = true;\n rest.push(message);\n }\n\n const system = systemParts.length > 0 ? systemParts.join('\\n\\n') : undefined;\n return system === undefined ? { rest } : { system, rest };\n}\n","import type {\n AnthropicContentBlock,\n AnthropicConversation,\n AnthropicMessage,\n ConvertOptions,\n OpenAIAssistantMessage,\n OpenAIContentPart,\n OpenAIMessage,\n OpenAIToolMessage,\n} from '../types.js';\nimport { Reporter, isRecord, parseArguments, textOf } from '../util.js';\nimport { imageFromAnthropic, imageFromOpenAI, imageToAnthropic, imageToOpenAI } from '../image.js';\nimport { splitSystem } from './openai.js';\n\n/* ------------------------------------------------------------------ */\n/* OpenAI (canonical) -> Anthropic */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a canonical OpenAI conversation into an Anthropic request fragment\n * (`{ system, messages }`). System messages move to the top-level `system`\n * field, tool-call arguments are JSON parsed into `input` objects, tool results\n * are folded into `tool_result` blocks inside a user turn, and consecutive\n * same-role turns are merged to satisfy Anthropic's strict alternation.\n */\nexport function toAnthropic(messages: OpenAIMessage[], options: ConvertOptions = {}): AnthropicConversation {\n const reporter = new Reporter(options);\n const { system, rest } = splitSystem(messages, reporter);\n const out: AnthropicMessage[] = [];\n\n for (let i = 0; i < rest.length; i++) {\n const message = rest[i];\n\n if (message.role === 'tool') {\n const blocks: AnthropicContentBlock[] = [];\n let j = i;\n while (j < rest.length && rest[j].role === 'tool') {\n const tool = rest[j] as OpenAIToolMessage;\n blocks.push({ type: 'tool_result', tool_use_id: tool.tool_call_id, content: textOf(tool.content) });\n j++;\n }\n out.push({ role: 'user', content: blocks });\n i = j - 1;\n continue;\n }\n\n if (message.role === 'user') {\n out.push({ role: 'user', content: userContent(message.content, reporter) });\n continue;\n }\n\n out.push({ role: 'assistant', content: assistantContent(message, reporter) });\n }\n\n const merged = mergeConsecutive(out, reporter);\n return system === undefined ? { messages: merged } : { system, messages: merged };\n}\n\nfunction userContent(content: string | OpenAIContentPart[], reporter: Reporter): string | AnthropicContentBlock[] {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return textOf(content);\n const blocks: AnthropicContentBlock[] = [];\n for (const part of content) {\n if (isRecord(part) && part.type === 'text' && typeof part.text === 'string') {\n blocks.push({ type: 'text', text: part.text });\n continue;\n }\n const image = imageFromOpenAI(part);\n if (image) {\n blocks.push(imageToAnthropic(image));\n continue;\n }\n reporter.warn('dropped-content', 'Dropped an unsupported user content part.');\n }\n return blocks;\n}\n\nfunction assistantContent(message: OpenAIAssistantMessage, reporter: Reporter): string | AnthropicContentBlock[] {\n const text = textOf(message.content ?? '');\n const toolCalls = message.tool_calls ?? [];\n if (toolCalls.length === 0) return text;\n\n const blocks: AnthropicContentBlock[] = [];\n if (text) blocks.push({ type: 'text', text });\n for (const call of toolCalls) {\n blocks.push({\n type: 'tool_use',\n id: call.id,\n name: call.function.name,\n input: parseArguments(call.function.arguments, reporter, call.function.name),\n });\n }\n return blocks;\n}\n\n/** Merges adjacent same-role messages by concatenating their content blocks. */\nfunction mergeConsecutive(messages: AnthropicMessage[], reporter: Reporter): AnthropicMessage[] {\n const result: AnthropicMessage[] = [];\n for (const message of messages) {\n const previous = result[result.length - 1];\n if (previous && previous.role === message.role) {\n previous.content = [...asBlocks(previous.content), ...asBlocks(message.content)];\n reporter.warn(\n 'merged-role',\n `Merged consecutive '${message.role}' turns (Anthropic requires alternating roles).`,\n );\n } else {\n result.push({ role: message.role, content: message.content });\n }\n }\n return result;\n}\n\nfunction asBlocks(content: string | AnthropicContentBlock[]): AnthropicContentBlock[] {\n if (typeof content !== 'string') return content;\n return content ? [{ type: 'text', text: content }] : [];\n}\n\n/* ------------------------------------------------------------------ */\n/* Anthropic -> OpenAI (canonical) */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts an Anthropic request fragment back into a canonical OpenAI message\n * array. The top-level `system` becomes a leading system message, `tool_use`\n * blocks become `tool_calls` (with `input` re-serialized to a JSON string), and\n * `tool_result` blocks become standalone `role: 'tool'` messages.\n */\nexport function fromAnthropic(conversation: AnthropicConversation, options: ConvertOptions = {}): OpenAIMessage[] {\n void options;\n const out: OpenAIMessage[] = [];\n if (conversation.system) {\n out.push({ role: 'system', content: textOf(conversation.system) });\n }\n\n for (const message of conversation.messages) {\n const blocks = asBlocks(message.content);\n\n if (message.role === 'user') {\n const toolResults = blocks.filter((b) => b.type === 'tool_result');\n const contentBlocks = blocks.filter((b) => b.type !== 'tool_result');\n for (const block of toolResults) {\n out.push({\n role: 'tool',\n tool_call_id: String((block as { tool_use_id?: string }).tool_use_id ?? ''),\n content: textOf((block as { content?: unknown }).content),\n });\n }\n if (contentBlocks.length > 0) {\n out.push({ role: 'user', content: userContentToOpenAI(contentBlocks) });\n }\n continue;\n }\n\n // assistant\n const text = textOf(blocks.filter((b) => b.type === 'text'));\n const toolUses = blocks.filter((b) => b.type === 'tool_use');\n const assistant: OpenAIAssistantMessage = { role: 'assistant', content: text || null };\n if (toolUses.length > 0) {\n assistant.tool_calls = toolUses.map((block) => {\n const b = block as { id: string; name: string; input: Record<string, unknown> };\n return { id: b.id, type: 'function', function: { name: b.name, arguments: JSON.stringify(b.input ?? {}) } };\n });\n }\n out.push(assistant);\n }\n\n return out;\n}\n\n/** Rebuilds OpenAI user content from Anthropic blocks, as a string unless an image is present. */\nfunction userContentToOpenAI(blocks: AnthropicContentBlock[]): string | OpenAIContentPart[] {\n const hasImage = blocks.some((block) => imageFromAnthropic(block) !== null);\n if (!hasImage) return textOf(blocks);\n const parts: OpenAIContentPart[] = [];\n for (const block of blocks) {\n const image = imageFromAnthropic(block);\n if (image) {\n parts.push(imageToOpenAI(image));\n } else if (isRecord(block) && block.type === 'text' && typeof block.text === 'string') {\n parts.push({ type: 'text', text: block.text });\n }\n }\n return parts;\n}\n","import type {\n ConvertOptions,\n GeminiContent,\n GeminiConversation,\n GeminiPart,\n OpenAIAssistantMessage,\n OpenAIContentPart,\n OpenAIMessage,\n OpenAIToolCall,\n OpenAIToolMessage,\n} from '../types.js';\nimport { Reporter, isRecord, parseArguments, textOf, unwrapResponse, wrapResponse } from '../util.js';\nimport { imageFromGemini, imageFromOpenAI, imageToGemini, imageToOpenAI } from '../image.js';\nimport { splitSystem } from './openai.js';\n\n/* ------------------------------------------------------------------ */\n/* OpenAI (canonical) -> Gemini */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a canonical OpenAI conversation into a Gemini request fragment\n * (`{ systemInstruction, contents }`). The assistant role becomes `model`,\n * tool-call arguments are JSON parsed into `args` objects, tool results become\n * `functionResponse` parts whose `name` is recovered from the matching call, and\n * consecutive same-role turns are merged for Gemini's strict alternation.\n *\n * The OpenAI tool-call `id` is carried through as `functionCall.id` so that a\n * round trip (OpenAI -> Gemini -> OpenAI) preserves ids exactly.\n */\nexport function toGemini(messages: OpenAIMessage[], options: ConvertOptions = {}): GeminiConversation {\n const reporter = new Reporter(options);\n const { system, rest } = splitSystem(messages, reporter);\n\n const idToName = new Map<string, string>();\n for (const message of rest) {\n if (message.role === 'assistant' && message.tool_calls) {\n for (const call of message.tool_calls) idToName.set(call.id, call.function.name);\n }\n }\n\n const contents: GeminiContent[] = [];\n for (let i = 0; i < rest.length; i++) {\n const message = rest[i];\n\n if (message.role === 'tool') {\n const parts: GeminiPart[] = [];\n let j = i;\n while (j < rest.length && rest[j].role === 'tool') {\n const tool = rest[j] as OpenAIToolMessage;\n const name = idToName.get(tool.tool_call_id);\n if (!name) {\n reporter.warn(\n 'unmapped-tool-result',\n `Tool result '${tool.tool_call_id}' has no matching call; used the id as the function name.`,\n );\n }\n parts.push({\n functionResponse: {\n id: tool.tool_call_id,\n name: name ?? tool.tool_call_id,\n response: wrapResponse(textOf(tool.content)),\n },\n });\n j++;\n }\n contents.push({ role: 'user', parts });\n i = j - 1;\n continue;\n }\n\n if (message.role === 'user') {\n contents.push({ role: 'user', parts: userParts(message.content, reporter) });\n continue;\n }\n\n contents.push({ role: 'model', parts: assistantParts(message, reporter) });\n }\n\n const merged = mergeConsecutive(contents, reporter);\n return system === undefined\n ? { contents: merged }\n : { systemInstruction: { parts: [{ text: system }] }, contents: merged };\n}\n\nfunction userParts(content: string | OpenAIContentPart[], reporter: Reporter): GeminiPart[] {\n if (typeof content === 'string') return [{ text: content }];\n const parts: GeminiPart[] = [];\n for (const part of content) {\n if (isRecord(part) && part.type === 'text' && typeof part.text === 'string') {\n parts.push({ text: part.text });\n continue;\n }\n const image = imageFromOpenAI(part);\n if (image) {\n parts.push(imageToGemini(image, reporter));\n continue;\n }\n reporter.warn('dropped-content', 'Dropped an unsupported user content part.');\n }\n return parts.length > 0 ? parts : [{ text: '' }];\n}\n\nfunction assistantParts(message: OpenAIAssistantMessage, reporter: Reporter): GeminiPart[] {\n const parts: GeminiPart[] = [];\n const text = textOf(message.content ?? '');\n if (text) parts.push({ text });\n for (const call of message.tool_calls ?? []) {\n parts.push({\n functionCall: {\n id: call.id,\n name: call.function.name,\n args: parseArguments(call.function.arguments, reporter, call.function.name),\n },\n });\n }\n return parts.length > 0 ? parts : [{ text: '' }];\n}\n\n/** Merges adjacent same-role contents by concatenating their `parts` arrays. */\nfunction mergeConsecutive(contents: GeminiContent[], reporter: Reporter): GeminiContent[] {\n const result: GeminiContent[] = [];\n for (const content of contents) {\n const previous = result[result.length - 1];\n if (previous && previous.role === content.role) {\n previous.parts = [...previous.parts, ...content.parts];\n reporter.warn('merged-role', `Merged consecutive '${content.role}' turns (Gemini requires alternating roles).`);\n } else {\n result.push({ role: content.role, parts: [...content.parts] });\n }\n }\n return result;\n}\n\n/* ------------------------------------------------------------------ */\n/* Gemini -> OpenAI (canonical) */\n/* ------------------------------------------------------------------ */\n\n/**\n * Converts a Gemini request fragment back into a canonical OpenAI message array.\n * Because Gemini matches tool calls and responses by function name (the `id`\n * field is optional), this maintains a queue of pending calls and resolves each\n * `functionResponse` by id when present, otherwise by name in call order,\n * generating a deterministic id as a last resort.\n */\nexport function fromGemini(conversation: GeminiConversation, options: ConvertOptions = {}): OpenAIMessage[] {\n const reporter = new Reporter(options);\n const out: OpenAIMessage[] = [];\n\n if (conversation.systemInstruction) {\n const text = textOf(conversation.systemInstruction.parts);\n if (text) out.push({ role: 'system', content: text });\n }\n\n const pending: { id: string; name: string }[] = [];\n let counter = 0;\n const generateId = (name: string): string => `call_${name.replace(/[^a-zA-Z0-9_-]/g, '_')}_${counter++}`;\n\n for (const content of conversation.contents) {\n const parts = Array.isArray(content.parts) ? content.parts : [];\n\n if (content.role === 'model') {\n const textPieces: string[] = [];\n const toolCalls: OpenAIToolCall[] = [];\n for (const part of parts) {\n if (isRecord(part) && isRecord(part.functionCall)) {\n const fc = part.functionCall as { id?: string; name: string; args?: Record<string, unknown> };\n const id = fc.id ?? generateId(fc.name);\n if (!fc.id) reporter.warn('generated-id', `Gemini functionCall '${fc.name}' had no id; generated '${id}'.`);\n toolCalls.push({\n id,\n type: 'function',\n function: { name: fc.name, arguments: JSON.stringify(fc.args ?? {}) },\n });\n pending.push({ id, name: fc.name });\n } else if (isRecord(part) && typeof part.text === 'string') {\n textPieces.push(part.text);\n }\n }\n const text = textPieces.join('');\n const assistant: OpenAIAssistantMessage = { role: 'assistant', content: text || null };\n if (toolCalls.length > 0) assistant.tool_calls = toolCalls;\n out.push(assistant);\n continue;\n }\n\n // role 'user' or unspecified\n const contentParts: OpenAIContentPart[] = [];\n let hasImage = false;\n for (const part of parts) {\n if (isRecord(part) && isRecord(part.functionResponse)) {\n const fr = part.functionResponse as { id?: string; name: string; response?: Record<string, unknown> };\n const id = resolveResponseId(fr, pending, reporter, generateId);\n out.push({ role: 'tool', tool_call_id: id, content: unwrapResponse(fr.response ?? {}) });\n continue;\n }\n const image = imageFromGemini(part);\n if (image) {\n contentParts.push(imageToOpenAI(image));\n hasImage = true;\n continue;\n }\n if (isRecord(part) && typeof part.text === 'string') {\n contentParts.push({ type: 'text', text: part.text });\n }\n }\n if (contentParts.length > 0) {\n if (hasImage) {\n out.push({ role: 'user', content: contentParts });\n } else {\n const text = textOf(contentParts);\n if (text) out.push({ role: 'user', content: text });\n }\n }\n }\n\n return out;\n}\n\nfunction resolveResponseId(\n response: { id?: string; name: string },\n pending: { id: string; name: string }[],\n reporter: Reporter,\n generateId: (name: string) => string,\n): string {\n if (response.id) {\n const index = pending.findIndex((p) => p.id === response.id);\n if (index >= 0) pending.splice(index, 1);\n return response.id;\n }\n const index = pending.findIndex((p) => p.name === response.name);\n if (index >= 0) {\n const { id } = pending[index];\n pending.splice(index, 1);\n return id;\n }\n const id = generateId(response.name);\n reporter.warn(\n 'unmapped-tool-result',\n `Gemini functionResponse for '${response.name}' had no matching call; generated '${id}'.`,\n );\n return id;\n}\n","import type { AnthropicConversation, ConvertOptions, GeminiConversation, OpenAIMessage, Provider } from './types.js';\nimport { fromAnthropic, toAnthropic } from './providers/anthropic.js';\nimport { fromGemini, toGemini } from './providers/gemini.js';\n\n/** Maps a provider to the conversation shape it accepts and returns. */\nexport type ConversationOf<P extends Provider> = P extends 'openai'\n ? OpenAIMessage[]\n : P extends 'anthropic'\n ? AnthropicConversation\n : P extends 'gemini'\n ? GeminiConversation\n : never;\n\n/**\n * Converts a conversation from one provider format to another. Every conversion\n * routes through the canonical OpenAI representation, so any source/target pair\n * is supported, including same-provider normalization.\n *\n * @example\n * const gemini = convert(openaiMessages, { from: 'openai', to: 'gemini' });\n * const openai = convert(anthropicBody, { from: 'anthropic', to: 'openai' });\n */\nexport function convert<From extends Provider, To extends Provider>(\n conversation: ConversationOf<From>,\n route: { from: From; to: To },\n options: ConvertOptions = {},\n): ConversationOf<To> {\n const canonical = toCanonical(conversation, route.from, options);\n return fromCanonical(canonical, route.to, options) as ConversationOf<To>;\n}\n\nfunction toCanonical(conversation: unknown, from: Provider, options: ConvertOptions): OpenAIMessage[] {\n switch (from) {\n case 'openai':\n return conversation as OpenAIMessage[];\n case 'anthropic':\n return fromAnthropic(conversation as AnthropicConversation, options);\n case 'gemini':\n return fromGemini(conversation as GeminiConversation, options);\n default:\n throw new Error(`Unknown source provider: ${String(from)}`);\n }\n}\n\nfunction fromCanonical(canonical: OpenAIMessage[], to: Provider, options: ConvertOptions): ConversationOf<Provider> {\n switch (to) {\n case 'openai':\n return canonical;\n case 'anthropic':\n return toAnthropic(canonical, options);\n case 'gemini':\n return toGemini(canonical, options);\n default:\n throw new Error(`Unknown target provider: ${String(to)}`);\n }\n}\n","import type { ConvertOptions, OpenAIAssistantMessage, OpenAIToolCall, Provider } from './types.js';\nimport { Reporter, isRecord, textOf } from './util.js';\n\n/** A provider-neutral finish reason. */\nexport type FinishReason = 'stop' | 'tool_calls' | 'length' | 'content_filter' | 'unknown';\n\n/** Provider-neutral token usage. */\nexport interface Usage {\n inputTokens: number;\n outputTokens: number;\n}\n\n/** A provider response normalized to the canonical OpenAI assistant shape. */\nexport interface NormalizedResponse {\n message: OpenAIAssistantMessage;\n finishReason: FinishReason;\n usage: Usage;\n}\n\nconst num = (value: unknown): number => (typeof value === 'number' ? value : 0);\n\n/** Builds the canonical message, setting `content` to null when only tool calls are present. */\nfunction buildMessage(text: string, toolCalls: OpenAIToolCall[]): OpenAIAssistantMessage {\n const message: OpenAIAssistantMessage = { role: 'assistant', content: text ? text : null };\n if (toolCalls.length > 0) message.tool_calls = toolCalls;\n return message;\n}\n\n/** When tool calls are present the canonical finish reason is always `tool_calls`. */\nfunction finalReason(mapped: FinishReason, toolCalls: OpenAIToolCall[]): FinishReason {\n return toolCalls.length > 0 ? 'tool_calls' : mapped;\n}\n\n/* -------------------------------- OpenAI ------------------------------- */\n\nconst OPENAI_FINISH: Record<string, FinishReason> = {\n stop: 'stop',\n length: 'length',\n tool_calls: 'tool_calls',\n content_filter: 'content_filter',\n function_call: 'tool_calls',\n};\n\n/** Normalizes an OpenAI Chat Completions response body. */\nexport function responseFromOpenAI(body: unknown): NormalizedResponse {\n const root = isRecord(body) ? body : {};\n const choice = Array.isArray(root.choices) && isRecord(root.choices[0]) ? root.choices[0] : {};\n const message = isRecord(choice.message) ? choice.message : {};\n\n const text = typeof message.content === 'string' ? message.content : textOf(message.content);\n const toolCalls = Array.isArray(message.tool_calls) ? (message.tool_calls as OpenAIToolCall[]) : [];\n\n const usage = isRecord(root.usage) ? root.usage : {};\n return {\n message: buildMessage(text, toolCalls),\n finishReason: finalReason(OPENAI_FINISH[String(choice.finish_reason)] ?? 'unknown', toolCalls),\n usage: { inputTokens: num(usage.prompt_tokens), outputTokens: num(usage.completion_tokens) },\n };\n}\n\n/* ------------------------------- Anthropic ----------------------------- */\n\nconst ANTHROPIC_FINISH: Record<string, FinishReason> = {\n end_turn: 'stop',\n stop_sequence: 'stop',\n tool_use: 'tool_calls',\n max_tokens: 'length',\n refusal: 'content_filter',\n pause_turn: 'unknown',\n};\n\n/** Normalizes an Anthropic Messages response body. */\nexport function responseFromAnthropic(body: unknown): NormalizedResponse {\n const root = isRecord(body) ? body : {};\n const blocks = Array.isArray(root.content) ? root.content : [];\n\n const textPieces: string[] = [];\n const toolCalls: OpenAIToolCall[] = [];\n for (const block of blocks) {\n if (!isRecord(block)) continue;\n if (block.type === 'text' && typeof block.text === 'string') {\n textPieces.push(block.text);\n } else if (block.type === 'tool_use' && typeof block.name === 'string') {\n toolCalls.push({\n id: typeof block.id === 'string' ? block.id : '',\n type: 'function',\n function: { name: block.name, arguments: JSON.stringify(block.input ?? {}) },\n });\n }\n }\n\n const usage = isRecord(root.usage) ? root.usage : {};\n return {\n message: buildMessage(textPieces.join(''), toolCalls),\n finishReason: finalReason(ANTHROPIC_FINISH[String(root.stop_reason)] ?? 'unknown', toolCalls),\n usage: { inputTokens: num(usage.input_tokens), outputTokens: num(usage.output_tokens) },\n };\n}\n\n/* -------------------------------- Gemini ------------------------------- */\n\nconst GEMINI_FINISH: Record<string, FinishReason> = {\n STOP: 'stop',\n MAX_TOKENS: 'length',\n SAFETY: 'content_filter',\n RECITATION: 'content_filter',\n MALFORMED_FUNCTION_CALL: 'content_filter',\n};\n\n/** Normalizes a Gemini generateContent response body. */\nexport function responseFromGemini(body: unknown, options: ConvertOptions = {}): NormalizedResponse {\n const reporter = new Reporter(options);\n const root = isRecord(body) ? body : {};\n const candidate = Array.isArray(root.candidates) && isRecord(root.candidates[0]) ? root.candidates[0] : {};\n const content = isRecord(candidate.content) ? candidate.content : {};\n const parts = Array.isArray(content.parts) ? content.parts : [];\n\n const textPieces: string[] = [];\n const toolCalls: OpenAIToolCall[] = [];\n let counter = 0;\n for (const part of parts) {\n if (!isRecord(part)) continue;\n if (isRecord(part.functionCall)) {\n const call = part.functionCall as { id?: string; name?: string; args?: Record<string, unknown> };\n const name = typeof call.name === 'string' ? call.name : 'function';\n let id = call.id;\n if (!id) {\n id = `call_${name.replace(/[^a-zA-Z0-9_-]/g, '_')}_${counter++}`;\n reporter.warn('generated-id', `Gemini functionCall '${name}' had no id; generated '${id}'.`);\n }\n toolCalls.push({ id, type: 'function', function: { name, arguments: JSON.stringify(call.args ?? {}) } });\n } else if (typeof part.text === 'string') {\n textPieces.push(part.text);\n }\n }\n\n const usage = isRecord(root.usageMetadata) ? root.usageMetadata : {};\n return {\n message: buildMessage(textPieces.join(''), toolCalls),\n finishReason: finalReason(GEMINI_FINISH[String(candidate.finishReason)] ?? 'unknown', toolCalls),\n usage: { inputTokens: num(usage.promptTokenCount), outputTokens: num(usage.candidatesTokenCount) },\n };\n}\n\n/* ------------------------------ Dispatcher ----------------------------- */\n\n/** Normalizes a provider response body into the canonical shape. */\nexport function normalizeResponse(\n body: unknown,\n route: { from: Provider },\n options: ConvertOptions = {},\n): NormalizedResponse {\n switch (route.from) {\n case 'openai':\n return responseFromOpenAI(body);\n case 'anthropic':\n return responseFromAnthropic(body);\n case 'gemini':\n return responseFromGemini(body, options);\n default:\n throw new Error(`Unknown source provider: ${String(route.from)}`);\n }\n}\n"],"mappings":"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","names":["mergeConsecutive","index","id"]}
|