@opencompress/opencompress 1.6.7 → 1.8.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/index.js +223 -350
- 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.
|
|
9
|
+
var VERSION = "1.8.0";
|
|
10
10
|
var DEFAULT_BASE_URL = "https://www.opencompress.ai/api";
|
|
11
11
|
function getApiKey(api) {
|
|
12
12
|
const auth = api.config.auth;
|
|
@@ -35,96 +35,7 @@ function getApiKey(api) {
|
|
|
35
35
|
}
|
|
36
36
|
return void 0;
|
|
37
37
|
}
|
|
38
|
-
|
|
39
|
-
{ id: "gpt-4o", name: "GPT-4o", reasoning: false, input: ["text", "image"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128e3, maxTokens: 16384 },
|
|
40
|
-
{ 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 },
|
|
41
|
-
{ 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 },
|
|
42
|
-
{ 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 },
|
|
43
|
-
{ 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 },
|
|
44
|
-
{ 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 },
|
|
45
|
-
{ 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 },
|
|
46
|
-
{ 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 },
|
|
47
|
-
{ 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 },
|
|
48
|
-
{ id: "deepseek/deepseek-reasoner", name: "DeepSeek Reasoner", reasoning: true, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 131072, maxTokens: 8192 }
|
|
49
|
-
];
|
|
50
|
-
function readExistingModels(api) {
|
|
51
|
-
const providers = api.config.models?.providers;
|
|
52
|
-
if (!providers) return null;
|
|
53
|
-
const seen = /* @__PURE__ */ new Set();
|
|
54
|
-
const models = [];
|
|
55
|
-
for (const [providerId, providerConfig] of Object.entries(providers)) {
|
|
56
|
-
if (providerId === "opencompress") continue;
|
|
57
|
-
const providerModels = providerConfig.models || [];
|
|
58
|
-
for (const m of providerModels) {
|
|
59
|
-
if (m.name?.includes("\u2192")) continue;
|
|
60
|
-
const rawId = m.id.includes("/") ? m.id.split("/").slice(1).join("/") : m.id;
|
|
61
|
-
if (seen.has(rawId)) continue;
|
|
62
|
-
seen.add(rawId);
|
|
63
|
-
const upstreamId = m.id.startsWith(`${providerId}/`) ? rawId : m.id;
|
|
64
|
-
models.push({
|
|
65
|
-
...m,
|
|
66
|
-
id: upstreamId,
|
|
67
|
-
name: m.name || upstreamId,
|
|
68
|
-
// Zero out cost — billing handled by OpenCompress proxy
|
|
69
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return models.length > 0 ? models : null;
|
|
74
|
-
}
|
|
75
|
-
function buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, models, apiKey) {
|
|
76
|
-
const config = {
|
|
77
|
-
baseUrl: `${baseUrl}/v1`,
|
|
78
|
-
api: "openai-completions",
|
|
79
|
-
apiKey: apiKey || void 0,
|
|
80
|
-
models: models || FALLBACK_MODELS
|
|
81
|
-
};
|
|
82
|
-
if (upstreamKey || upstreamBaseUrl) {
|
|
83
|
-
config.headers = {};
|
|
84
|
-
if (upstreamKey) config.headers["X-Upstream-Key"] = upstreamKey;
|
|
85
|
-
if (upstreamBaseUrl) config.headers["X-Upstream-Base-Url"] = upstreamBaseUrl;
|
|
86
|
-
}
|
|
87
|
-
return config;
|
|
88
|
-
}
|
|
89
|
-
function persistModelsConfig(providerModels) {
|
|
90
|
-
try {
|
|
91
|
-
const os = __require("os");
|
|
92
|
-
const fs = __require("fs");
|
|
93
|
-
const path = __require("path");
|
|
94
|
-
const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
95
|
-
if (!fs.existsSync(configPath)) return;
|
|
96
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
97
|
-
let config;
|
|
98
|
-
try {
|
|
99
|
-
config = JSON.parse(raw);
|
|
100
|
-
} catch {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (!config.models) config.models = {};
|
|
104
|
-
const models = config.models;
|
|
105
|
-
if (!models.providers) models.providers = {};
|
|
106
|
-
const providers = models.providers;
|
|
107
|
-
const configSafeModels = providerModels.models.map((m) => ({
|
|
108
|
-
id: m.id,
|
|
109
|
-
name: m.name
|
|
110
|
-
}));
|
|
111
|
-
const configEntry = {
|
|
112
|
-
baseUrl: providerModels.baseUrl,
|
|
113
|
-
api: providerModels.api || "openai-completions",
|
|
114
|
-
models: configSafeModels
|
|
115
|
-
};
|
|
116
|
-
if (providerModels.apiKey) {
|
|
117
|
-
configEntry.apiKey = providerModels.apiKey;
|
|
118
|
-
}
|
|
119
|
-
if (providerModels.headers) {
|
|
120
|
-
configEntry.headers = providerModels.headers;
|
|
121
|
-
}
|
|
122
|
-
providers.opencompress = configEntry;
|
|
123
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
124
|
-
} catch {
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function persistAgentModelsJson(providerModels) {
|
|
38
|
+
function persistAuthProfile(apiKey) {
|
|
128
39
|
try {
|
|
129
40
|
const os = __require("os");
|
|
130
41
|
const fs = __require("fs");
|
|
@@ -133,37 +44,27 @@ function persistAgentModelsJson(providerModels) {
|
|
|
133
44
|
if (!fs.existsSync(agentsDir)) return;
|
|
134
45
|
const agentDirs = fs.readdirSync(agentsDir);
|
|
135
46
|
for (const agent of agentDirs) {
|
|
136
|
-
const
|
|
137
|
-
const
|
|
138
|
-
if (!fs.existsSync(
|
|
139
|
-
|
|
140
|
-
|
|
47
|
+
const authPath = path.join(agentsDir, agent, "agent", "auth-profiles.json");
|
|
48
|
+
const authDir = path.dirname(authPath);
|
|
49
|
+
if (!fs.existsSync(authDir)) {
|
|
50
|
+
fs.mkdirSync(authDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
let profiles = {
|
|
53
|
+
version: 1,
|
|
54
|
+
profiles: {}
|
|
55
|
+
};
|
|
56
|
+
if (fs.existsSync(authPath)) {
|
|
141
57
|
try {
|
|
142
|
-
|
|
143
|
-
if (!data.providers || typeof data.providers !== "object") {
|
|
144
|
-
data.providers = {};
|
|
145
|
-
}
|
|
58
|
+
profiles = JSON.parse(fs.readFileSync(authPath, "utf-8"));
|
|
146
59
|
} catch {
|
|
147
|
-
data = { providers: {} };
|
|
148
60
|
}
|
|
149
61
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
models: providerModels.models.map((m) => ({
|
|
155
|
-
id: m.id,
|
|
156
|
-
name: m.name,
|
|
157
|
-
api: m.api || "openai-completions",
|
|
158
|
-
reasoning: m.reasoning ?? false,
|
|
159
|
-
input: m.input || ["text"],
|
|
160
|
-
cost: m.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
161
|
-
contextWindow: m.contextWindow || 2e5,
|
|
162
|
-
maxTokens: m.maxTokens || 8192
|
|
163
|
-
})),
|
|
164
|
-
...providerModels.headers ? { headers: providerModels.headers } : {}
|
|
62
|
+
profiles.profiles["opencompress:default"] = {
|
|
63
|
+
type: "api_key",
|
|
64
|
+
provider: "opencompress",
|
|
65
|
+
key: apiKey
|
|
165
66
|
};
|
|
166
|
-
fs.writeFileSync(
|
|
67
|
+
fs.writeFileSync(authPath, JSON.stringify(profiles, null, 2) + "\n");
|
|
167
68
|
}
|
|
168
69
|
} catch {
|
|
169
70
|
}
|
|
@@ -197,125 +98,181 @@ function persistAgentAuthJson(apiKey) {
|
|
|
197
98
|
} catch {
|
|
198
99
|
}
|
|
199
100
|
}
|
|
200
|
-
function
|
|
101
|
+
function proxyStatePath() {
|
|
102
|
+
const os = __require("os");
|
|
103
|
+
const path = __require("path");
|
|
104
|
+
return path.join(os.homedir(), ".openclaw", "opencompress-proxy.json");
|
|
105
|
+
}
|
|
106
|
+
function readProxyState() {
|
|
201
107
|
try {
|
|
202
|
-
const os = __require("os");
|
|
203
108
|
const fs = __require("fs");
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
208
|
-
let config;
|
|
209
|
-
try {
|
|
210
|
-
config = JSON.parse(raw);
|
|
211
|
-
} catch {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
if (!config.agents) config.agents = {};
|
|
215
|
-
const agents = config.agents;
|
|
216
|
-
if (!agents.defaults) agents.defaults = {};
|
|
217
|
-
const defaults = agents.defaults;
|
|
218
|
-
if (!defaults.models) defaults.models = {};
|
|
219
|
-
const allowlist = defaults.models;
|
|
220
|
-
const existingKeys = Object.keys(allowlist);
|
|
221
|
-
if (existingKeys.length === 0) return;
|
|
222
|
-
let changed = false;
|
|
223
|
-
for (const m of models) {
|
|
224
|
-
const fullId = `opencompress/${m.id}`;
|
|
225
|
-
if (!allowlist[fullId]) {
|
|
226
|
-
allowlist[fullId] = {};
|
|
227
|
-
changed = true;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (changed) {
|
|
231
|
-
const tmpPath = `${configPath}.tmp.${process.pid}`;
|
|
232
|
-
fs.writeFileSync(tmpPath, JSON.stringify(config, null, 2) + "\n");
|
|
233
|
-
fs.renameSync(tmpPath, configPath);
|
|
234
|
-
}
|
|
109
|
+
const p = proxyStatePath();
|
|
110
|
+
if (!fs.existsSync(p)) return { enabled: false, originals: {} };
|
|
111
|
+
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
|
235
112
|
} catch {
|
|
113
|
+
return { enabled: false, originals: {} };
|
|
236
114
|
}
|
|
237
115
|
}
|
|
238
|
-
function
|
|
116
|
+
function writeProxyState(state) {
|
|
239
117
|
try {
|
|
240
|
-
const os = __require("os");
|
|
241
118
|
const fs = __require("fs");
|
|
242
|
-
|
|
119
|
+
fs.writeFileSync(proxyStatePath(), JSON.stringify(state, null, 2) + "\n");
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function persistProviderToDisk(providerId, config) {
|
|
124
|
+
const os = __require("os");
|
|
125
|
+
const fs = __require("fs");
|
|
126
|
+
const path = __require("path");
|
|
127
|
+
try {
|
|
128
|
+
const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
129
|
+
if (fs.existsSync(configPath)) {
|
|
130
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
131
|
+
if (!raw.models) raw.models = {};
|
|
132
|
+
if (!raw.models.providers) raw.models.providers = {};
|
|
133
|
+
raw.models.providers[providerId] = {
|
|
134
|
+
baseUrl: config.baseUrl,
|
|
135
|
+
api: config.api || "openai-completions",
|
|
136
|
+
apiKey: config.apiKey || void 0,
|
|
137
|
+
models: config.models?.map((m) => ({ id: m.id, name: m.name })) || [],
|
|
138
|
+
...config.headers ? { headers: config.headers } : {}
|
|
139
|
+
};
|
|
140
|
+
fs.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
141
|
+
}
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
243
145
|
const agentsDir = path.join(os.homedir(), ".openclaw", "agents");
|
|
244
146
|
if (!fs.existsSync(agentsDir)) return;
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (
|
|
250
|
-
fs.mkdirSync(authDir, { recursive: true });
|
|
251
|
-
}
|
|
252
|
-
let profiles = {
|
|
253
|
-
version: 1,
|
|
254
|
-
profiles: {}
|
|
255
|
-
};
|
|
256
|
-
if (fs.existsSync(authPath)) {
|
|
147
|
+
for (const agent of fs.readdirSync(agentsDir)) {
|
|
148
|
+
const modelsPath = path.join(agentsDir, agent, "agent", "models.json");
|
|
149
|
+
if (!fs.existsSync(path.dirname(modelsPath))) continue;
|
|
150
|
+
let data = { providers: {} };
|
|
151
|
+
if (fs.existsSync(modelsPath)) {
|
|
257
152
|
try {
|
|
258
|
-
|
|
153
|
+
data = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
|
|
259
154
|
} catch {
|
|
260
155
|
}
|
|
156
|
+
if (!data.providers) data.providers = {};
|
|
261
157
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
158
|
+
data.providers[providerId] = {
|
|
159
|
+
baseUrl: config.baseUrl,
|
|
160
|
+
api: config.api || "openai-completions",
|
|
161
|
+
apiKey: config.apiKey || void 0,
|
|
162
|
+
models: config.models?.map((m) => ({
|
|
163
|
+
id: m.id,
|
|
164
|
+
name: m.name,
|
|
165
|
+
api: m.api || config.api || "openai-completions",
|
|
166
|
+
reasoning: m.reasoning ?? false,
|
|
167
|
+
input: m.input || ["text"],
|
|
168
|
+
cost: m.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
169
|
+
contextWindow: m.contextWindow || 2e5,
|
|
170
|
+
maxTokens: m.maxTokens || 8192
|
|
171
|
+
})) || [],
|
|
172
|
+
...config.headers ? { headers: config.headers } : {}
|
|
266
173
|
};
|
|
267
|
-
fs.writeFileSync(
|
|
174
|
+
fs.writeFileSync(modelsPath, JSON.stringify(data, null, 2) + "\n");
|
|
268
175
|
}
|
|
269
176
|
} catch {
|
|
270
177
|
}
|
|
271
178
|
}
|
|
179
|
+
function enableProxy(api, baseUrl) {
|
|
180
|
+
const occKey = getApiKey(api);
|
|
181
|
+
if (!occKey) return { proxied: [], skipped: [] };
|
|
182
|
+
const providers = api.config.models?.providers;
|
|
183
|
+
if (!providers) return { proxied: [], skipped: [] };
|
|
184
|
+
const state = readProxyState();
|
|
185
|
+
const proxied = [];
|
|
186
|
+
const skipped = [];
|
|
187
|
+
for (const [id, raw] of Object.entries(providers)) {
|
|
188
|
+
if (id === "opencompress") continue;
|
|
189
|
+
const provider = raw;
|
|
190
|
+
if (provider.baseUrl?.includes("opencompress.ai")) continue;
|
|
191
|
+
const pApi = provider.api || "openai-completions";
|
|
192
|
+
if (pApi === "anthropic-messages" || pApi === "google-generative-ai") {
|
|
193
|
+
skipped.push(`${id} (${pApi})`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
state.originals[id] = {
|
|
197
|
+
baseUrl: provider.baseUrl,
|
|
198
|
+
apiKey: provider.apiKey,
|
|
199
|
+
api: provider.api,
|
|
200
|
+
headers: provider.headers ? { ...provider.headers } : void 0
|
|
201
|
+
};
|
|
202
|
+
provider.headers = {
|
|
203
|
+
...provider.headers || {},
|
|
204
|
+
"X-Upstream-Key": provider.apiKey || "",
|
|
205
|
+
"X-Upstream-Base-Url": provider.baseUrl
|
|
206
|
+
};
|
|
207
|
+
provider.baseUrl = `${baseUrl}/v1`;
|
|
208
|
+
provider.apiKey = occKey;
|
|
209
|
+
persistProviderToDisk(id, provider);
|
|
210
|
+
proxied.push(id);
|
|
211
|
+
}
|
|
212
|
+
state.enabled = true;
|
|
213
|
+
writeProxyState(state);
|
|
214
|
+
return { proxied, skipped };
|
|
215
|
+
}
|
|
216
|
+
function disableProxy(api) {
|
|
217
|
+
const state = readProxyState();
|
|
218
|
+
if (!state.enabled) return [];
|
|
219
|
+
const providers = api.config.models?.providers;
|
|
220
|
+
if (!providers) return [];
|
|
221
|
+
const restored = [];
|
|
222
|
+
for (const [id, orig] of Object.entries(state.originals)) {
|
|
223
|
+
const provider = providers[id];
|
|
224
|
+
if (!provider) continue;
|
|
225
|
+
provider.baseUrl = orig.baseUrl;
|
|
226
|
+
provider.apiKey = orig.apiKey;
|
|
227
|
+
provider.api = orig.api;
|
|
228
|
+
if (orig.headers) {
|
|
229
|
+
provider.headers = orig.headers;
|
|
230
|
+
} else {
|
|
231
|
+
const h = provider.headers;
|
|
232
|
+
if (h) {
|
|
233
|
+
delete h["X-Upstream-Key"];
|
|
234
|
+
delete h["X-Upstream-Base-Url"];
|
|
235
|
+
if (Object.keys(h).length === 0) delete provider.headers;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
persistProviderToDisk(id, provider);
|
|
239
|
+
restored.push(id);
|
|
240
|
+
}
|
|
241
|
+
state.enabled = false;
|
|
242
|
+
state.originals = {};
|
|
243
|
+
writeProxyState(state);
|
|
244
|
+
return restored;
|
|
245
|
+
}
|
|
272
246
|
var opencompressProvider = {
|
|
273
247
|
id: "opencompress",
|
|
274
248
|
label: "OpenCompress",
|
|
275
249
|
docsPath: "https://docs.opencompress.ai",
|
|
276
250
|
aliases: ["oc", "compress"],
|
|
277
251
|
envVars: ["OPENCOMPRESS_API_KEY"],
|
|
278
|
-
models
|
|
252
|
+
// No models — we're a transparent proxy, not a router.
|
|
253
|
+
// Users keep their existing providers; we just compress their traffic.
|
|
254
|
+
models: {
|
|
255
|
+
baseUrl: `${DEFAULT_BASE_URL}/v1`,
|
|
256
|
+
api: "openai-completions",
|
|
257
|
+
models: []
|
|
258
|
+
},
|
|
279
259
|
formatApiKey: (cred) => cred.apiKey || "",
|
|
280
260
|
auth: [
|
|
281
261
|
{
|
|
282
262
|
id: "api-key",
|
|
283
263
|
label: "OpenCompress",
|
|
284
|
-
hint: "
|
|
264
|
+
hint: "Compress all LLM calls automatically \u2014 save 40-60% on any provider",
|
|
285
265
|
kind: "custom",
|
|
286
266
|
run: async (ctx) => {
|
|
287
267
|
ctx.prompter.note(
|
|
288
|
-
"OpenCompress compresses all LLM prompts automatically.\
|
|
268
|
+
"OpenCompress compresses all LLM prompts automatically.\nWorks with any provider you already have (OpenAI, Anthropic, OpenRouter, etc.).\n\nAfter setup, run `/compress on` to enable compression for all providers."
|
|
289
269
|
);
|
|
290
|
-
const
|
|
291
|
-
message: "Enter your LLM API key (OpenAI/Anthropic/OpenRouter):",
|
|
292
|
-
validate: (val) => {
|
|
293
|
-
if (!val || val.length < 10) return "Please enter a valid API key";
|
|
294
|
-
if (val.startsWith("sk-occ-")) return "Enter your LLM provider key, not an OpenCompress key";
|
|
295
|
-
return void 0;
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
if (typeof llmKey === "symbol") {
|
|
299
|
-
throw new Error("Setup cancelled");
|
|
300
|
-
}
|
|
301
|
-
let provider = "openrouter";
|
|
302
|
-
let upstreamBaseUrl = "https://openrouter.ai/api/v1";
|
|
303
|
-
if (llmKey.startsWith("sk-proj-") || llmKey.startsWith("sk-") && !llmKey.startsWith("sk-ant-") && !llmKey.startsWith("sk-or-")) {
|
|
304
|
-
provider = "openai";
|
|
305
|
-
upstreamBaseUrl = "https://api.openai.com/v1";
|
|
306
|
-
} else if (llmKey.startsWith("sk-ant-")) {
|
|
307
|
-
provider = "anthropic";
|
|
308
|
-
upstreamBaseUrl = "https://api.anthropic.com/v1";
|
|
309
|
-
} else if (llmKey.startsWith("AIza")) {
|
|
310
|
-
provider = "google";
|
|
311
|
-
upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
|
|
312
|
-
}
|
|
313
|
-
const spinner = ctx.prompter.progress("Creating account...");
|
|
270
|
+
const spinner = ctx.prompter.progress("Creating OpenCompress account...");
|
|
314
271
|
try {
|
|
315
272
|
const res = await fetch(`${DEFAULT_BASE_URL}/v1/provision`, {
|
|
316
273
|
method: "POST",
|
|
317
274
|
headers: { "Content-Type": "application/json" },
|
|
318
|
-
body: JSON.stringify({
|
|
275
|
+
body: JSON.stringify({})
|
|
319
276
|
});
|
|
320
277
|
if (!res.ok) {
|
|
321
278
|
const err = await res.json().catch(() => ({ error: { message: "Unknown error" } }));
|
|
@@ -326,21 +283,6 @@ var opencompressProvider = {
|
|
|
326
283
|
}
|
|
327
284
|
const data = await res.json();
|
|
328
285
|
spinner.stop("Account created");
|
|
329
|
-
try {
|
|
330
|
-
await fetch(`${DEFAULT_BASE_URL}/v1/byok`, {
|
|
331
|
-
method: "POST",
|
|
332
|
-
headers: {
|
|
333
|
-
Authorization: `Bearer ${data.apiKey}`,
|
|
334
|
-
"Content-Type": "application/json"
|
|
335
|
-
},
|
|
336
|
-
body: JSON.stringify({ provider, passthrough: true })
|
|
337
|
-
});
|
|
338
|
-
} catch {
|
|
339
|
-
}
|
|
340
|
-
const onboardModels = buildProviderModels(DEFAULT_BASE_URL, llmKey, upstreamBaseUrl, void 0, data.apiKey);
|
|
341
|
-
const modelCount = FALLBACK_MODELS.length;
|
|
342
|
-
persistModelsConfig(onboardModels);
|
|
343
|
-
persistAgentModelsJson(onboardModels);
|
|
344
286
|
persistAuthProfile(data.apiKey);
|
|
345
287
|
persistAgentAuthJson(data.apiKey);
|
|
346
288
|
return {
|
|
@@ -350,18 +292,11 @@ var opencompressProvider = {
|
|
|
350
292
|
credential: { apiKey: data.apiKey }
|
|
351
293
|
}
|
|
352
294
|
],
|
|
353
|
-
configPatch: {
|
|
354
|
-
models: {
|
|
355
|
-
providers: {
|
|
356
|
-
opencompress: onboardModels
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
},
|
|
360
|
-
defaultModel: "gpt-4o-mini",
|
|
361
295
|
notes: [
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
`
|
|
296
|
+
"OpenCompress is ready!",
|
|
297
|
+
`Free credit: ${data.freeCredit}`,
|
|
298
|
+
"Run `/compress on` to enable compression for all your LLM providers.",
|
|
299
|
+
"Dashboard: opencompress.ai/dashboard"
|
|
365
300
|
]
|
|
366
301
|
};
|
|
367
302
|
} catch (err) {
|
|
@@ -375,53 +310,17 @@ var opencompressProvider = {
|
|
|
375
310
|
var plugin = {
|
|
376
311
|
id: "opencompress",
|
|
377
312
|
name: "OpenCompress",
|
|
378
|
-
description: "
|
|
313
|
+
description: "Transparent prompt compression \u2014 save 40-60% on any LLM provider",
|
|
379
314
|
version: VERSION,
|
|
380
315
|
register(api) {
|
|
381
316
|
const baseUrl = api.pluginConfig?.baseUrl || DEFAULT_BASE_URL;
|
|
382
|
-
const existingHeaders = api.config.models?.providers?.opencompress?.headers;
|
|
383
|
-
const existingUpstreamKey = existingHeaders?.["X-Upstream-Key"];
|
|
384
|
-
const existingUpstreamBaseUrl = existingHeaders?.["X-Upstream-Base-Url"];
|
|
385
|
-
const existingApiKey = api.config.models?.providers?.opencompress?.apiKey || getApiKey(api);
|
|
386
|
-
const existingModels = readExistingModels(api);
|
|
387
|
-
const providerModels = buildProviderModels(baseUrl, existingUpstreamKey, existingUpstreamBaseUrl, existingModels || void 0, existingApiKey);
|
|
388
|
-
opencompressProvider.models = providerModels;
|
|
389
317
|
api.registerProvider(opencompressProvider);
|
|
390
|
-
if (!api.config.models) {
|
|
391
|
-
api.config.models = { providers: {} };
|
|
392
|
-
}
|
|
393
|
-
if (!api.config.models.providers) {
|
|
394
|
-
api.config.models.providers = {};
|
|
395
|
-
}
|
|
396
|
-
api.config.models.providers.opencompress = providerModels;
|
|
397
|
-
persistModelsConfig(providerModels);
|
|
398
|
-
persistAgentModelsJson(providerModels);
|
|
399
|
-
{
|
|
400
|
-
if (!api.config.agents) api.config.agents = {};
|
|
401
|
-
const agents = api.config.agents;
|
|
402
|
-
if (!agents.defaults) agents.defaults = {};
|
|
403
|
-
const defaults = agents.defaults;
|
|
404
|
-
if (!defaults.models) defaults.models = {};
|
|
405
|
-
const allowlist = defaults.models;
|
|
406
|
-
if (Object.keys(allowlist).length > 0) {
|
|
407
|
-
for (const m of providerModels.models) {
|
|
408
|
-
const fullId = `opencompress/${m.id}`;
|
|
409
|
-
if (!allowlist[fullId]) {
|
|
410
|
-
allowlist[fullId] = {};
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
const modelsForAllowlist = providerModels.models;
|
|
416
|
-
setTimeout(() => injectModelsAllowlist(modelsForAllowlist), 3e3);
|
|
417
318
|
const apiKey = getApiKey(api);
|
|
418
319
|
if (apiKey) {
|
|
419
320
|
persistAuthProfile(apiKey);
|
|
420
321
|
persistAgentAuthJson(apiKey);
|
|
421
322
|
}
|
|
422
|
-
|
|
423
|
-
const source = existingModels ? "from existing providers" : "fallback";
|
|
424
|
-
api.logger.info(`OpenCompress provider registered (${modelCount} models ${source}, 5-layer compression)`);
|
|
323
|
+
api.logger.info("OpenCompress registered (transparent proxy mode)");
|
|
425
324
|
api.registerCommand({
|
|
426
325
|
name: "compress-stats",
|
|
427
326
|
description: "Show OpenCompress usage statistics and savings",
|
|
@@ -471,107 +370,81 @@ var plugin = {
|
|
|
471
370
|
});
|
|
472
371
|
api.logger.info("Registered /compress-stats command");
|
|
473
372
|
api.registerCommand({
|
|
474
|
-
name: "compress
|
|
475
|
-
description: "
|
|
373
|
+
name: "compress",
|
|
374
|
+
description: "Toggle transparent compression for all LLM providers",
|
|
476
375
|
acceptsArgs: true,
|
|
477
376
|
requireAuth: false,
|
|
478
377
|
handler: async (ctx) => {
|
|
479
|
-
const
|
|
480
|
-
if (
|
|
481
|
-
|
|
378
|
+
const arg = ctx.args?.trim().toLowerCase();
|
|
379
|
+
if (arg === "on" || arg === "enable") {
|
|
380
|
+
const occKey2 = getApiKey(api);
|
|
381
|
+
if (!occKey2) {
|
|
382
|
+
return { text: "No API key found. Run `openclaw onboard opencompress` first." };
|
|
383
|
+
}
|
|
384
|
+
const result = enableProxy(api, baseUrl);
|
|
385
|
+
if (result.proxied.length === 0 && result.skipped.length === 0) {
|
|
386
|
+
return { text: "No providers found to proxy. Add LLM providers first." };
|
|
387
|
+
}
|
|
388
|
+
const lines = [
|
|
389
|
+
"**Compression enabled** for all compatible providers.",
|
|
390
|
+
""
|
|
391
|
+
];
|
|
392
|
+
if (result.proxied.length > 0) {
|
|
393
|
+
lines.push(`Proxied (${result.proxied.length}): ${result.proxied.join(", ")}`);
|
|
394
|
+
}
|
|
395
|
+
if (result.skipped.length > 0) {
|
|
396
|
+
lines.push(`Skipped (incompatible format): ${result.skipped.join(", ")}`);
|
|
397
|
+
}
|
|
398
|
+
lines.push("", "All requests now route through OpenCompress for automatic compression.");
|
|
399
|
+
lines.push("To disable: `/compress off`");
|
|
400
|
+
return { text: lines.join("\n") };
|
|
482
401
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
}
|
|
488
|
-
const data = res.ok ? await res.json() : null;
|
|
402
|
+
if (arg === "off" || arg === "disable") {
|
|
403
|
+
const restored = disableProxy(api);
|
|
404
|
+
if (restored.length === 0) {
|
|
405
|
+
return { text: "Compression proxy was not active." };
|
|
406
|
+
}
|
|
489
407
|
return {
|
|
490
408
|
text: [
|
|
491
|
-
"**
|
|
409
|
+
"**Compression disabled.** Restored original provider configs.",
|
|
492
410
|
"",
|
|
493
|
-
|
|
411
|
+
`Restored: ${restored.join(", ")}`,
|
|
494
412
|
"",
|
|
495
|
-
"
|
|
496
|
-
" `/compress-byok sk-proj-xxx` \u2014 Connect OpenAI key",
|
|
497
|
-
" `/compress-byok sk-ant-xxx` \u2014 Connect Anthropic key",
|
|
498
|
-
" `/compress-byok sk-or-xxx` \u2014 Connect OpenRouter key",
|
|
499
|
-
" `/compress-byok off` \u2014 Switch back to router mode",
|
|
500
|
-
"",
|
|
501
|
-
data ? `**Balance:** $${Number(data.balance || 0).toFixed(2)}` : ""
|
|
413
|
+
"To re-enable: `/compress on`"
|
|
502
414
|
].join("\n")
|
|
503
415
|
};
|
|
504
416
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
headers: { Authorization: `Bearer ${apiKey2}` }
|
|
516
|
-
});
|
|
517
|
-
} catch {
|
|
518
|
-
}
|
|
519
|
-
return { text: "Switched back to **router mode**. Your upstream key has been removed from local config." };
|
|
520
|
-
}
|
|
521
|
-
if (upstreamKey.startsWith("sk-occ-")) {
|
|
522
|
-
return { text: "That's an OpenCompress key. Provide your LLM provider key (OpenAI, Anthropic, etc.)." };
|
|
523
|
-
}
|
|
524
|
-
if (upstreamKey.length < 10) {
|
|
525
|
-
return { text: "Key looks too short. Provide your full LLM API key." };
|
|
526
|
-
}
|
|
527
|
-
let provider = "unknown";
|
|
528
|
-
let upstreamBaseUrl = "";
|
|
529
|
-
if (upstreamKey.startsWith("sk-proj-") || upstreamKey.startsWith("sk-") && !upstreamKey.startsWith("sk-ant-") && !upstreamKey.startsWith("sk-or-")) {
|
|
530
|
-
provider = "openai";
|
|
531
|
-
upstreamBaseUrl = "https://api.openai.com/v1";
|
|
532
|
-
} else if (upstreamKey.startsWith("sk-ant-")) {
|
|
533
|
-
provider = "anthropic";
|
|
534
|
-
upstreamBaseUrl = "https://api.anthropic.com/v1";
|
|
535
|
-
} else if (upstreamKey.startsWith("sk-or-")) {
|
|
536
|
-
provider = "openrouter";
|
|
537
|
-
upstreamBaseUrl = "https://openrouter.ai/api/v1";
|
|
538
|
-
} else if (upstreamKey.startsWith("AIza")) {
|
|
539
|
-
provider = "google";
|
|
540
|
-
upstreamBaseUrl = "https://generativelanguage.googleapis.com/v1beta/openai";
|
|
541
|
-
}
|
|
542
|
-
const existingModels2 = readExistingModels(api);
|
|
543
|
-
const updatedModels = buildProviderModels(baseUrl, upstreamKey, upstreamBaseUrl, existingModels2 || void 0);
|
|
544
|
-
if (api.config.models?.providers) {
|
|
545
|
-
api.config.models.providers.opencompress = updatedModels;
|
|
417
|
+
const proxyState = readProxyState();
|
|
418
|
+
const occKey = getApiKey(api);
|
|
419
|
+
const statusLines = [
|
|
420
|
+
"**OpenCompress Transparent Proxy**",
|
|
421
|
+
"",
|
|
422
|
+
`Status: ${proxyState.enabled ? "**ON**" : "**OFF**"}`,
|
|
423
|
+
`API key: ${occKey ? `${occKey.slice(0, 12)}...` : "not set"}`
|
|
424
|
+
];
|
|
425
|
+
if (proxyState.enabled && Object.keys(proxyState.originals).length > 0) {
|
|
426
|
+
statusLines.push(`Proxied providers: ${Object.keys(proxyState.originals).join(", ")}`);
|
|
546
427
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
},
|
|
556
|
-
body: JSON.stringify({ provider, passthrough: true })
|
|
557
|
-
});
|
|
558
|
-
} catch {
|
|
559
|
-
}
|
|
560
|
-
const modelCount2 = existingModels2 ? existingModels2.length : FALLBACK_MODELS.length;
|
|
561
|
-
return {
|
|
562
|
-
text: [
|
|
563
|
-
`Switched to **BYOK mode** (${provider}).`,
|
|
564
|
-
`Loaded **${modelCount2} models** from your ${provider} account.`,
|
|
565
|
-
"",
|
|
566
|
-
"Your key is stored **locally only** \u2014 never sent to our server for storage.",
|
|
567
|
-
"It's passed through on each request via header and discarded immediately.",
|
|
568
|
-
"",
|
|
569
|
-
"To switch back: `/compress-byok off`"
|
|
570
|
-
].join("\n")
|
|
571
|
-
};
|
|
428
|
+
statusLines.push(
|
|
429
|
+
"",
|
|
430
|
+
"**Usage:**",
|
|
431
|
+
" `/compress on` \u2014 Route all providers through OpenCompress",
|
|
432
|
+
" `/compress off` \u2014 Restore original provider configs",
|
|
433
|
+
" `/compress-stats` \u2014 View compression savings"
|
|
434
|
+
);
|
|
435
|
+
return { text: statusLines.join("\n") };
|
|
572
436
|
}
|
|
573
437
|
});
|
|
574
|
-
api.logger.info("Registered /compress
|
|
438
|
+
api.logger.info("Registered /compress command");
|
|
439
|
+
setTimeout(() => {
|
|
440
|
+
const proxyState = readProxyState();
|
|
441
|
+
if (proxyState.enabled) {
|
|
442
|
+
const result = enableProxy(api, baseUrl);
|
|
443
|
+
if (result.proxied.length > 0) {
|
|
444
|
+
api.logger.info(`Proxy auto-applied on startup: ${result.proxied.length} providers (${result.proxied.join(", ")})`);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}, 2e3);
|
|
575
448
|
const os = __require("os");
|
|
576
449
|
const fs = __require("fs");
|
|
577
450
|
const path = __require("path");
|