headroom-ai 0.37.0 → 0.39.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/dist/adapters/anthropic.cjs +34 -8
- package/dist/adapters/anthropic.cjs.map +1 -1
- package/dist/adapters/anthropic.d.cts +1 -1
- package/dist/adapters/anthropic.d.ts +1 -1
- package/dist/adapters/anthropic.js +33 -7
- package/dist/adapters/anthropic.js.map +1 -1
- package/dist/adapters/gemini.cjs +3 -3
- package/dist/adapters/gemini.d.cts +1 -1
- package/dist/adapters/gemini.d.ts +1 -1
- package/dist/adapters/gemini.js +1 -1
- package/dist/adapters/openai.cjs +2 -2
- package/dist/adapters/openai.d.cts +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.js +1 -1
- package/dist/adapters/vercel-ai.cjs +7 -7
- package/dist/adapters/vercel-ai.d.cts +1 -1
- package/dist/adapters/vercel-ai.d.ts +1 -1
- package/dist/adapters/vercel-ai.js +1 -1
- package/dist/{chunk-2NXG6XPP.js → chunk-24W6JPRV.js} +54 -59
- package/dist/chunk-24W6JPRV.js.map +1 -0
- package/dist/{chunk-UVXIB3EK.cjs → chunk-26N6BVLT.cjs} +54 -59
- package/dist/chunk-26N6BVLT.cjs.map +1 -0
- package/dist/index.cjs +35 -35
- package/dist/index.d.cts +7 -152
- package/dist/index.d.ts +7 -152
- package/dist/index.js +2 -2
- package/dist/types-Cjdl75F4.d.cts +327 -0
- package/dist/types-Cjdl75F4.d.ts +327 -0
- package/package.json +10 -9
- package/dist/chunk-2NXG6XPP.js.map +0 -1
- package/dist/chunk-UVXIB3EK.cjs.map +0 -1
- package/dist/types-BTrX7__W.d.cts +0 -149
- package/dist/types-BTrX7__W.d.ts +0 -149
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk26N6BVLT_cjs = require('../chunk-26N6BVLT.cjs');
|
|
4
4
|
|
|
5
5
|
// src/adapters/anthropic.ts
|
|
6
|
+
function anthropicImageUrl(source) {
|
|
7
|
+
if (!source || typeof source !== "object") return null;
|
|
8
|
+
if (source.type === "base64" && source.media_type && source.data) {
|
|
9
|
+
return `data:${source.media_type};base64,${source.data}`;
|
|
10
|
+
}
|
|
11
|
+
if (source.type === "url" && typeof source.url === "string") return source.url;
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function openAIImageBlock(url) {
|
|
15
|
+
const m = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
16
|
+
if (m) return { type: "image", source: { type: "base64", media_type: m[1], data: m[2] } };
|
|
17
|
+
return { type: "image", source: { type: "url", url } };
|
|
18
|
+
}
|
|
6
19
|
function anthropicToOpenAI(messages) {
|
|
7
20
|
const result = [];
|
|
8
21
|
for (const msg of messages) {
|
|
@@ -13,12 +26,24 @@ function anthropicToOpenAI(messages) {
|
|
|
13
26
|
const toolResults = msg.content.filter(
|
|
14
27
|
(b) => b.type === "tool_result"
|
|
15
28
|
);
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
const hasImages = msg.content.some((b) => b.type === "image");
|
|
30
|
+
if (hasImages) {
|
|
31
|
+
const parts = msg.content.filter((b) => b.type === "text" || b.type === "image").map((b) => {
|
|
32
|
+
if (b.type === "text") return { type: "text", text: b.text };
|
|
33
|
+
const url = anthropicImageUrl(b.source);
|
|
34
|
+
return url ? { type: "image_url", image_url: { url } } : null;
|
|
35
|
+
}).filter((p) => p !== null);
|
|
36
|
+
if (parts.length > 0) {
|
|
37
|
+
result.push({ role: "user", content: parts });
|
|
38
|
+
}
|
|
39
|
+
} else {
|
|
40
|
+
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
41
|
+
if (textBlocks.length > 0) {
|
|
42
|
+
result.push({
|
|
43
|
+
role: "user",
|
|
44
|
+
content: textBlocks.map((b) => b.text).join("\n")
|
|
45
|
+
});
|
|
46
|
+
}
|
|
22
47
|
}
|
|
23
48
|
for (const tr of toolResults) {
|
|
24
49
|
result.push({
|
|
@@ -70,6 +95,7 @@ function openAIToAnthropic(messages) {
|
|
|
70
95
|
role: "user",
|
|
71
96
|
content: msg.content.map((p) => {
|
|
72
97
|
if (p.type === "text") return { type: "text", text: p.text };
|
|
98
|
+
if (p.type === "image_url") return openAIImageBlock(p.image_url.url);
|
|
73
99
|
return { type: "text", text: "" };
|
|
74
100
|
})
|
|
75
101
|
});
|
|
@@ -120,7 +146,7 @@ function withHeadroom(client, options = {}) {
|
|
|
120
146
|
const messages = params.messages;
|
|
121
147
|
const model = options.model ?? params.model ?? "claude-sonnet-4-5-20250929";
|
|
122
148
|
const openaiMessages = anthropicToOpenAI(messages);
|
|
123
|
-
const result = await
|
|
149
|
+
const result = await chunk26N6BVLT_cjs.compress(openaiMessages, {
|
|
124
150
|
stack: "adapter_ts_anthropic",
|
|
125
151
|
...options,
|
|
126
152
|
model
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/anthropic.ts"],"names":["compress"],"mappings":"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","file":"anthropic.cjs","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\nimport type { AssistantMessage, ToolCall } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface AnthropicLike {\n messages: {\n create: (params: any) => any;\n };\n [key: string]: any;\n}\n\n/**\n * Convert Anthropic messages to OpenAI format for compression.\n *\n * Anthropic format:\n * { role: 'user' | 'assistant', content: string | ContentBlock[] }\n * ContentBlock = { type: 'text', text } | { type: 'tool_use', ... } | { type: 'tool_result', ... }\n */\nfunction anthropicToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolResults = msg.content.filter(\n (b: any) => b.type === \"tool_result\",\n );\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n\n if (textBlocks.length > 0) {\n result.push({\n role: \"user\",\n content: textBlocks.map((b: any) => b.text).join(\"\\n\"),\n });\n }\n for (const tr of toolResults) {\n result.push({\n role: \"tool\",\n content:\n typeof tr.content === \"string\"\n ? tr.content\n : JSON.stringify(tr.content),\n tool_call_id: tr.tool_use_id,\n });\n }\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n const toolUseBlocks = msg.content.filter(\n (b: any) => b.type === \"tool_use\",\n );\n\n const content =\n textBlocks.length > 0\n ? textBlocks.map((b: any) => b.text).join(\"\\n\")\n : null;\n\n const openaiMsg: AssistantMessage = { role: \"assistant\", content };\n if (toolUseBlocks.length > 0) {\n openaiMsg.tool_calls = toolUseBlocks.map(\n (b: any): ToolCall => ({\n id: b.id,\n type: \"function\",\n function: {\n name: b.name,\n arguments: JSON.stringify(b.input),\n },\n }),\n );\n }\n result.push(openaiMsg);\n }\n continue;\n }\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to Anthropic format.\n */\nfunction openAIToAnthropic(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n result.push({\n role: \"user\",\n content: msg.content.map((p) => {\n if (p.type === \"text\") return { type: \"text\", text: p.text };\n return { type: \"text\", text: \"\" };\n }),\n });\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) blocks.push({ type: \"text\", text: msg.content });\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n });\n }\n }\n result.push({\n role: \"assistant\",\n content:\n blocks.length === 1 && blocks[0].type === \"text\"\n ? blocks[0].text\n : blocks,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n result.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: msg.content,\n },\n ],\n });\n continue;\n }\n\n if (msg.role === \"system\") {\n // System messages are handled separately in Anthropic (as `system` param)\n // Pass through as user message if it appears in the messages array\n result.push({ role: \"user\", content: msg.content });\n }\n }\n\n return result;\n}\n\n/**\n * Wrap an Anthropic client to auto-compress messages before each request.\n *\n * Intercepts `client.messages.create()` only. All other methods pass through.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n *\n * const client = withHeadroom(new Anthropic());\n * const response = await client.messages.create({\n * model: 'claude-sonnet-4-5-20250929',\n * messages: longConversation,\n * max_tokens: 1024,\n * });\n * ```\n */\nexport function withHeadroom<T extends AnthropicLike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.messages.create.bind(client.messages);\n\n const compressedCreate = async (params: any) => {\n const messages = params.messages;\n const model =\n options.model ?? params.model ?? \"claude-sonnet-4-5-20250929\";\n\n const openaiMessages = anthropicToOpenAI(messages);\n const result = await compress(openaiMessages, {\n stack: \"adapter_ts_anthropic\",\n ...options,\n model,\n });\n\n const anthropicMessages = result.compressed\n ? openAIToAnthropic(result.messages)\n : messages;\n\n return originalCreate({\n ...params,\n messages: anthropicMessages,\n });\n };\n\n const messagesProxy = new Proxy(client.messages, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"messages\") return messagesProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/anthropic.ts"],"names":["compress"],"mappings":"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","file":"anthropic.cjs","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\nimport type { AssistantMessage, ToolCall } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface AnthropicLike {\n messages: {\n create: (params: any) => any;\n };\n [key: string]: any;\n}\n\n// Anthropic image block `source` -> OpenAI image_url url string, or null if unconvertible.\nfunction anthropicImageUrl(source: any): string | null {\n if (!source || typeof source !== \"object\") return null;\n if (source.type === \"base64\" && source.media_type && source.data) {\n return `data:${source.media_type};base64,${source.data}`;\n }\n if (source.type === \"url\" && typeof source.url === \"string\") return source.url;\n return null;\n}\n\n// OpenAI image_url url -> Anthropic image block. data: URIs become a base64 source; other urls a url source.\nfunction openAIImageBlock(url: string): any {\n const m = /^data:([^;,]+);base64,(.*)$/s.exec(url);\n if (m) return { type: \"image\", source: { type: \"base64\", media_type: m[1], data: m[2] } };\n return { type: \"image\", source: { type: \"url\", url } };\n}\n\n/**\n * Convert Anthropic messages to OpenAI format for compression.\n *\n * Anthropic format:\n * { role: 'user' | 'assistant', content: string | ContentBlock[] }\n * ContentBlock = { type: 'text', text } | { type: 'tool_use', ... } | { type: 'tool_result', ... }\n */\nfunction anthropicToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolResults = msg.content.filter(\n (b: any) => b.type === \"tool_result\",\n );\n const hasImages = msg.content.some((b: any) => b.type === \"image\");\n\n if (hasImages) {\n const parts = msg.content\n .filter((b: any) => b.type === \"text\" || b.type === \"image\")\n .map((b: any) => {\n if (b.type === \"text\") return { type: \"text\" as const, text: b.text };\n const url = anthropicImageUrl(b.source);\n return url ? { type: \"image_url\" as const, image_url: { url } } : null;\n })\n .filter((p: any): p is NonNullable<typeof p> => p !== null);\n\n if (parts.length > 0) {\n result.push({ role: \"user\", content: parts });\n }\n } else {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n if (textBlocks.length > 0) {\n result.push({\n role: \"user\",\n content: textBlocks.map((b: any) => b.text).join(\"\\n\"),\n });\n }\n }\n for (const tr of toolResults) {\n result.push({\n role: \"tool\",\n content:\n typeof tr.content === \"string\"\n ? tr.content\n : JSON.stringify(tr.content),\n tool_call_id: tr.tool_use_id,\n });\n }\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n const toolUseBlocks = msg.content.filter(\n (b: any) => b.type === \"tool_use\",\n );\n\n const content =\n textBlocks.length > 0\n ? textBlocks.map((b: any) => b.text).join(\"\\n\")\n : null;\n\n const openaiMsg: AssistantMessage = { role: \"assistant\", content };\n if (toolUseBlocks.length > 0) {\n openaiMsg.tool_calls = toolUseBlocks.map(\n (b: any): ToolCall => ({\n id: b.id,\n type: \"function\",\n function: {\n name: b.name,\n arguments: JSON.stringify(b.input),\n },\n }),\n );\n }\n result.push(openaiMsg);\n }\n continue;\n }\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to Anthropic format.\n */\nfunction openAIToAnthropic(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n result.push({\n role: \"user\",\n content: msg.content.map((p) => {\n if (p.type === \"text\") return { type: \"text\", text: p.text };\n if (p.type === \"image_url\") return openAIImageBlock(p.image_url.url);\n return { type: \"text\", text: \"\" };\n }),\n });\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) blocks.push({ type: \"text\", text: msg.content });\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n });\n }\n }\n result.push({\n role: \"assistant\",\n content:\n blocks.length === 1 && blocks[0].type === \"text\"\n ? blocks[0].text\n : blocks,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n result.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: msg.content,\n },\n ],\n });\n continue;\n }\n\n if (msg.role === \"system\") {\n // System messages are handled separately in Anthropic (as `system` param)\n // Pass through as user message if it appears in the messages array\n result.push({ role: \"user\", content: msg.content });\n }\n }\n\n return result;\n}\n\n/**\n * Wrap an Anthropic client to auto-compress messages before each request.\n *\n * Intercepts `client.messages.create()` only. All other methods pass through.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n *\n * const client = withHeadroom(new Anthropic());\n * const response = await client.messages.create({\n * model: 'claude-sonnet-4-5-20250929',\n * messages: longConversation,\n * max_tokens: 1024,\n * });\n * ```\n */\nexport function withHeadroom<T extends AnthropicLike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.messages.create.bind(client.messages);\n\n const compressedCreate = async (params: any) => {\n const messages = params.messages;\n const model =\n options.model ?? params.model ?? \"claude-sonnet-4-5-20250929\";\n\n const openaiMessages = anthropicToOpenAI(messages);\n const result = await compress(openaiMessages, {\n stack: \"adapter_ts_anthropic\",\n ...options,\n model,\n });\n\n const anthropicMessages = result.compressed\n ? openAIToAnthropic(result.messages)\n : messages;\n\n return originalCreate({\n ...params,\n messages: anthropicMessages,\n });\n };\n\n const messagesProxy = new Proxy(client.messages, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"messages\") return messagesProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
import { compress } from '../chunk-
|
|
1
|
+
import { compress } from '../chunk-24W6JPRV.js';
|
|
2
2
|
|
|
3
3
|
// src/adapters/anthropic.ts
|
|
4
|
+
function anthropicImageUrl(source) {
|
|
5
|
+
if (!source || typeof source !== "object") return null;
|
|
6
|
+
if (source.type === "base64" && source.media_type && source.data) {
|
|
7
|
+
return `data:${source.media_type};base64,${source.data}`;
|
|
8
|
+
}
|
|
9
|
+
if (source.type === "url" && typeof source.url === "string") return source.url;
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function openAIImageBlock(url) {
|
|
13
|
+
const m = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
14
|
+
if (m) return { type: "image", source: { type: "base64", media_type: m[1], data: m[2] } };
|
|
15
|
+
return { type: "image", source: { type: "url", url } };
|
|
16
|
+
}
|
|
4
17
|
function anthropicToOpenAI(messages) {
|
|
5
18
|
const result = [];
|
|
6
19
|
for (const msg of messages) {
|
|
@@ -11,12 +24,24 @@ function anthropicToOpenAI(messages) {
|
|
|
11
24
|
const toolResults = msg.content.filter(
|
|
12
25
|
(b) => b.type === "tool_result"
|
|
13
26
|
);
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
const hasImages = msg.content.some((b) => b.type === "image");
|
|
28
|
+
if (hasImages) {
|
|
29
|
+
const parts = msg.content.filter((b) => b.type === "text" || b.type === "image").map((b) => {
|
|
30
|
+
if (b.type === "text") return { type: "text", text: b.text };
|
|
31
|
+
const url = anthropicImageUrl(b.source);
|
|
32
|
+
return url ? { type: "image_url", image_url: { url } } : null;
|
|
33
|
+
}).filter((p) => p !== null);
|
|
34
|
+
if (parts.length > 0) {
|
|
35
|
+
result.push({ role: "user", content: parts });
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
39
|
+
if (textBlocks.length > 0) {
|
|
40
|
+
result.push({
|
|
41
|
+
role: "user",
|
|
42
|
+
content: textBlocks.map((b) => b.text).join("\n")
|
|
43
|
+
});
|
|
44
|
+
}
|
|
20
45
|
}
|
|
21
46
|
for (const tr of toolResults) {
|
|
22
47
|
result.push({
|
|
@@ -68,6 +93,7 @@ function openAIToAnthropic(messages) {
|
|
|
68
93
|
role: "user",
|
|
69
94
|
content: msg.content.map((p) => {
|
|
70
95
|
if (p.type === "text") return { type: "text", text: p.text };
|
|
96
|
+
if (p.type === "image_url") return openAIImageBlock(p.image_url.url);
|
|
71
97
|
return { type: "text", text: "" };
|
|
72
98
|
})
|
|
73
99
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"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","file":"anthropic.js","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\nimport type { AssistantMessage, ToolCall } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface AnthropicLike {\n messages: {\n create: (params: any) => any;\n };\n [key: string]: any;\n}\n\n/**\n * Convert Anthropic messages to OpenAI format for compression.\n *\n * Anthropic format:\n * { role: 'user' | 'assistant', content: string | ContentBlock[] }\n * ContentBlock = { type: 'text', text } | { type: 'tool_use', ... } | { type: 'tool_result', ... }\n */\nfunction anthropicToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolResults = msg.content.filter(\n (b: any) => b.type === \"tool_result\",\n );\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n\n if (textBlocks.length > 0) {\n result.push({\n role: \"user\",\n content: textBlocks.map((b: any) => b.text).join(\"\\n\"),\n });\n }\n for (const tr of toolResults) {\n result.push({\n role: \"tool\",\n content:\n typeof tr.content === \"string\"\n ? tr.content\n : JSON.stringify(tr.content),\n tool_call_id: tr.tool_use_id,\n });\n }\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n const toolUseBlocks = msg.content.filter(\n (b: any) => b.type === \"tool_use\",\n );\n\n const content =\n textBlocks.length > 0\n ? textBlocks.map((b: any) => b.text).join(\"\\n\")\n : null;\n\n const openaiMsg: AssistantMessage = { role: \"assistant\", content };\n if (toolUseBlocks.length > 0) {\n openaiMsg.tool_calls = toolUseBlocks.map(\n (b: any): ToolCall => ({\n id: b.id,\n type: \"function\",\n function: {\n name: b.name,\n arguments: JSON.stringify(b.input),\n },\n }),\n );\n }\n result.push(openaiMsg);\n }\n continue;\n }\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to Anthropic format.\n */\nfunction openAIToAnthropic(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n result.push({\n role: \"user\",\n content: msg.content.map((p) => {\n if (p.type === \"text\") return { type: \"text\", text: p.text };\n return { type: \"text\", text: \"\" };\n }),\n });\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) blocks.push({ type: \"text\", text: msg.content });\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n });\n }\n }\n result.push({\n role: \"assistant\",\n content:\n blocks.length === 1 && blocks[0].type === \"text\"\n ? blocks[0].text\n : blocks,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n result.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: msg.content,\n },\n ],\n });\n continue;\n }\n\n if (msg.role === \"system\") {\n // System messages are handled separately in Anthropic (as `system` param)\n // Pass through as user message if it appears in the messages array\n result.push({ role: \"user\", content: msg.content });\n }\n }\n\n return result;\n}\n\n/**\n * Wrap an Anthropic client to auto-compress messages before each request.\n *\n * Intercepts `client.messages.create()` only. All other methods pass through.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n *\n * const client = withHeadroom(new Anthropic());\n * const response = await client.messages.create({\n * model: 'claude-sonnet-4-5-20250929',\n * messages: longConversation,\n * max_tokens: 1024,\n * });\n * ```\n */\nexport function withHeadroom<T extends AnthropicLike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.messages.create.bind(client.messages);\n\n const compressedCreate = async (params: any) => {\n const messages = params.messages;\n const model =\n options.model ?? params.model ?? \"claude-sonnet-4-5-20250929\";\n\n const openaiMessages = anthropicToOpenAI(messages);\n const result = await compress(openaiMessages, {\n stack: \"adapter_ts_anthropic\",\n ...options,\n model,\n });\n\n const anthropicMessages = result.compressed\n ? openAIToAnthropic(result.messages)\n : messages;\n\n return originalCreate({\n ...params,\n messages: anthropicMessages,\n });\n };\n\n const messagesProxy = new Proxy(client.messages, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"messages\") return messagesProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"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","file":"anthropic.js","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\nimport type { AssistantMessage, ToolCall } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface AnthropicLike {\n messages: {\n create: (params: any) => any;\n };\n [key: string]: any;\n}\n\n// Anthropic image block `source` -> OpenAI image_url url string, or null if unconvertible.\nfunction anthropicImageUrl(source: any): string | null {\n if (!source || typeof source !== \"object\") return null;\n if (source.type === \"base64\" && source.media_type && source.data) {\n return `data:${source.media_type};base64,${source.data}`;\n }\n if (source.type === \"url\" && typeof source.url === \"string\") return source.url;\n return null;\n}\n\n// OpenAI image_url url -> Anthropic image block. data: URIs become a base64 source; other urls a url source.\nfunction openAIImageBlock(url: string): any {\n const m = /^data:([^;,]+);base64,(.*)$/s.exec(url);\n if (m) return { type: \"image\", source: { type: \"base64\", media_type: m[1], data: m[2] } };\n return { type: \"image\", source: { type: \"url\", url } };\n}\n\n/**\n * Convert Anthropic messages to OpenAI format for compression.\n *\n * Anthropic format:\n * { role: 'user' | 'assistant', content: string | ContentBlock[] }\n * ContentBlock = { type: 'text', text } | { type: 'tool_use', ... } | { type: 'tool_result', ... }\n */\nfunction anthropicToOpenAI(messages: any[]): OpenAIMessage[] {\n const result: OpenAIMessage[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const toolResults = msg.content.filter(\n (b: any) => b.type === \"tool_result\",\n );\n const hasImages = msg.content.some((b: any) => b.type === \"image\");\n\n if (hasImages) {\n const parts = msg.content\n .filter((b: any) => b.type === \"text\" || b.type === \"image\")\n .map((b: any) => {\n if (b.type === \"text\") return { type: \"text\" as const, text: b.text };\n const url = anthropicImageUrl(b.source);\n return url ? { type: \"image_url\" as const, image_url: { url } } : null;\n })\n .filter((p: any): p is NonNullable<typeof p> => p !== null);\n\n if (parts.length > 0) {\n result.push({ role: \"user\", content: parts });\n }\n } else {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n if (textBlocks.length > 0) {\n result.push({\n role: \"user\",\n content: textBlocks.map((b: any) => b.text).join(\"\\n\"),\n });\n }\n }\n for (const tr of toolResults) {\n result.push({\n role: \"tool\",\n content:\n typeof tr.content === \"string\"\n ? tr.content\n : JSON.stringify(tr.content),\n tool_call_id: tr.tool_use_id,\n });\n }\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"assistant\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n const textBlocks = msg.content.filter((b: any) => b.type === \"text\");\n const toolUseBlocks = msg.content.filter(\n (b: any) => b.type === \"tool_use\",\n );\n\n const content =\n textBlocks.length > 0\n ? textBlocks.map((b: any) => b.text).join(\"\\n\")\n : null;\n\n const openaiMsg: AssistantMessage = { role: \"assistant\", content };\n if (toolUseBlocks.length > 0) {\n openaiMsg.tool_calls = toolUseBlocks.map(\n (b: any): ToolCall => ({\n id: b.id,\n type: \"function\",\n function: {\n name: b.name,\n arguments: JSON.stringify(b.input),\n },\n }),\n );\n }\n result.push(openaiMsg);\n }\n continue;\n }\n }\n\n return result;\n}\n\n/**\n * Convert compressed OpenAI messages back to Anthropic format.\n */\nfunction openAIToAnthropic(messages: OpenAIMessage[]): any[] {\n const result: any[] = [];\n\n for (const msg of messages) {\n if (msg.role === \"user\") {\n if (typeof msg.content === \"string\") {\n result.push({ role: \"user\", content: msg.content });\n } else if (Array.isArray(msg.content)) {\n result.push({\n role: \"user\",\n content: msg.content.map((p) => {\n if (p.type === \"text\") return { type: \"text\", text: p.text };\n if (p.type === \"image_url\") return openAIImageBlock(p.image_url.url);\n return { type: \"text\", text: \"\" };\n }),\n });\n }\n continue;\n }\n\n if (msg.role === \"assistant\") {\n const blocks: any[] = [];\n if (msg.content) blocks.push({ type: \"text\", text: msg.content });\n if (msg.tool_calls) {\n for (const tc of msg.tool_calls) {\n blocks.push({\n type: \"tool_use\",\n id: tc.id,\n name: tc.function.name,\n input: JSON.parse(tc.function.arguments),\n });\n }\n }\n result.push({\n role: \"assistant\",\n content:\n blocks.length === 1 && blocks[0].type === \"text\"\n ? blocks[0].text\n : blocks,\n });\n continue;\n }\n\n if (msg.role === \"tool\") {\n result.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: msg.tool_call_id,\n content: msg.content,\n },\n ],\n });\n continue;\n }\n\n if (msg.role === \"system\") {\n // System messages are handled separately in Anthropic (as `system` param)\n // Pass through as user message if it appears in the messages array\n result.push({ role: \"user\", content: msg.content });\n }\n }\n\n return result;\n}\n\n/**\n * Wrap an Anthropic client to auto-compress messages before each request.\n *\n * Intercepts `client.messages.create()` only. All other methods pass through.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/anthropic';\n * import Anthropic from '@anthropic-ai/sdk';\n *\n * const client = withHeadroom(new Anthropic());\n * const response = await client.messages.create({\n * model: 'claude-sonnet-4-5-20250929',\n * messages: longConversation,\n * max_tokens: 1024,\n * });\n * ```\n */\nexport function withHeadroom<T extends AnthropicLike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.messages.create.bind(client.messages);\n\n const compressedCreate = async (params: any) => {\n const messages = params.messages;\n const model =\n options.model ?? params.model ?? \"claude-sonnet-4-5-20250929\";\n\n const openaiMessages = anthropicToOpenAI(messages);\n const result = await compress(openaiMessages, {\n stack: \"adapter_ts_anthropic\",\n ...options,\n model,\n });\n\n const anthropicMessages = result.compressed\n ? openAIToAnthropic(result.messages)\n : messages;\n\n return originalCreate({\n ...params,\n messages: anthropicMessages,\n });\n };\n\n const messagesProxy = new Proxy(client.messages, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"messages\") return messagesProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}
|
package/dist/adapters/gemini.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk26N6BVLT_cjs = require('../chunk-26N6BVLT.cjs');
|
|
4
4
|
|
|
5
5
|
// src/adapters/gemini.ts
|
|
6
6
|
function withHeadroom(model, options = {}) {
|
|
@@ -9,7 +9,7 @@ function withHeadroom(model, options = {}) {
|
|
|
9
9
|
const compressedGenerate = async (params) => {
|
|
10
10
|
const contents = params.contents ?? params;
|
|
11
11
|
const modelName = options.model ?? "gemini-2.0-flash";
|
|
12
|
-
const result = await
|
|
12
|
+
const result = await chunk26N6BVLT_cjs.compress(
|
|
13
13
|
Array.isArray(contents) ? contents : [contents],
|
|
14
14
|
{ stack: "adapter_ts_gemini", ...options, model: modelName }
|
|
15
15
|
);
|
|
@@ -19,7 +19,7 @@ function withHeadroom(model, options = {}) {
|
|
|
19
19
|
const compressedStream = originalStream ? async (params) => {
|
|
20
20
|
const contents = params.contents ?? params;
|
|
21
21
|
const modelName = options.model ?? "gemini-2.0-flash";
|
|
22
|
-
const result = await
|
|
22
|
+
const result = await chunk26N6BVLT_cjs.compress(
|
|
23
23
|
Array.isArray(contents) ? contents : [contents],
|
|
24
24
|
{ stack: "adapter_ts_gemini", ...options, model: modelName }
|
|
25
25
|
);
|
package/dist/adapters/gemini.js
CHANGED
package/dist/adapters/openai.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk26N6BVLT_cjs = require('../chunk-26N6BVLT.cjs');
|
|
4
4
|
|
|
5
5
|
// src/adapters/openai.ts
|
|
6
6
|
function withHeadroom(client, options = {}) {
|
|
@@ -10,7 +10,7 @@ function withHeadroom(client, options = {}) {
|
|
|
10
10
|
const compressedCreate = async (params) => {
|
|
11
11
|
const messages = params.messages;
|
|
12
12
|
const model = options.model ?? params.model ?? "gpt-4o";
|
|
13
|
-
const result = await
|
|
13
|
+
const result = await chunk26N6BVLT_cjs.compress(messages, {
|
|
14
14
|
stack: "adapter_ts_openai",
|
|
15
15
|
...options,
|
|
16
16
|
model
|
package/dist/adapters/openai.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunk26N6BVLT_cjs = require('../chunk-26N6BVLT.cjs');
|
|
4
4
|
var module$1 = require('module');
|
|
5
5
|
|
|
6
6
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -12,25 +12,25 @@ function headroomMiddleware(options = {}) {
|
|
|
12
12
|
const prompt = params.prompt;
|
|
13
13
|
if (!prompt || prompt.length === 0) return params;
|
|
14
14
|
const model = options.model ?? params.modelId ?? "gpt-4o";
|
|
15
|
-
const openaiMessages =
|
|
16
|
-
const result = await
|
|
15
|
+
const openaiMessages = chunk26N6BVLT_cjs.vercelToOpenAI(prompt);
|
|
16
|
+
const result = await chunk26N6BVLT_cjs.compress(openaiMessages, {
|
|
17
17
|
stack: "adapter_ts_vercel_ai",
|
|
18
18
|
...options,
|
|
19
19
|
model
|
|
20
20
|
});
|
|
21
21
|
if (!result.compressed) return params;
|
|
22
|
-
const compressedPrompt =
|
|
22
|
+
const compressedPrompt = chunk26N6BVLT_cjs.openAIToVercel(result.messages);
|
|
23
23
|
return { ...params, prompt: compressedPrompt };
|
|
24
24
|
}
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
async function compressVercelMessages(messages, options = {}) {
|
|
28
|
-
const openaiMessages =
|
|
29
|
-
const result = await
|
|
28
|
+
const openaiMessages = chunk26N6BVLT_cjs.vercelToOpenAI(messages);
|
|
29
|
+
const result = await chunk26N6BVLT_cjs.compress(openaiMessages, {
|
|
30
30
|
stack: "adapter_ts_vercel_ai",
|
|
31
31
|
...options
|
|
32
32
|
});
|
|
33
|
-
const vercelMessages =
|
|
33
|
+
const vercelMessages = chunk26N6BVLT_cjs.openAIToVercel(result.messages);
|
|
34
34
|
return {
|
|
35
35
|
...result,
|
|
36
36
|
messages: vercelMessages
|
|
@@ -130,10 +130,9 @@ async function* parseSSE(response) {
|
|
|
130
130
|
try {
|
|
131
131
|
while (true) {
|
|
132
132
|
const { done, value } = await reader.read();
|
|
133
|
-
|
|
134
|
-
buffer += decoder.decode(value, { stream: true });
|
|
133
|
+
buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
|
|
135
134
|
const lines = buffer.split("\n");
|
|
136
|
-
buffer = lines.pop();
|
|
135
|
+
buffer = done ? "" : lines.pop();
|
|
137
136
|
for (const line of lines) {
|
|
138
137
|
if (line.startsWith("data: ")) {
|
|
139
138
|
const data = line.slice(6).trim();
|
|
@@ -144,6 +143,7 @@ async function* parseSSE(response) {
|
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
}
|
|
146
|
+
if (done) break;
|
|
147
147
|
}
|
|
148
148
|
} finally {
|
|
149
149
|
reader.releaseLock();
|
|
@@ -301,7 +301,7 @@ var HeadroomClient = class {
|
|
|
301
301
|
const maxAttempts = 1 + this.retries;
|
|
302
302
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
303
303
|
try {
|
|
304
|
-
return await this._doCompress(messages, model, options.tokenBudget);
|
|
304
|
+
return await this._doCompress(messages, model, options.tokenBudget, options.config);
|
|
305
305
|
} catch (error) {
|
|
306
306
|
lastError = error;
|
|
307
307
|
if (error instanceof HeadroomAuthError) throw error;
|
|
@@ -518,6 +518,7 @@ var HeadroomClient = class {
|
|
|
518
518
|
// ============================================================
|
|
519
519
|
/**
|
|
520
520
|
* Raw fetch with proxy base URL, auth, and timeout.
|
|
521
|
+
* A string body is sent as-is; anything else is JSON-serialized.
|
|
521
522
|
* @internal
|
|
522
523
|
*/
|
|
523
524
|
async rawFetch(path, options) {
|
|
@@ -527,6 +528,7 @@ var HeadroomClient = class {
|
|
|
527
528
|
...options.headers
|
|
528
529
|
};
|
|
529
530
|
if (this.apiKey) {
|
|
531
|
+
headers["X-Headroom-Proxy-Token"] = this.apiKey;
|
|
530
532
|
if (!headers["Authorization"] && !headers["x-api-key"]) {
|
|
531
533
|
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
532
534
|
}
|
|
@@ -539,7 +541,7 @@ var HeadroomClient = class {
|
|
|
539
541
|
response = await fetch(url, {
|
|
540
542
|
method: options.method,
|
|
541
543
|
headers,
|
|
542
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
544
|
+
body: typeof options.body === "string" ? options.body : options.body ? JSON.stringify(options.body) : void 0,
|
|
543
545
|
signal: AbortSignal.timeout(this.timeout)
|
|
544
546
|
});
|
|
545
547
|
} catch (error) {
|
|
@@ -561,53 +563,18 @@ var HeadroomClient = class {
|
|
|
561
563
|
}
|
|
562
564
|
return response;
|
|
563
565
|
}
|
|
564
|
-
/** @internal */
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
const headers = {
|
|
568
|
-
"Content-Type": "application/json",
|
|
569
|
-
...init.headers
|
|
570
|
-
};
|
|
571
|
-
if (this.apiKey) {
|
|
572
|
-
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
573
|
-
}
|
|
574
|
-
if (this.stack && !headers["X-Headroom-Stack"]) {
|
|
575
|
-
headers["X-Headroom-Stack"] = this.stack;
|
|
576
|
-
}
|
|
577
|
-
let response;
|
|
578
|
-
try {
|
|
579
|
-
response = await fetch(url, {
|
|
580
|
-
method: init.method,
|
|
581
|
-
headers,
|
|
582
|
-
body: init.body,
|
|
583
|
-
signal: AbortSignal.timeout(this.timeout)
|
|
584
|
-
});
|
|
585
|
-
} catch (error) {
|
|
586
|
-
throw new HeadroomConnectionError(
|
|
587
|
-
`Failed to connect to Headroom at ${this.baseUrl}: ${error}`
|
|
588
|
-
);
|
|
589
|
-
}
|
|
590
|
-
if (!response.ok) {
|
|
591
|
-
let errorBody;
|
|
592
|
-
try {
|
|
593
|
-
errorBody = await response.json();
|
|
594
|
-
} catch {
|
|
595
|
-
}
|
|
596
|
-
throw mapProxyError(
|
|
597
|
-
response.status,
|
|
598
|
-
errorBody?.error?.type ?? "unknown",
|
|
599
|
-
errorBody?.error?.message ?? `HTTP ${response.status}`
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
return response;
|
|
566
|
+
/** Proxy-only endpoints, whose bodies are already serialized. @internal */
|
|
567
|
+
_fetch(path, init) {
|
|
568
|
+
return this.rawFetch(path, init);
|
|
603
569
|
}
|
|
604
|
-
async _doCompress(messages, model, tokenBudget) {
|
|
570
|
+
async _doCompress(messages, model, tokenBudget, config) {
|
|
605
571
|
const body = { messages, model };
|
|
606
572
|
if (tokenBudget) {
|
|
607
573
|
body.token_budget = tokenBudget;
|
|
608
574
|
}
|
|
609
|
-
|
|
610
|
-
|
|
575
|
+
const mergedConfig = { ...this.config, ...config };
|
|
576
|
+
if (Object.keys(mergedConfig).length > 0) {
|
|
577
|
+
body.config = deepSnakeCase(mergedConfig);
|
|
611
578
|
}
|
|
612
579
|
const response = await this._fetch("/v1/compress", {
|
|
613
580
|
method: "POST",
|
|
@@ -644,6 +611,19 @@ function detectFormat(messages) {
|
|
|
644
611
|
}
|
|
645
612
|
return "openai";
|
|
646
613
|
}
|
|
614
|
+
function anthropicImageUrl(source) {
|
|
615
|
+
if (!source || typeof source !== "object") return null;
|
|
616
|
+
if (source.type === "base64" && source.media_type && source.data) {
|
|
617
|
+
return `data:${source.media_type};base64,${source.data}`;
|
|
618
|
+
}
|
|
619
|
+
if (source.type === "url" && typeof source.url === "string") return source.url;
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
function openAIImageBlock(url) {
|
|
623
|
+
const m = /^data:([^;,]+);base64,(.*)$/s.exec(url);
|
|
624
|
+
if (m) return { type: "image", source: { type: "base64", media_type: m[1], data: m[2] } };
|
|
625
|
+
return { type: "image", source: { type: "url", url } };
|
|
626
|
+
}
|
|
647
627
|
function anthropicToOpenAI(messages) {
|
|
648
628
|
const result = [];
|
|
649
629
|
for (const msg of messages) {
|
|
@@ -653,13 +633,25 @@ function anthropicToOpenAI(messages) {
|
|
|
653
633
|
continue;
|
|
654
634
|
}
|
|
655
635
|
if (Array.isArray(msg.content)) {
|
|
656
|
-
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
657
636
|
const toolResults = msg.content.filter((b) => b.type === "tool_result");
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
637
|
+
const hasImages = msg.content.some((b) => b.type === "image");
|
|
638
|
+
if (hasImages) {
|
|
639
|
+
const parts = msg.content.filter((b) => b.type === "text" || b.type === "image").map((b) => {
|
|
640
|
+
if (b.type === "text") return { type: "text", text: b.text };
|
|
641
|
+
const url = anthropicImageUrl(b.source);
|
|
642
|
+
return url ? { type: "image_url", image_url: { url } } : null;
|
|
643
|
+
}).filter((p) => p !== null);
|
|
644
|
+
if (parts.length > 0) {
|
|
645
|
+
result.push({ role: "user", content: parts });
|
|
646
|
+
}
|
|
647
|
+
} else {
|
|
648
|
+
const textBlocks = msg.content.filter((b) => b.type === "text");
|
|
649
|
+
if (textBlocks.length > 0) {
|
|
650
|
+
result.push({
|
|
651
|
+
role: "user",
|
|
652
|
+
content: textBlocks.map((b) => b.text).join("\n")
|
|
653
|
+
});
|
|
654
|
+
}
|
|
663
655
|
}
|
|
664
656
|
for (const tr of toolResults) {
|
|
665
657
|
const content = typeof tr.content === "string" ? tr.content : Array.isArray(tr.content) ? tr.content.map((b) => b.text ?? JSON.stringify(b)).join("\n") : JSON.stringify(tr.content);
|
|
@@ -712,9 +704,11 @@ function openAIToAnthropic(messages) {
|
|
|
712
704
|
} else if (Array.isArray(msg.content)) {
|
|
713
705
|
result.push({
|
|
714
706
|
role: "user",
|
|
715
|
-
content: msg.content.map(
|
|
716
|
-
(p
|
|
717
|
-
|
|
707
|
+
content: msg.content.map((p) => {
|
|
708
|
+
if (p.type === "text") return { type: "text", text: p.text };
|
|
709
|
+
if (p.type === "image_url") return openAIImageBlock(p.image_url.url);
|
|
710
|
+
return { type: "text", text: "" };
|
|
711
|
+
})
|
|
718
712
|
});
|
|
719
713
|
}
|
|
720
714
|
continue;
|
|
@@ -1063,6 +1057,7 @@ async function compress(messages, options = {}) {
|
|
|
1063
1057
|
model,
|
|
1064
1058
|
tokenBudget,
|
|
1065
1059
|
hooks,
|
|
1060
|
+
config,
|
|
1066
1061
|
...clientOptions
|
|
1067
1062
|
} = options;
|
|
1068
1063
|
const ctx = {
|
|
@@ -1082,7 +1077,7 @@ async function compress(messages, options = {}) {
|
|
|
1082
1077
|
await hooks.computeBiases(openaiMessages, ctx);
|
|
1083
1078
|
}
|
|
1084
1079
|
const client = providedClient ?? new HeadroomClient(clientOptions);
|
|
1085
|
-
const result = await client.compress(openaiMessages, { model, tokenBudget });
|
|
1080
|
+
const result = await client.compress(openaiMessages, { model, tokenBudget, config });
|
|
1086
1081
|
const outputMessages = fromOpenAI(result.messages, inputFormat);
|
|
1087
1082
|
const finalResult = {
|
|
1088
1083
|
...result,
|
|
@@ -1106,5 +1101,5 @@ async function compress(messages, options = {}) {
|
|
|
1106
1101
|
}
|
|
1107
1102
|
|
|
1108
1103
|
export { CacheError, CompressionHooks, ConfigurationError, HeadroomAuthError, HeadroomClient, HeadroomCompressError, HeadroomConnectionError, HeadroomError, ProviderError, StorageError, TokenizationError, TransformError, ValidationError, __require, camelToSnake, collectStream, compress, countTurns, deepCamelCase, deepSnakeCase, detectFormat, extractToolCalls, extractUserQuery, fromOpenAI, mapProxyError, openAIToVercel, parseSSE, snakeToCamel, toOpenAI, vercelToOpenAI };
|
|
1109
|
-
//# sourceMappingURL=chunk-
|
|
1110
|
-
//# sourceMappingURL=chunk-
|
|
1104
|
+
//# sourceMappingURL=chunk-24W6JPRV.js.map
|
|
1105
|
+
//# sourceMappingURL=chunk-24W6JPRV.js.map
|