headroom-ai 0.1.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/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # headroom-ai
2
+
3
+ Compress LLM context. Save tokens. Fit more into every request.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install headroom-ai
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { compress } from 'headroom-ai';
15
+
16
+ const result = await compress(messages, { model: 'gpt-4o' });
17
+ console.log(`Saved ${result.tokensSaved} tokens (${((1 - result.compressionRatio) * 100).toFixed(0)}%)`);
18
+
19
+ // Use compressed messages with any LLM client
20
+ const response = await openai.chat.completions.create({
21
+ model: 'gpt-4o',
22
+ messages: result.messages,
23
+ });
24
+ ```
25
+
26
+ Requires a running Headroom proxy (`headroom proxy`) or Headroom Cloud API key.
27
+
28
+ ## Framework Adapters
29
+
30
+ ### Vercel AI SDK
31
+
32
+ ```typescript
33
+ import { headroomMiddleware } from 'headroom-ai/vercel-ai';
34
+ import { wrapLanguageModel, generateText } from 'ai';
35
+ import { openai } from '@ai-sdk/openai';
36
+
37
+ const model = wrapLanguageModel({
38
+ model: openai('gpt-4o'),
39
+ middleware: headroomMiddleware(),
40
+ });
41
+
42
+ const { text } = await generateText({ model, messages });
43
+ ```
44
+
45
+ ### OpenAI SDK
46
+
47
+ ```typescript
48
+ import { withHeadroom } from 'headroom-ai/openai';
49
+ import OpenAI from 'openai';
50
+
51
+ const client = withHeadroom(new OpenAI());
52
+ const response = await client.chat.completions.create({
53
+ model: 'gpt-4o',
54
+ messages: longConversation,
55
+ });
56
+ ```
57
+
58
+ ### Anthropic SDK
59
+
60
+ ```typescript
61
+ import { withHeadroom } from 'headroom-ai/anthropic';
62
+ import Anthropic from '@anthropic-ai/sdk';
63
+
64
+ const client = withHeadroom(new Anthropic());
65
+ const response = await client.messages.create({
66
+ model: 'claude-sonnet-4-5-20250929',
67
+ messages: longConversation,
68
+ max_tokens: 1024,
69
+ });
70
+ ```
71
+
72
+ ## Configuration
73
+
74
+ ```typescript
75
+ import { compress } from 'headroom-ai';
76
+
77
+ const result = await compress(messages, {
78
+ model: 'gpt-4o',
79
+ baseUrl: 'http://localhost:8787', // or https://api.headroom.ai
80
+ apiKey: 'hr_...', // for Headroom Cloud
81
+ timeout: 30000, // ms
82
+ fallback: true, // return uncompressed if proxy is down (default)
83
+ retries: 1, // retry on transient failures (default)
84
+ });
85
+ ```
86
+
87
+ Or use environment variables:
88
+ - `HEADROOM_BASE_URL` — proxy/cloud URL
89
+ - `HEADROOM_API_KEY` — Cloud API key
90
+
91
+ ## Reusable Client
92
+
93
+ ```typescript
94
+ import { HeadroomClient } from 'headroom-ai';
95
+
96
+ const client = new HeadroomClient({
97
+ baseUrl: 'http://localhost:8787',
98
+ apiKey: 'hr_...',
99
+ });
100
+
101
+ // Reuse across many calls
102
+ const r1 = await client.compress(messages1, { model: 'gpt-4o' });
103
+ const r2 = await client.compress(messages2, { model: 'gpt-4o' });
104
+ ```
105
+
106
+ ## License
107
+
108
+ Apache-2.0
@@ -0,0 +1,146 @@
1
+ 'use strict';
2
+
3
+ var chunk3N25JEWK_cjs = require('../chunk-3N25JEWK.cjs');
4
+
5
+ // src/adapters/anthropic.ts
6
+ function anthropicToOpenAI(messages) {
7
+ const result = [];
8
+ for (const msg of messages) {
9
+ if (msg.role === "user") {
10
+ if (typeof msg.content === "string") {
11
+ result.push({ role: "user", content: msg.content });
12
+ } else if (Array.isArray(msg.content)) {
13
+ const toolResults = msg.content.filter(
14
+ (b) => b.type === "tool_result"
15
+ );
16
+ const textBlocks = msg.content.filter((b) => b.type === "text");
17
+ if (textBlocks.length > 0) {
18
+ result.push({
19
+ role: "user",
20
+ content: textBlocks.map((b) => b.text).join("\n")
21
+ });
22
+ }
23
+ for (const tr of toolResults) {
24
+ result.push({
25
+ role: "tool",
26
+ content: typeof tr.content === "string" ? tr.content : JSON.stringify(tr.content),
27
+ tool_call_id: tr.tool_use_id
28
+ });
29
+ }
30
+ }
31
+ continue;
32
+ }
33
+ if (msg.role === "assistant") {
34
+ if (typeof msg.content === "string") {
35
+ result.push({ role: "assistant", content: msg.content });
36
+ } else if (Array.isArray(msg.content)) {
37
+ const textBlocks = msg.content.filter((b) => b.type === "text");
38
+ const toolUseBlocks = msg.content.filter(
39
+ (b) => b.type === "tool_use"
40
+ );
41
+ const content = textBlocks.length > 0 ? textBlocks.map((b) => b.text).join("\n") : null;
42
+ const openaiMsg = { role: "assistant", content };
43
+ if (toolUseBlocks.length > 0) {
44
+ openaiMsg.tool_calls = toolUseBlocks.map(
45
+ (b) => ({
46
+ id: b.id,
47
+ type: "function",
48
+ function: {
49
+ name: b.name,
50
+ arguments: JSON.stringify(b.input)
51
+ }
52
+ })
53
+ );
54
+ }
55
+ result.push(openaiMsg);
56
+ }
57
+ continue;
58
+ }
59
+ }
60
+ return result;
61
+ }
62
+ function openAIToAnthropic(messages) {
63
+ const result = [];
64
+ for (const msg of messages) {
65
+ if (msg.role === "user") {
66
+ if (typeof msg.content === "string") {
67
+ result.push({ role: "user", content: msg.content });
68
+ } else if (Array.isArray(msg.content)) {
69
+ result.push({
70
+ role: "user",
71
+ content: msg.content.map((p) => {
72
+ if (p.type === "text") return { type: "text", text: p.text };
73
+ return { type: "text", text: "" };
74
+ })
75
+ });
76
+ }
77
+ continue;
78
+ }
79
+ if (msg.role === "assistant") {
80
+ const blocks = [];
81
+ if (msg.content) blocks.push({ type: "text", text: msg.content });
82
+ if (msg.tool_calls) {
83
+ for (const tc of msg.tool_calls) {
84
+ blocks.push({
85
+ type: "tool_use",
86
+ id: tc.id,
87
+ name: tc.function.name,
88
+ input: JSON.parse(tc.function.arguments)
89
+ });
90
+ }
91
+ }
92
+ result.push({
93
+ role: "assistant",
94
+ content: blocks.length === 1 && blocks[0].type === "text" ? blocks[0].text : blocks
95
+ });
96
+ continue;
97
+ }
98
+ if (msg.role === "tool") {
99
+ result.push({
100
+ role: "user",
101
+ content: [
102
+ {
103
+ type: "tool_result",
104
+ tool_use_id: msg.tool_call_id,
105
+ content: msg.content
106
+ }
107
+ ]
108
+ });
109
+ continue;
110
+ }
111
+ if (msg.role === "system") {
112
+ result.push({ role: "user", content: msg.content });
113
+ }
114
+ }
115
+ return result;
116
+ }
117
+ function withHeadroom(client, options = {}) {
118
+ const originalCreate = client.messages.create.bind(client.messages);
119
+ const compressedCreate = async (params) => {
120
+ const messages = params.messages;
121
+ const model = options.model ?? params.model ?? "claude-sonnet-4-5-20250929";
122
+ const openaiMessages = anthropicToOpenAI(messages);
123
+ const result = await chunk3N25JEWK_cjs.compress(openaiMessages, { ...options, model });
124
+ const anthropicMessages = result.compressed ? openAIToAnthropic(result.messages) : messages;
125
+ return originalCreate({
126
+ ...params,
127
+ messages: anthropicMessages
128
+ });
129
+ };
130
+ const messagesProxy = new Proxy(client.messages, {
131
+ get(target, prop) {
132
+ if (prop === "create") return compressedCreate;
133
+ return target[prop];
134
+ }
135
+ });
136
+ return new Proxy(client, {
137
+ get(target, prop) {
138
+ if (prop === "messages") return messagesProxy;
139
+ return target[prop];
140
+ }
141
+ });
142
+ }
143
+
144
+ exports.withHeadroom = withHeadroom;
145
+ //# sourceMappingURL=anthropic.cjs.map
146
+ //# sourceMappingURL=anthropic.cjs.map
@@ -0,0 +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, { ...options, model });\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"]}
@@ -0,0 +1,29 @@
1
+ import { C as CompressOptions } from '../types-DQtcLXq3.cjs';
2
+
3
+ interface AnthropicLike {
4
+ messages: {
5
+ create: (params: any) => any;
6
+ };
7
+ [key: string]: any;
8
+ }
9
+ /**
10
+ * Wrap an Anthropic client to auto-compress messages before each request.
11
+ *
12
+ * Intercepts `client.messages.create()` only. All other methods pass through.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { withHeadroom } from 'headroom-ai/anthropic';
17
+ * import Anthropic from '@anthropic-ai/sdk';
18
+ *
19
+ * const client = withHeadroom(new Anthropic());
20
+ * const response = await client.messages.create({
21
+ * model: 'claude-sonnet-4-5-20250929',
22
+ * messages: longConversation,
23
+ * max_tokens: 1024,
24
+ * });
25
+ * ```
26
+ */
27
+ declare function withHeadroom<T extends AnthropicLike>(client: T, options?: CompressOptions): T;
28
+
29
+ export { withHeadroom };
@@ -0,0 +1,29 @@
1
+ import { C as CompressOptions } from '../types-DQtcLXq3.js';
2
+
3
+ interface AnthropicLike {
4
+ messages: {
5
+ create: (params: any) => any;
6
+ };
7
+ [key: string]: any;
8
+ }
9
+ /**
10
+ * Wrap an Anthropic client to auto-compress messages before each request.
11
+ *
12
+ * Intercepts `client.messages.create()` only. All other methods pass through.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { withHeadroom } from 'headroom-ai/anthropic';
17
+ * import Anthropic from '@anthropic-ai/sdk';
18
+ *
19
+ * const client = withHeadroom(new Anthropic());
20
+ * const response = await client.messages.create({
21
+ * model: 'claude-sonnet-4-5-20250929',
22
+ * messages: longConversation,
23
+ * max_tokens: 1024,
24
+ * });
25
+ * ```
26
+ */
27
+ declare function withHeadroom<T extends AnthropicLike>(client: T, options?: CompressOptions): T;
28
+
29
+ export { withHeadroom };
@@ -0,0 +1,144 @@
1
+ import { compress } from '../chunk-YTTW7S2Q.js';
2
+
3
+ // src/adapters/anthropic.ts
4
+ function anthropicToOpenAI(messages) {
5
+ const result = [];
6
+ for (const msg of messages) {
7
+ if (msg.role === "user") {
8
+ if (typeof msg.content === "string") {
9
+ result.push({ role: "user", content: msg.content });
10
+ } else if (Array.isArray(msg.content)) {
11
+ const toolResults = msg.content.filter(
12
+ (b) => b.type === "tool_result"
13
+ );
14
+ const textBlocks = msg.content.filter((b) => b.type === "text");
15
+ if (textBlocks.length > 0) {
16
+ result.push({
17
+ role: "user",
18
+ content: textBlocks.map((b) => b.text).join("\n")
19
+ });
20
+ }
21
+ for (const tr of toolResults) {
22
+ result.push({
23
+ role: "tool",
24
+ content: typeof tr.content === "string" ? tr.content : JSON.stringify(tr.content),
25
+ tool_call_id: tr.tool_use_id
26
+ });
27
+ }
28
+ }
29
+ continue;
30
+ }
31
+ if (msg.role === "assistant") {
32
+ if (typeof msg.content === "string") {
33
+ result.push({ role: "assistant", content: msg.content });
34
+ } else if (Array.isArray(msg.content)) {
35
+ const textBlocks = msg.content.filter((b) => b.type === "text");
36
+ const toolUseBlocks = msg.content.filter(
37
+ (b) => b.type === "tool_use"
38
+ );
39
+ const content = textBlocks.length > 0 ? textBlocks.map((b) => b.text).join("\n") : null;
40
+ const openaiMsg = { role: "assistant", content };
41
+ if (toolUseBlocks.length > 0) {
42
+ openaiMsg.tool_calls = toolUseBlocks.map(
43
+ (b) => ({
44
+ id: b.id,
45
+ type: "function",
46
+ function: {
47
+ name: b.name,
48
+ arguments: JSON.stringify(b.input)
49
+ }
50
+ })
51
+ );
52
+ }
53
+ result.push(openaiMsg);
54
+ }
55
+ continue;
56
+ }
57
+ }
58
+ return result;
59
+ }
60
+ function openAIToAnthropic(messages) {
61
+ const result = [];
62
+ for (const msg of messages) {
63
+ if (msg.role === "user") {
64
+ if (typeof msg.content === "string") {
65
+ result.push({ role: "user", content: msg.content });
66
+ } else if (Array.isArray(msg.content)) {
67
+ result.push({
68
+ role: "user",
69
+ content: msg.content.map((p) => {
70
+ if (p.type === "text") return { type: "text", text: p.text };
71
+ return { type: "text", text: "" };
72
+ })
73
+ });
74
+ }
75
+ continue;
76
+ }
77
+ if (msg.role === "assistant") {
78
+ const blocks = [];
79
+ if (msg.content) blocks.push({ type: "text", text: msg.content });
80
+ if (msg.tool_calls) {
81
+ for (const tc of msg.tool_calls) {
82
+ blocks.push({
83
+ type: "tool_use",
84
+ id: tc.id,
85
+ name: tc.function.name,
86
+ input: JSON.parse(tc.function.arguments)
87
+ });
88
+ }
89
+ }
90
+ result.push({
91
+ role: "assistant",
92
+ content: blocks.length === 1 && blocks[0].type === "text" ? blocks[0].text : blocks
93
+ });
94
+ continue;
95
+ }
96
+ if (msg.role === "tool") {
97
+ result.push({
98
+ role: "user",
99
+ content: [
100
+ {
101
+ type: "tool_result",
102
+ tool_use_id: msg.tool_call_id,
103
+ content: msg.content
104
+ }
105
+ ]
106
+ });
107
+ continue;
108
+ }
109
+ if (msg.role === "system") {
110
+ result.push({ role: "user", content: msg.content });
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+ function withHeadroom(client, options = {}) {
116
+ const originalCreate = client.messages.create.bind(client.messages);
117
+ const compressedCreate = async (params) => {
118
+ const messages = params.messages;
119
+ const model = options.model ?? params.model ?? "claude-sonnet-4-5-20250929";
120
+ const openaiMessages = anthropicToOpenAI(messages);
121
+ const result = await compress(openaiMessages, { ...options, model });
122
+ const anthropicMessages = result.compressed ? openAIToAnthropic(result.messages) : messages;
123
+ return originalCreate({
124
+ ...params,
125
+ messages: anthropicMessages
126
+ });
127
+ };
128
+ const messagesProxy = new Proxy(client.messages, {
129
+ get(target, prop) {
130
+ if (prop === "create") return compressedCreate;
131
+ return target[prop];
132
+ }
133
+ });
134
+ return new Proxy(client, {
135
+ get(target, prop) {
136
+ if (prop === "messages") return messagesProxy;
137
+ return target[prop];
138
+ }
139
+ });
140
+ }
141
+
142
+ export { withHeadroom };
143
+ //# sourceMappingURL=anthropic.js.map
144
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +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, { ...options, model });\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"]}
@@ -0,0 +1,41 @@
1
+ 'use strict';
2
+
3
+ var chunk3N25JEWK_cjs = require('../chunk-3N25JEWK.cjs');
4
+
5
+ // src/adapters/openai.ts
6
+ function withHeadroom(client, options = {}) {
7
+ const originalCreate = client.chat.completions.create.bind(
8
+ client.chat.completions
9
+ );
10
+ const compressedCreate = async (params) => {
11
+ const messages = params.messages;
12
+ const model = options.model ?? params.model ?? "gpt-4o";
13
+ const result = await chunk3N25JEWK_cjs.compress(messages, { ...options, model });
14
+ return originalCreate({
15
+ ...params,
16
+ messages: result.messages
17
+ });
18
+ };
19
+ const completionsProxy = new Proxy(client.chat.completions, {
20
+ get(target, prop) {
21
+ if (prop === "create") return compressedCreate;
22
+ return target[prop];
23
+ }
24
+ });
25
+ const chatProxy = new Proxy(client.chat, {
26
+ get(target, prop) {
27
+ if (prop === "completions") return completionsProxy;
28
+ return target[prop];
29
+ }
30
+ });
31
+ return new Proxy(client, {
32
+ get(target, prop) {
33
+ if (prop === "chat") return chatProxy;
34
+ return target[prop];
35
+ }
36
+ });
37
+ }
38
+
39
+ exports.withHeadroom = withHeadroom;
40
+ //# sourceMappingURL=openai.cjs.map
41
+ //# sourceMappingURL=openai.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/openai.ts"],"names":["compress"],"mappings":";;;;;AAgCO,SAAS,YAAA,CACd,MAAA,EACA,OAAA,GAA2B,EAAC,EACzB;AACH,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,IAAA;AAAA,IACpD,OAAO,IAAA,CAAK;AAAA,GACd;AAEA,EAAA,MAAM,gBAAA,GAAmB,OAAO,MAAA,KAAgB;AAC9C,IAAA,MAAM,WAA4B,MAAA,CAAO,QAAA;AACzC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,MAAA,CAAO,KAAA,IAAS,QAAA;AAE/C,IAAA,MAAM,MAAA,GAAS,MAAMA,0BAAA,CAAS,QAAA,EAAU,EAAE,GAAG,OAAA,EAAS,OAAO,CAAA;AAE7D,IAAA,OAAO,cAAA,CAAe;AAAA,MACpB,GAAG,MAAA;AAAA,MACH,UAAU,MAAA,CAAO;AAAA,KAClB,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,WAAA,EAAa;AAAA,IAC1D,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,UAAU,OAAO,gBAAA;AAC9B,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AAED,EAAA,MAAM,SAAA,GAAY,IAAI,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM;AAAA,IACvC,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,eAAe,OAAO,gBAAA;AACnC,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AAED,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,SAAA;AAC5B,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AACH","file":"openai.cjs","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface OpenAILike {\n chat: {\n completions: {\n create: (params: any) => any;\n };\n };\n [key: string]: any;\n}\n\n/**\n * Wrap an OpenAI client to auto-compress messages before each request.\n *\n * Intercepts `client.chat.completions.create()` only. All other methods\n * (embeddings, images, audio, etc.) pass through unchanged.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/openai';\n * import OpenAI from 'openai';\n *\n * const client = withHeadroom(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: 'gpt-4o',\n * messages: longConversation,\n * });\n * ```\n */\nexport function withHeadroom<T extends OpenAILike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.chat.completions.create.bind(\n client.chat.completions,\n );\n\n const compressedCreate = async (params: any) => {\n const messages: OpenAIMessage[] = params.messages;\n const model = options.model ?? params.model ?? \"gpt-4o\";\n\n const result = await compress(messages, { ...options, model });\n\n return originalCreate({\n ...params,\n messages: result.messages,\n });\n };\n\n const completionsProxy = new Proxy(client.chat.completions, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n const chatProxy = new Proxy(client.chat, {\n get(target, prop) {\n if (prop === \"completions\") return completionsProxy;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"chat\") return chatProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}
@@ -0,0 +1,31 @@
1
+ import { C as CompressOptions } from '../types-DQtcLXq3.cjs';
2
+
3
+ interface OpenAILike {
4
+ chat: {
5
+ completions: {
6
+ create: (params: any) => any;
7
+ };
8
+ };
9
+ [key: string]: any;
10
+ }
11
+ /**
12
+ * Wrap an OpenAI client to auto-compress messages before each request.
13
+ *
14
+ * Intercepts `client.chat.completions.create()` only. All other methods
15
+ * (embeddings, images, audio, etc.) pass through unchanged.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { withHeadroom } from 'headroom-ai/openai';
20
+ * import OpenAI from 'openai';
21
+ *
22
+ * const client = withHeadroom(new OpenAI());
23
+ * const response = await client.chat.completions.create({
24
+ * model: 'gpt-4o',
25
+ * messages: longConversation,
26
+ * });
27
+ * ```
28
+ */
29
+ declare function withHeadroom<T extends OpenAILike>(client: T, options?: CompressOptions): T;
30
+
31
+ export { withHeadroom };
@@ -0,0 +1,31 @@
1
+ import { C as CompressOptions } from '../types-DQtcLXq3.js';
2
+
3
+ interface OpenAILike {
4
+ chat: {
5
+ completions: {
6
+ create: (params: any) => any;
7
+ };
8
+ };
9
+ [key: string]: any;
10
+ }
11
+ /**
12
+ * Wrap an OpenAI client to auto-compress messages before each request.
13
+ *
14
+ * Intercepts `client.chat.completions.create()` only. All other methods
15
+ * (embeddings, images, audio, etc.) pass through unchanged.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { withHeadroom } from 'headroom-ai/openai';
20
+ * import OpenAI from 'openai';
21
+ *
22
+ * const client = withHeadroom(new OpenAI());
23
+ * const response = await client.chat.completions.create({
24
+ * model: 'gpt-4o',
25
+ * messages: longConversation,
26
+ * });
27
+ * ```
28
+ */
29
+ declare function withHeadroom<T extends OpenAILike>(client: T, options?: CompressOptions): T;
30
+
31
+ export { withHeadroom };
@@ -0,0 +1,39 @@
1
+ import { compress } from '../chunk-YTTW7S2Q.js';
2
+
3
+ // src/adapters/openai.ts
4
+ function withHeadroom(client, options = {}) {
5
+ const originalCreate = client.chat.completions.create.bind(
6
+ client.chat.completions
7
+ );
8
+ const compressedCreate = async (params) => {
9
+ const messages = params.messages;
10
+ const model = options.model ?? params.model ?? "gpt-4o";
11
+ const result = await compress(messages, { ...options, model });
12
+ return originalCreate({
13
+ ...params,
14
+ messages: result.messages
15
+ });
16
+ };
17
+ const completionsProxy = new Proxy(client.chat.completions, {
18
+ get(target, prop) {
19
+ if (prop === "create") return compressedCreate;
20
+ return target[prop];
21
+ }
22
+ });
23
+ const chatProxy = new Proxy(client.chat, {
24
+ get(target, prop) {
25
+ if (prop === "completions") return completionsProxy;
26
+ return target[prop];
27
+ }
28
+ });
29
+ return new Proxy(client, {
30
+ get(target, prop) {
31
+ if (prop === "chat") return chatProxy;
32
+ return target[prop];
33
+ }
34
+ });
35
+ }
36
+
37
+ export { withHeadroom };
38
+ //# sourceMappingURL=openai.js.map
39
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/openai.ts"],"names":[],"mappings":";;;AAgCO,SAAS,YAAA,CACd,MAAA,EACA,OAAA,GAA2B,EAAC,EACzB;AACH,EAAA,MAAM,cAAA,GAAiB,MAAA,CAAO,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,IAAA;AAAA,IACpD,OAAO,IAAA,CAAK;AAAA,GACd;AAEA,EAAA,MAAM,gBAAA,GAAmB,OAAO,MAAA,KAAgB;AAC9C,IAAA,MAAM,WAA4B,MAAA,CAAO,QAAA;AACzC,IAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,IAAS,MAAA,CAAO,KAAA,IAAS,QAAA;AAE/C,IAAA,MAAM,MAAA,GAAS,MAAM,QAAA,CAAS,QAAA,EAAU,EAAE,GAAG,OAAA,EAAS,OAAO,CAAA;AAE7D,IAAA,OAAO,cAAA,CAAe;AAAA,MACpB,GAAG,MAAA;AAAA,MACH,UAAU,MAAA,CAAO;AAAA,KAClB,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,WAAA,EAAa;AAAA,IAC1D,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,UAAU,OAAO,gBAAA;AAC9B,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AAED,EAAA,MAAM,SAAA,GAAY,IAAI,KAAA,CAAM,MAAA,CAAO,IAAA,EAAM;AAAA,IACvC,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,eAAe,OAAO,gBAAA;AACnC,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AAED,EAAA,OAAO,IAAI,MAAM,MAAA,EAAQ;AAAA,IACvB,GAAA,CAAI,QAAQ,IAAA,EAAM;AAChB,MAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,SAAA;AAC5B,MAAA,OAAQ,OAAe,IAAI,CAAA;AAAA,IAC7B;AAAA,GACD,CAAA;AACH","file":"openai.js","sourcesContent":["import { compress } from \"../compress.js\";\nimport type { CompressOptions, OpenAIMessage } from \"../types.js\";\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\ninterface OpenAILike {\n chat: {\n completions: {\n create: (params: any) => any;\n };\n };\n [key: string]: any;\n}\n\n/**\n * Wrap an OpenAI client to auto-compress messages before each request.\n *\n * Intercepts `client.chat.completions.create()` only. All other methods\n * (embeddings, images, audio, etc.) pass through unchanged.\n *\n * @example\n * ```typescript\n * import { withHeadroom } from 'headroom-ai/openai';\n * import OpenAI from 'openai';\n *\n * const client = withHeadroom(new OpenAI());\n * const response = await client.chat.completions.create({\n * model: 'gpt-4o',\n * messages: longConversation,\n * });\n * ```\n */\nexport function withHeadroom<T extends OpenAILike>(\n client: T,\n options: CompressOptions = {},\n): T {\n const originalCreate = client.chat.completions.create.bind(\n client.chat.completions,\n );\n\n const compressedCreate = async (params: any) => {\n const messages: OpenAIMessage[] = params.messages;\n const model = options.model ?? params.model ?? \"gpt-4o\";\n\n const result = await compress(messages, { ...options, model });\n\n return originalCreate({\n ...params,\n messages: result.messages,\n });\n };\n\n const completionsProxy = new Proxy(client.chat.completions, {\n get(target, prop) {\n if (prop === \"create\") return compressedCreate;\n return (target as any)[prop];\n },\n });\n\n const chatProxy = new Proxy(client.chat, {\n get(target, prop) {\n if (prop === \"completions\") return completionsProxy;\n return (target as any)[prop];\n },\n });\n\n return new Proxy(client, {\n get(target, prop) {\n if (prop === \"chat\") return chatProxy;\n return (target as any)[prop];\n },\n }) as T;\n}\n"]}