@opencompress/opencompress 1.6.6 → 1.7.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 +246 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/index.ts
9
- var VERSION = "1.6.6";
9
+ var VERSION = "1.7.0";
10
10
  var DEFAULT_BASE_URL = "https://www.opencompress.ai/api";
11
11
  function getApiKey(api) {
12
12
  const auth = api.config.auth;
@@ -269,6 +269,151 @@ function persistAuthProfile(apiKey) {
269
269
  } catch {
270
270
  }
271
271
  }
272
+ function proxyStatePath() {
273
+ const os = __require("os");
274
+ const path = __require("path");
275
+ return path.join(os.homedir(), ".openclaw", "opencompress-proxy.json");
276
+ }
277
+ function readProxyState() {
278
+ try {
279
+ const fs = __require("fs");
280
+ const p = proxyStatePath();
281
+ if (!fs.existsSync(p)) return { enabled: false, originals: {} };
282
+ return JSON.parse(fs.readFileSync(p, "utf-8"));
283
+ } catch {
284
+ return { enabled: false, originals: {} };
285
+ }
286
+ }
287
+ function writeProxyState(state) {
288
+ try {
289
+ const fs = __require("fs");
290
+ fs.writeFileSync(proxyStatePath(), JSON.stringify(state, null, 2) + "\n");
291
+ } catch {
292
+ }
293
+ }
294
+ function persistProviderToDisk(providerId, config) {
295
+ const os = __require("os");
296
+ const fs = __require("fs");
297
+ const path = __require("path");
298
+ try {
299
+ const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
300
+ if (fs.existsSync(configPath)) {
301
+ const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
302
+ if (!raw.models) raw.models = {};
303
+ if (!raw.models.providers) raw.models.providers = {};
304
+ raw.models.providers[providerId] = {
305
+ baseUrl: config.baseUrl,
306
+ api: config.api || "openai-completions",
307
+ apiKey: config.apiKey || void 0,
308
+ models: config.models?.map((m) => ({ id: m.id, name: m.name })) || [],
309
+ ...config.headers ? { headers: config.headers } : {}
310
+ };
311
+ fs.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
312
+ }
313
+ } catch {
314
+ }
315
+ try {
316
+ const agentsDir = path.join(os.homedir(), ".openclaw", "agents");
317
+ if (!fs.existsSync(agentsDir)) return;
318
+ for (const agent of fs.readdirSync(agentsDir)) {
319
+ const modelsPath = path.join(agentsDir, agent, "agent", "models.json");
320
+ if (!fs.existsSync(path.dirname(modelsPath))) continue;
321
+ let data = { providers: {} };
322
+ if (fs.existsSync(modelsPath)) {
323
+ try {
324
+ data = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
325
+ } catch {
326
+ }
327
+ if (!data.providers) data.providers = {};
328
+ }
329
+ data.providers[providerId] = {
330
+ baseUrl: config.baseUrl,
331
+ api: config.api || "openai-completions",
332
+ apiKey: config.apiKey || void 0,
333
+ models: config.models?.map((m) => ({
334
+ id: m.id,
335
+ name: m.name,
336
+ api: m.api || config.api || "openai-completions",
337
+ reasoning: m.reasoning ?? false,
338
+ input: m.input || ["text"],
339
+ cost: m.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
340
+ contextWindow: m.contextWindow || 2e5,
341
+ maxTokens: m.maxTokens || 8192
342
+ })) || [],
343
+ ...config.headers ? { headers: config.headers } : {}
344
+ };
345
+ fs.writeFileSync(modelsPath, JSON.stringify(data, null, 2) + "\n");
346
+ }
347
+ } catch {
348
+ }
349
+ }
350
+ function enableProxy(api, baseUrl) {
351
+ const occKey = getApiKey(api);
352
+ if (!occKey) return { proxied: [], skipped: [] };
353
+ const providers = api.config.models?.providers;
354
+ if (!providers) return { proxied: [], skipped: [] };
355
+ const state = readProxyState();
356
+ const proxied = [];
357
+ const skipped = [];
358
+ for (const [id, raw] of Object.entries(providers)) {
359
+ if (id === "opencompress") continue;
360
+ const provider = raw;
361
+ if (provider.baseUrl?.includes("opencompress.ai")) continue;
362
+ const pApi = provider.api || "openai-completions";
363
+ if (pApi === "anthropic-messages" || pApi === "google-generative-ai") {
364
+ skipped.push(`${id} (${pApi})`);
365
+ continue;
366
+ }
367
+ state.originals[id] = {
368
+ baseUrl: provider.baseUrl,
369
+ apiKey: provider.apiKey,
370
+ api: provider.api,
371
+ headers: provider.headers ? { ...provider.headers } : void 0
372
+ };
373
+ provider.headers = {
374
+ ...provider.headers || {},
375
+ "X-Upstream-Key": provider.apiKey || "",
376
+ "X-Upstream-Base-Url": provider.baseUrl
377
+ };
378
+ provider.baseUrl = `${baseUrl}/v1`;
379
+ provider.apiKey = occKey;
380
+ persistProviderToDisk(id, provider);
381
+ proxied.push(id);
382
+ }
383
+ state.enabled = true;
384
+ writeProxyState(state);
385
+ return { proxied, skipped };
386
+ }
387
+ function disableProxy(api) {
388
+ const state = readProxyState();
389
+ if (!state.enabled) return [];
390
+ const providers = api.config.models?.providers;
391
+ if (!providers) return [];
392
+ const restored = [];
393
+ for (const [id, orig] of Object.entries(state.originals)) {
394
+ const provider = providers[id];
395
+ if (!provider) continue;
396
+ provider.baseUrl = orig.baseUrl;
397
+ provider.apiKey = orig.apiKey;
398
+ provider.api = orig.api;
399
+ if (orig.headers) {
400
+ provider.headers = orig.headers;
401
+ } else {
402
+ const h = provider.headers;
403
+ if (h) {
404
+ delete h["X-Upstream-Key"];
405
+ delete h["X-Upstream-Base-Url"];
406
+ if (Object.keys(h).length === 0) delete provider.headers;
407
+ }
408
+ }
409
+ persistProviderToDisk(id, provider);
410
+ restored.push(id);
411
+ }
412
+ state.enabled = false;
413
+ state.originals = {};
414
+ writeProxyState(state);
415
+ return restored;
416
+ }
272
417
  var opencompressProvider = {
273
418
  id: "opencompress",
274
419
  label: "OpenCompress",
@@ -375,7 +520,7 @@ var opencompressProvider = {
375
520
  var plugin = {
376
521
  id: "opencompress",
377
522
  name: "OpenCompress",
378
- description: "5-layer prompt compression \u2014 53% input reduction, 62% latency cut, 96% quality",
523
+ description: "5-layer prompt compression \u2014 40-60% cost reduction, 62% latency cut, 6ms CompactClassifier",
379
524
  version: VERSION,
380
525
  register(api) {
381
526
  const baseUrl = api.pluginConfig?.baseUrl || DEFAULT_BASE_URL;
@@ -572,6 +717,82 @@ var plugin = {
572
717
  }
573
718
  });
574
719
  api.logger.info("Registered /compress-byok command");
720
+ api.registerCommand({
721
+ name: "compress",
722
+ description: "Toggle transparent compression for all LLM providers",
723
+ acceptsArgs: true,
724
+ requireAuth: false,
725
+ handler: async (ctx) => {
726
+ const arg = ctx.args?.trim().toLowerCase();
727
+ if (arg === "on" || arg === "enable") {
728
+ const occKey2 = getApiKey(api);
729
+ if (!occKey2) {
730
+ return { text: "No API key found. Run `openclaw onboard opencompress` first." };
731
+ }
732
+ const result = enableProxy(api, baseUrl);
733
+ if (result.proxied.length === 0 && result.skipped.length === 0) {
734
+ return { text: "No providers found to proxy. Add LLM providers first." };
735
+ }
736
+ const lines = [
737
+ "**Compression enabled** for all compatible providers.",
738
+ ""
739
+ ];
740
+ if (result.proxied.length > 0) {
741
+ lines.push(`Proxied (${result.proxied.length}): ${result.proxied.join(", ")}`);
742
+ }
743
+ if (result.skipped.length > 0) {
744
+ lines.push(`Skipped (incompatible format): ${result.skipped.join(", ")}`);
745
+ }
746
+ lines.push("", "All requests now route through OpenCompress for automatic compression.");
747
+ lines.push("To disable: `/compress off`");
748
+ return { text: lines.join("\n") };
749
+ }
750
+ if (arg === "off" || arg === "disable") {
751
+ const restored = disableProxy(api);
752
+ if (restored.length === 0) {
753
+ return { text: "Compression proxy was not active." };
754
+ }
755
+ return {
756
+ text: [
757
+ "**Compression disabled.** Restored original provider configs.",
758
+ "",
759
+ `Restored: ${restored.join(", ")}`,
760
+ "",
761
+ "To re-enable: `/compress on`"
762
+ ].join("\n")
763
+ };
764
+ }
765
+ const proxyState = readProxyState();
766
+ const occKey = getApiKey(api);
767
+ const statusLines = [
768
+ "**OpenCompress Transparent Proxy**",
769
+ "",
770
+ `Status: ${proxyState.enabled ? "**ON**" : "**OFF**"}`,
771
+ `API key: ${occKey ? `${occKey.slice(0, 12)}...` : "not set"}`
772
+ ];
773
+ if (proxyState.enabled && Object.keys(proxyState.originals).length > 0) {
774
+ statusLines.push(`Proxied providers: ${Object.keys(proxyState.originals).join(", ")}`);
775
+ }
776
+ statusLines.push(
777
+ "",
778
+ "**Usage:**",
779
+ " `/compress on` \u2014 Route all providers through OpenCompress",
780
+ " `/compress off` \u2014 Restore original provider configs",
781
+ " `/compress-stats` \u2014 View compression savings"
782
+ );
783
+ return { text: statusLines.join("\n") };
784
+ }
785
+ });
786
+ api.logger.info("Registered /compress command");
787
+ setTimeout(() => {
788
+ const proxyState = readProxyState();
789
+ if (proxyState.enabled) {
790
+ const result = enableProxy(api, baseUrl);
791
+ if (result.proxied.length > 0) {
792
+ api.logger.info(`Proxy auto-applied on startup: ${result.proxied.length} providers (${result.proxied.join(", ")})`);
793
+ }
794
+ }
795
+ }, 2e3);
575
796
  const os = __require("os");
576
797
  const fs = __require("fs");
577
798
  const path = __require("path");
@@ -580,46 +801,46 @@ var plugin = {
580
801
  fs.mkdirSync(logDir, { recursive: true });
581
802
  } catch {
582
803
  }
583
- api.on("llm_input", async (event) => {
804
+ api.on("llm_input", (...args) => {
584
805
  try {
585
806
  const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
586
- const systemPromptLen = event.systemPrompt?.length || 0;
587
- const historyLen = Array.isArray(event.historyMessages) ? event.historyMessages.length : 0;
588
- const promptLen = event.prompt?.length || 0;
807
+ const event = args[0] || {};
808
+ const systemPromptLen = typeof event.systemPrompt === "string" ? event.systemPrompt.length : 0;
809
+ const historyMessages = Array.isArray(event.historyMessages) ? event.historyMessages : [];
810
+ const promptLen = typeof event.prompt === "string" ? event.prompt.length : 0;
589
811
  const systemTokens = Math.ceil(systemPromptLen / 4);
590
- const historyText = JSON.stringify(event.historyMessages || []);
812
+ const historyText = JSON.stringify(historyMessages);
591
813
  const historyTokens = Math.ceil(historyText.length / 4);
592
814
  const promptTokens = Math.ceil(promptLen / 4);
593
815
  const totalTokens = systemTokens + historyTokens + promptTokens;
594
816
  const entry = {
595
817
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
818
+ _argCount: args.length,
819
+ _eventKeys: Object.keys(event),
596
820
  runId: event.runId,
597
821
  sessionId: event.sessionId,
598
822
  provider: event.provider,
599
823
  model: event.model,
600
824
  imagesCount: event.imagesCount || 0,
601
- tokenEstimate: {
602
- systemPrompt: systemTokens,
603
- history: historyTokens,
604
- currentPrompt: promptTokens,
605
- total: totalTokens
606
- },
607
- charLengths: {
608
- systemPrompt: systemPromptLen,
609
- history: historyText.length,
610
- currentPrompt: promptLen
611
- },
612
- historyMessageCount: historyLen,
825
+ tokenEstimate: { systemPrompt: systemTokens, history: historyTokens, currentPrompt: promptTokens, total: totalTokens },
826
+ charLengths: { systemPrompt: systemPromptLen, history: historyText.length, currentPrompt: promptLen },
827
+ historyMessageCount: historyMessages.length,
613
828
  systemPrompt: event.systemPrompt,
614
829
  historyMessages: event.historyMessages,
615
830
  prompt: event.prompt
616
831
  };
617
- const filename = `${ts}_${event.model || "unknown"}.json`;
618
- fs.writeFileSync(
619
- path.join(logDir, filename),
620
- JSON.stringify(entry, null, 2) + "\n"
621
- );
622
- } catch {
832
+ const model = typeof event.model === "string" ? event.model.replace(/\//g, "_") : "unknown";
833
+ const filename = `${ts}_${model}.json`;
834
+ fs.writeFileSync(path.join(logDir, filename), JSON.stringify(entry, null, 2) + "\n");
835
+ } catch (err) {
836
+ try {
837
+ const errFile = path.join(logDir, `error_${Date.now()}.txt`);
838
+ fs.writeFileSync(errFile, `${err}
839
+ ${err.stack || ""}
840
+ args: ${JSON.stringify(args?.map((a) => typeof a))}
841
+ `);
842
+ } catch {
843
+ }
623
844
  }
624
845
  });
625
846
  api.logger.info(`LLM input logging enabled \u2192 ${logDir}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencompress/opencompress",
3
- "version": "1.6.6",
3
+ "version": "1.7.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",