@opencompress/opencompress 1.3.0 → 1.4.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.
Files changed (2) hide show
  1. package/dist/index.js +81 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,40 +5,66 @@ function getApiKey(api) {
5
5
  const auth = api.config.auth;
6
6
  return auth?.profiles?.opencompress?.credentials?.["api-key"]?.apiKey;
7
7
  }
8
- var OPENCOMPRESS_MODELS = [
9
- // OpenAI
10
- { id: "gpt-4o", name: "GPT-4o (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
11
- { id: "gpt-4o-mini", name: "GPT-4o Mini (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
12
- { id: "gpt-4.1", name: "GPT-4.1 (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
13
- { id: "gpt-4.1-mini", name: "GPT-4.1 Mini (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
14
- { id: "gpt-4.1-nano", name: "GPT-4.1 Nano (Compressed)", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
15
- { id: "o3", name: "O3 (Compressed)", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 1e5 },
16
- { id: "o4-mini", name: "O4 Mini (Compressed)", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 1e5 },
17
- // Anthropic
18
- { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6 (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
19
- { id: "claude-opus-4-6", name: "Claude Opus 4.6 (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
20
- { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5 (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
21
- // Google
22
- { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro (Compressed)", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
23
- { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
24
- { id: "google/gemini-2.5-pro-preview", name: "Gemini 2.5 Pro Preview (Compressed)", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
25
- // DeepSeek
26
- { id: "deepseek/deepseek-chat-v3-0324", name: "DeepSeek V3 (Compressed)", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
27
- { id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner (Compressed)", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
28
- // Meta
29
- { id: "meta-llama/llama-4-maverick", name: "Llama 4 Maverick (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
30
- { id: "meta-llama/llama-4-scout", name: "Llama 4 Scout (Compressed)", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 524288, maxTokens: 65536 },
31
- // Qwen
32
- { id: "qwen/qwen3-235b-a22b", name: "Qwen3 235B (Compressed)", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
33
- { id: "qwen/qwen3-32b", name: "Qwen3 32B (Compressed)", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
34
- // Mistral
35
- { id: "mistralai/mistral-large-2411", name: "Mistral Large (Compressed)", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 }
8
+ var FALLBACK_MODELS = [
9
+ { id: "gpt-4o", name: "GPT-4o", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
10
+ { id: "gpt-4o-mini", name: "GPT-4o Mini", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
11
+ { id: "gpt-4.1", name: "GPT-4.1", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
12
+ { id: "gpt-4.1-mini", name: "GPT-4.1 Mini", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1047576, maxTokens: 32768 },
13
+ { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
14
+ { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 2e5, maxTokens: 128e3 },
15
+ { id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", reasoning: true, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
16
+ { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 1048576, maxTokens: 65536 },
17
+ { id: "deepseek/deepseek-chat-v3-0324", name: "DeepSeek V3", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 },
18
+ { id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 }
36
19
  ];
37
- function buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl) {
20
+ function upstreamToModels(data) {
21
+ return data.map((m) => {
22
+ const promptPrice = m.pricing?.prompt ? parseFloat(m.pricing.prompt) * 1e6 : 0;
23
+ const completionPrice = m.pricing?.completion ? parseFloat(m.pricing.completion) * 1e6 : 0;
24
+ const isReasoning = /\bo[34]\b|reasoner|thinking|deepthink/i.test(m.id);
25
+ const inputMods = ["text"];
26
+ const archInputs = m.architecture?.input_modalities || [];
27
+ if (archInputs.includes("image") || /vision|4o|gemini|claude-.*[34]/i.test(m.id)) {
28
+ inputMods.push("image");
29
+ }
30
+ return {
31
+ id: m.id,
32
+ name: m.id,
33
+ reasoning: isReasoning,
34
+ input: inputMods,
35
+ cost: {
36
+ input: promptPrice,
37
+ output: completionPrice,
38
+ cacheRead: 0,
39
+ cacheWrite: 0
40
+ },
41
+ contextWindow: m.context_length || 128e3,
42
+ maxTokens: Math.min(m.context_length || 128e3, 65536)
43
+ };
44
+ });
45
+ }
46
+ async function fetchUpstreamModels(baseUrl, apiKey, upstreamKey, upstreamBaseUrl) {
47
+ try {
48
+ const headers = {
49
+ Authorization: `Bearer ${apiKey}`
50
+ };
51
+ if (upstreamKey) headers["X-Upstream-Key"] = upstreamKey;
52
+ if (upstreamBaseUrl) headers["X-Upstream-Base-Url"] = upstreamBaseUrl;
53
+ const res = await fetch(`${baseUrl}/v1/models`, { headers });
54
+ if (!res.ok) return null;
55
+ const data = await res.json();
56
+ const models = data.data || [];
57
+ if (models.length === 0) return null;
58
+ return upstreamToModels(models);
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, models) {
38
64
  const config = {
39
65
  baseUrl: `${baseUrl}/v1`,
40
66
  api: "openai-completions",
41
- models: OPENCOMPRESS_MODELS
67
+ models: models || FALLBACK_MODELS
42
68
  };
43
69
  if (upstreamKey || upstreamBaseUrl) {
44
70
  config.headers = {};
@@ -115,6 +141,8 @@ var opencompressProvider = {
115
141
  });
116
142
  } catch {
117
143
  }
144
+ const upstreamModels = await fetchUpstreamModels(DEFAULT_BASE_URL, data.apiKey, llmKey, upstreamBaseUrl);
145
+ const modelCount = upstreamModels ? upstreamModels.length : FALLBACK_MODELS.length;
118
146
  return {
119
147
  profiles: [
120
148
  {
@@ -125,13 +153,13 @@ var opencompressProvider = {
125
153
  configPatch: {
126
154
  models: {
127
155
  providers: {
128
- opencompress: buildProviderModels(DEFAULT_BASE_URL, llmKey, upstreamBaseUrl)
156
+ opencompress: buildProviderModels(DEFAULT_BASE_URL, llmKey, upstreamBaseUrl, upstreamModels || void 0)
129
157
  }
130
158
  }
131
159
  },
132
160
  defaultModel: "gpt-4o-mini",
133
161
  notes: [
134
- `OpenCompress is ready! Connected to ${provider}.`,
162
+ `OpenCompress is ready! Connected to ${provider} (${modelCount} models).`,
135
163
  "Your LLM key is stored locally only \u2014 never on our server.",
136
164
  `Free credit: ${data.freeCredit}. Dashboard: opencompress.ai/dashboard`
137
165
  ]
@@ -154,7 +182,12 @@ var plugin = {
154
182
  const existingHeaders = api.config.models?.providers?.opencompress?.headers;
155
183
  const existingUpstreamKey = existingHeaders?.["X-Upstream-Key"];
156
184
  const existingUpstreamBaseUrl = existingHeaders?.["X-Upstream-Base-Url"];
157
- const providerModels = buildProviderModels(baseUrl, existingUpstreamKey, existingUpstreamBaseUrl);
185
+ const apiKey = getApiKey(api);
186
+ let dynamicModels = null;
187
+ if (apiKey && existingUpstreamKey) {
188
+ dynamicModels = await fetchUpstreamModels(baseUrl, apiKey, existingUpstreamKey, existingUpstreamBaseUrl);
189
+ }
190
+ const providerModels = buildProviderModels(baseUrl, existingUpstreamKey, existingUpstreamBaseUrl, dynamicModels || void 0);
158
191
  opencompressProvider.models = providerModels;
159
192
  api.registerProvider(opencompressProvider);
160
193
  if (!api.config.models) {
@@ -164,22 +197,24 @@ var plugin = {
164
197
  api.config.models.providers = {};
165
198
  }
166
199
  api.config.models.providers.opencompress = providerModels;
167
- api.logger.info("OpenCompress provider registered (20 models, 5-layer compression)");
200
+ const modelCount = dynamicModels ? dynamicModels.length : FALLBACK_MODELS.length;
201
+ const source = dynamicModels ? "upstream" : "fallback";
202
+ api.logger.info(`OpenCompress provider registered (${modelCount} ${source} models, 5-layer compression)`);
168
203
  api.registerCommand({
169
204
  name: "compress-stats",
170
205
  description: "Show OpenCompress usage statistics and savings",
171
206
  acceptsArgs: true,
172
207
  requireAuth: false,
173
208
  handler: async () => {
174
- const apiKey = getApiKey(api);
175
- if (!apiKey) {
209
+ const apiKey2 = getApiKey(api);
210
+ if (!apiKey2) {
176
211
  return {
177
212
  text: "No API key found. Run `openclaw onboard opencompress` to set up."
178
213
  };
179
214
  }
180
215
  try {
181
216
  const res = await fetch(`${baseUrl}/user/stats`, {
182
- headers: { Authorization: `Bearer ${apiKey}` }
217
+ headers: { Authorization: `Bearer ${apiKey2}` }
183
218
  });
184
219
  if (!res.ok) {
185
220
  return { text: `Failed to fetch stats: HTTP ${res.status}` };
@@ -219,14 +254,14 @@ var plugin = {
219
254
  acceptsArgs: true,
220
255
  requireAuth: false,
221
256
  handler: async (ctx) => {
222
- const apiKey = getApiKey(api);
223
- if (!apiKey) {
257
+ const apiKey2 = getApiKey(api);
258
+ if (!apiKey2) {
224
259
  return { text: "Not set up. Run `openclaw onboard opencompress` first." };
225
260
  }
226
261
  const upstreamKey = ctx.args?.trim();
227
262
  if (!upstreamKey) {
228
263
  const res = await fetch(`${baseUrl}/v1/topup`, {
229
- headers: { Authorization: `Bearer ${apiKey}` }
264
+ headers: { Authorization: `Bearer ${apiKey2}` }
230
265
  });
231
266
  const data = res.ok ? await res.json() : null;
232
267
  return {
@@ -253,7 +288,7 @@ var plugin = {
253
288
  try {
254
289
  await fetch(`${baseUrl}/v1/byok`, {
255
290
  method: "DELETE",
256
- headers: { Authorization: `Bearer ${apiKey}` }
291
+ headers: { Authorization: `Bearer ${apiKey2}` }
257
292
  });
258
293
  } catch {
259
294
  }
@@ -280,7 +315,8 @@ var plugin = {
280
315
  provider = "google";
281
316
  upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
282
317
  }
283
- const updatedModels = buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl);
318
+ const upstreamModels = await fetchUpstreamModels(baseUrl, apiKey2, upstreamKey, upstreamBaseUrl);
319
+ const updatedModels = buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, upstreamModels || void 0);
284
320
  if (api.config.models?.providers) {
285
321
  api.config.models.providers.opencompress = updatedModels;
286
322
  }
@@ -288,16 +324,18 @@ var plugin = {
288
324
  await fetch(`${baseUrl}/v1/byok`, {
289
325
  method: "POST",
290
326
  headers: {
291
- Authorization: `Bearer ${apiKey}`,
327
+ Authorization: `Bearer ${apiKey2}`,
292
328
  "Content-Type": "application/json"
293
329
  },
294
330
  body: JSON.stringify({ provider, passthrough: true })
295
331
  });
296
332
  } catch {
297
333
  }
334
+ const modelCount2 = upstreamModels ? upstreamModels.length : FALLBACK_MODELS.length;
298
335
  return {
299
336
  text: [
300
337
  `Switched to **BYOK mode** (${provider}).`,
338
+ `Loaded **${modelCount2} models** from your ${provider} account.`,
301
339
  "",
302
340
  "Your key is stored **locally only** \u2014 never sent to our server for storage.",
303
341
  "It's passed through on each request via header and discarded immediately.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencompress/opencompress",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "OpenCompress plugin for OpenClaw — automatic 5-layer prompt compression for any LLM",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",