@ckcloudai.com/clawrouter 0.0.6 → 0.0.8
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 +1004 -296
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -27,9 +27,23 @@ var RpcError = class extends Error {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
// src/plugin-logger.ts
|
|
31
|
+
var pluginLogger = console;
|
|
32
|
+
function setPluginLogger(logger2) {
|
|
33
|
+
pluginLogger = logger2 ?? console;
|
|
34
|
+
}
|
|
35
|
+
var logger = {
|
|
36
|
+
debug: (message) => pluginLogger.debug?.(message),
|
|
37
|
+
info: (message) => pluginLogger.info(message),
|
|
38
|
+
warn: (message) => pluginLogger.warn(message),
|
|
39
|
+
error: (message) => pluginLogger.error(message)
|
|
40
|
+
};
|
|
41
|
+
|
|
30
42
|
// src/balance.ts
|
|
31
43
|
var USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
32
44
|
var CACHE_TTL_MS = 3e4;
|
|
45
|
+
var REMOTE_BALANCE_TIMEOUT_MS = 5e3;
|
|
46
|
+
var REMOTE_BALANCE_URL = "https://t.ckcloudai.com/v1/balance?Pubkey=";
|
|
33
47
|
var BALANCE_THRESHOLDS = {
|
|
34
48
|
/** Low balance warning threshold: $1.00 */
|
|
35
49
|
LOW_BALANCE_MICROS: 1000000n,
|
|
@@ -62,6 +76,14 @@ var BalanceMonitor = class {
|
|
|
62
76
|
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS) {
|
|
63
77
|
return this.buildInfo(this.cachedBalance);
|
|
64
78
|
}
|
|
79
|
+
const remoteBalance = await this.fetchRemoteBalance();
|
|
80
|
+
if (remoteBalance !== null) {
|
|
81
|
+
if (remoteBalance > 0n) {
|
|
82
|
+
this.cachedBalance = remoteBalance;
|
|
83
|
+
this.cachedAt = now;
|
|
84
|
+
}
|
|
85
|
+
return this.buildInfo(remoteBalance);
|
|
86
|
+
}
|
|
65
87
|
const balance = await this.fetchBalance();
|
|
66
88
|
if (balance > 0n) {
|
|
67
89
|
this.cachedBalance = balance;
|
|
@@ -69,6 +91,78 @@ var BalanceMonitor = class {
|
|
|
69
91
|
}
|
|
70
92
|
return this.buildInfo(balance);
|
|
71
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Best-effort remote balance lookup.
|
|
96
|
+
* Returns balance in USDC micros or null if unavailable/invalid.
|
|
97
|
+
*/
|
|
98
|
+
async fetchRemoteBalance() {
|
|
99
|
+
const url = `${REMOTE_BALANCE_URL}${encodeURIComponent(this.walletAddress)}`;
|
|
100
|
+
try {
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
const timeoutId = setTimeout(() => controller.abort(), REMOTE_BALANCE_TIMEOUT_MS);
|
|
103
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
104
|
+
clearTimeout(timeoutId);
|
|
105
|
+
if (!resp.ok) {
|
|
106
|
+
logger.warn(
|
|
107
|
+
`[ckcloud] Remote balance lookup failed (${resp.status}) for ${this.walletAddress}`
|
|
108
|
+
);
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const text = await resp.text();
|
|
112
|
+
const parsed = this.parseRemoteBalance(text);
|
|
113
|
+
if (parsed === null) {
|
|
114
|
+
logger.warn(`[ckcloud] Remote balance lookup returned unparseable data`);
|
|
115
|
+
}
|
|
116
|
+
return parsed;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
logger.warn(
|
|
119
|
+
`[ckcloud] Remote balance lookup error for ${this.walletAddress}: ${err instanceof Error ? err.message : String(err)}`
|
|
120
|
+
);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
parseRemoteBalance(body) {
|
|
125
|
+
const trimmed = body.trim();
|
|
126
|
+
if (!trimmed) return null;
|
|
127
|
+
try {
|
|
128
|
+
const data = JSON.parse(trimmed);
|
|
129
|
+
const extracted = this.extractBalanceValue(data);
|
|
130
|
+
return this.coerceMicros(extracted);
|
|
131
|
+
} catch {
|
|
132
|
+
}
|
|
133
|
+
return this.coerceMicros(trimmed);
|
|
134
|
+
}
|
|
135
|
+
extractBalanceValue(data) {
|
|
136
|
+
if (data && typeof data === "object") {
|
|
137
|
+
const obj = data;
|
|
138
|
+
return obj.balance ?? obj.balance_usd ?? obj.balanceUSD ?? (obj.data && typeof obj.data === "object" ? obj.data.balance ?? obj.data.balance_usd ?? obj.data.balanceUSD : void 0) ?? (obj.result && typeof obj.result === "object" ? obj.result.balance ?? obj.result.balance_usd ?? obj.result.balanceUSD : void 0);
|
|
139
|
+
}
|
|
140
|
+
return data;
|
|
141
|
+
}
|
|
142
|
+
coerceMicros(value) {
|
|
143
|
+
if (value === null || value === void 0) return null;
|
|
144
|
+
if (typeof value === "number") {
|
|
145
|
+
if (!Number.isFinite(value)) return null;
|
|
146
|
+
if (Number.isInteger(value)) return value >= 0 ? BigInt(value) : null;
|
|
147
|
+
return BigInt(Math.round(value * 1e6));
|
|
148
|
+
}
|
|
149
|
+
if (typeof value === "string") {
|
|
150
|
+
const match = value.trim().match(/-?\d+(\.\d+)?/);
|
|
151
|
+
if (!match) return null;
|
|
152
|
+
const s = match[0];
|
|
153
|
+
if (s.includes(".")) {
|
|
154
|
+
const num = Number.parseFloat(s);
|
|
155
|
+
if (!Number.isFinite(num)) return null;
|
|
156
|
+
return BigInt(Math.round(num * 1e6));
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
return BigInt(s);
|
|
160
|
+
} catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
72
166
|
/**
|
|
73
167
|
* Check if balance is sufficient for an estimated cost.
|
|
74
168
|
*
|
|
@@ -157,6 +251,8 @@ var SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
|
157
251
|
var SOLANA_DEFAULT_RPC = "https://api.mainnet-beta.solana.com";
|
|
158
252
|
var BALANCE_TIMEOUT_MS = 1e4;
|
|
159
253
|
var CACHE_TTL_MS2 = 3e4;
|
|
254
|
+
var REMOTE_BALANCE_TIMEOUT_MS2 = 5e3;
|
|
255
|
+
var REMOTE_BALANCE_URL2 = "https://t.ckcloudai.com/v1/balance?Pubkey=";
|
|
160
256
|
var SolanaBalanceMonitor = class {
|
|
161
257
|
rpc;
|
|
162
258
|
walletAddress;
|
|
@@ -172,6 +268,14 @@ var SolanaBalanceMonitor = class {
|
|
|
172
268
|
if (this.cachedBalance !== null && this.cachedBalance > 0n && now - this.cachedAt < CACHE_TTL_MS2) {
|
|
173
269
|
return this.buildInfo(this.cachedBalance);
|
|
174
270
|
}
|
|
271
|
+
const remoteBalance = await this.fetchRemoteBalance();
|
|
272
|
+
if (remoteBalance !== null) {
|
|
273
|
+
if (remoteBalance > 0n) {
|
|
274
|
+
this.cachedBalance = remoteBalance;
|
|
275
|
+
this.cachedAt = now;
|
|
276
|
+
}
|
|
277
|
+
return this.buildInfo(remoteBalance);
|
|
278
|
+
}
|
|
175
279
|
const balance = await this.fetchBalance();
|
|
176
280
|
if (balance > 0n) {
|
|
177
281
|
this.cachedBalance = balance;
|
|
@@ -179,6 +283,78 @@ var SolanaBalanceMonitor = class {
|
|
|
179
283
|
}
|
|
180
284
|
return this.buildInfo(balance);
|
|
181
285
|
}
|
|
286
|
+
/**
|
|
287
|
+
* Best-effort remote balance lookup.
|
|
288
|
+
* Returns balance in USDC micros or null if unavailable/invalid.
|
|
289
|
+
*/
|
|
290
|
+
async fetchRemoteBalance() {
|
|
291
|
+
const url = `${REMOTE_BALANCE_URL2}${encodeURIComponent(this.walletAddress)}`;
|
|
292
|
+
try {
|
|
293
|
+
const controller = new AbortController();
|
|
294
|
+
const timeoutId = setTimeout(() => controller.abort(), REMOTE_BALANCE_TIMEOUT_MS2);
|
|
295
|
+
const resp = await fetch(url, { signal: controller.signal });
|
|
296
|
+
clearTimeout(timeoutId);
|
|
297
|
+
if (!resp.ok) {
|
|
298
|
+
logger.warn(
|
|
299
|
+
`[ckcloud] Remote balance lookup failed (${resp.status}) for ${this.walletAddress}`
|
|
300
|
+
);
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
const text = await resp.text();
|
|
304
|
+
const parsed = this.parseRemoteBalance(text);
|
|
305
|
+
if (parsed === null) {
|
|
306
|
+
logger.warn(`[ckcloud] Remote balance lookup returned unparseable data`);
|
|
307
|
+
}
|
|
308
|
+
return parsed;
|
|
309
|
+
} catch (err) {
|
|
310
|
+
logger.warn(
|
|
311
|
+
`[ckcloud] Remote balance lookup error for ${this.walletAddress}: ${err instanceof Error ? err.message : String(err)}`
|
|
312
|
+
);
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
parseRemoteBalance(body) {
|
|
317
|
+
const trimmed = body.trim();
|
|
318
|
+
if (!trimmed) return null;
|
|
319
|
+
try {
|
|
320
|
+
const data = JSON.parse(trimmed);
|
|
321
|
+
const extracted = this.extractBalanceValue(data);
|
|
322
|
+
return this.coerceMicros(extracted);
|
|
323
|
+
} catch {
|
|
324
|
+
}
|
|
325
|
+
return this.coerceMicros(trimmed);
|
|
326
|
+
}
|
|
327
|
+
extractBalanceValue(data) {
|
|
328
|
+
if (data && typeof data === "object") {
|
|
329
|
+
const obj = data;
|
|
330
|
+
return obj.balance ?? obj.balance_usd ?? obj.balanceUSD ?? (obj.data && typeof obj.data === "object" ? obj.data.balance ?? obj.data.balance_usd ?? obj.data.balanceUSD : void 0) ?? (obj.result && typeof obj.result === "object" ? obj.result.balance ?? obj.result.balance_usd ?? obj.result.balanceUSD : void 0);
|
|
331
|
+
}
|
|
332
|
+
return data;
|
|
333
|
+
}
|
|
334
|
+
coerceMicros(value) {
|
|
335
|
+
if (value === null || value === void 0) return null;
|
|
336
|
+
if (typeof value === "number") {
|
|
337
|
+
if (!Number.isFinite(value)) return null;
|
|
338
|
+
if (Number.isInteger(value)) return value >= 0 ? BigInt(value) : null;
|
|
339
|
+
return BigInt(Math.round(value * 1e6));
|
|
340
|
+
}
|
|
341
|
+
if (typeof value === "string") {
|
|
342
|
+
const match = value.trim().match(/-?\d+(\.\d+)?/);
|
|
343
|
+
if (!match) return null;
|
|
344
|
+
const s = match[0];
|
|
345
|
+
if (s.includes(".")) {
|
|
346
|
+
const num = Number.parseFloat(s);
|
|
347
|
+
if (!Number.isFinite(num)) return null;
|
|
348
|
+
return BigInt(Math.round(num * 1e6));
|
|
349
|
+
}
|
|
350
|
+
try {
|
|
351
|
+
return BigInt(s);
|
|
352
|
+
} catch {
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return null;
|
|
357
|
+
}
|
|
182
358
|
deductEstimated(amountMicros) {
|
|
183
359
|
if (this.cachedBalance !== null && this.cachedBalance >= amountMicros) {
|
|
184
360
|
this.cachedBalance -= amountMicros;
|
|
@@ -799,17 +975,750 @@ async function logUsage(entry) {
|
|
|
799
975
|
}
|
|
800
976
|
}
|
|
801
977
|
|
|
802
|
-
// src/
|
|
803
|
-
var
|
|
804
|
-
|
|
805
|
-
|
|
978
|
+
// src/compression/types.ts
|
|
979
|
+
var DEFAULT_COMPRESSION_CONFIG = {
|
|
980
|
+
enabled: true,
|
|
981
|
+
preserveRaw: true,
|
|
982
|
+
layers: {
|
|
983
|
+
deduplication: true,
|
|
984
|
+
// Safe: removes duplicate messages
|
|
985
|
+
whitespace: true,
|
|
986
|
+
// Safe: normalizes whitespace
|
|
987
|
+
dictionary: false,
|
|
988
|
+
// DISABLED: requires model to understand codebook
|
|
989
|
+
paths: false,
|
|
990
|
+
// DISABLED: requires model to understand path codes
|
|
991
|
+
jsonCompact: true,
|
|
992
|
+
// Safe: just removes JSON whitespace
|
|
993
|
+
observation: false,
|
|
994
|
+
// DISABLED: may lose important context
|
|
995
|
+
dynamicCodebook: false
|
|
996
|
+
// DISABLED: requires model to understand codes
|
|
997
|
+
},
|
|
998
|
+
dictionary: {
|
|
999
|
+
maxEntries: 50,
|
|
1000
|
+
minPhraseLength: 15,
|
|
1001
|
+
includeCodebookHeader: false
|
|
1002
|
+
// No codebook header needed
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
// src/compression/layers/deduplication.ts
|
|
1007
|
+
import crypto from "crypto";
|
|
1008
|
+
function hashMessage(message) {
|
|
1009
|
+
let contentStr = "";
|
|
1010
|
+
if (typeof message.content === "string") {
|
|
1011
|
+
contentStr = message.content;
|
|
1012
|
+
} else if (Array.isArray(message.content)) {
|
|
1013
|
+
contentStr = JSON.stringify(message.content);
|
|
1014
|
+
}
|
|
1015
|
+
const parts = [message.role, contentStr, message.tool_call_id || "", message.name || ""];
|
|
1016
|
+
if (message.tool_calls) {
|
|
1017
|
+
parts.push(
|
|
1018
|
+
JSON.stringify(
|
|
1019
|
+
message.tool_calls.map((tc) => ({
|
|
1020
|
+
name: tc.function.name,
|
|
1021
|
+
args: tc.function.arguments
|
|
1022
|
+
}))
|
|
1023
|
+
)
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
const content = parts.join("|");
|
|
1027
|
+
return crypto.createHash("md5").update(content).digest("hex");
|
|
806
1028
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
1029
|
+
function deduplicateMessages(messages) {
|
|
1030
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1031
|
+
const result = [];
|
|
1032
|
+
let duplicatesRemoved = 0;
|
|
1033
|
+
const referencedToolCallIds = /* @__PURE__ */ new Set();
|
|
1034
|
+
for (const message of messages) {
|
|
1035
|
+
if (message.role === "tool" && message.tool_call_id) {
|
|
1036
|
+
referencedToolCallIds.add(message.tool_call_id);
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
for (const message of messages) {
|
|
1040
|
+
if (message.role === "system") {
|
|
1041
|
+
result.push(message);
|
|
1042
|
+
continue;
|
|
1043
|
+
}
|
|
1044
|
+
if (message.role === "user") {
|
|
1045
|
+
result.push(message);
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
if (message.role === "tool") {
|
|
1049
|
+
result.push(message);
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
if (message.role === "assistant" && message.tool_calls) {
|
|
1053
|
+
const hasReferencedToolCall = message.tool_calls.some(
|
|
1054
|
+
(tc) => referencedToolCallIds.has(tc.id)
|
|
1055
|
+
);
|
|
1056
|
+
if (hasReferencedToolCall) {
|
|
1057
|
+
result.push(message);
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
const hash = hashMessage(message);
|
|
1062
|
+
if (!seen.has(hash)) {
|
|
1063
|
+
seen.add(hash);
|
|
1064
|
+
result.push(message);
|
|
1065
|
+
} else {
|
|
1066
|
+
duplicatesRemoved++;
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
messages: result,
|
|
1071
|
+
duplicatesRemoved,
|
|
1072
|
+
originalCount: messages.length
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// src/compression/layers/whitespace.ts
|
|
1077
|
+
function normalizeWhitespace(content) {
|
|
1078
|
+
if (!content || typeof content !== "string") return content;
|
|
1079
|
+
return content.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]+$/gm, "").replace(/([^\n]) {2,}/g, "$1 ").replace(/^[ ]{8,}/gm, (match) => " ".repeat(Math.ceil(match.length / 4))).replace(/\t/g, " ").trim();
|
|
1080
|
+
}
|
|
1081
|
+
function normalizeMessagesWhitespace(messages) {
|
|
1082
|
+
let charsSaved = 0;
|
|
1083
|
+
const result = messages.map((message) => {
|
|
1084
|
+
if (!message.content || typeof message.content !== "string") return message;
|
|
1085
|
+
const originalLength = message.content.length;
|
|
1086
|
+
const normalizedContent = normalizeWhitespace(message.content);
|
|
1087
|
+
charsSaved += originalLength - normalizedContent.length;
|
|
1088
|
+
return {
|
|
1089
|
+
...message,
|
|
1090
|
+
content: normalizedContent
|
|
1091
|
+
};
|
|
1092
|
+
});
|
|
1093
|
+
return {
|
|
1094
|
+
messages: result,
|
|
1095
|
+
charsSaved
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/compression/codebook.ts
|
|
1100
|
+
var STATIC_CODEBOOK = {
|
|
1101
|
+
// High-impact: OpenClaw/Agent system prompt patterns (very common)
|
|
1102
|
+
$OC01: "unbrowse_",
|
|
1103
|
+
// Common prefix in tool names
|
|
1104
|
+
$OC02: "<location>",
|
|
1105
|
+
$OC03: "</location>",
|
|
1106
|
+
$OC04: "<name>",
|
|
1107
|
+
$OC05: "</name>",
|
|
1108
|
+
$OC06: "<description>",
|
|
1109
|
+
$OC07: "</description>",
|
|
1110
|
+
$OC08: "(may need login)",
|
|
1111
|
+
$OC09: "API skill for OpenClaw",
|
|
1112
|
+
$OC10: "endpoints",
|
|
1113
|
+
// Skill/tool markers
|
|
1114
|
+
$SK01: "<available_skills>",
|
|
1115
|
+
$SK02: "</available_skills>",
|
|
1116
|
+
$SK03: "<skill>",
|
|
1117
|
+
$SK04: "</skill>",
|
|
1118
|
+
// Schema patterns (very common in tool definitions)
|
|
1119
|
+
$T01: 'type: "function"',
|
|
1120
|
+
$T02: '"type": "function"',
|
|
1121
|
+
$T03: '"type": "string"',
|
|
1122
|
+
$T04: '"type": "object"',
|
|
1123
|
+
$T05: '"type": "array"',
|
|
1124
|
+
$T06: '"type": "boolean"',
|
|
1125
|
+
$T07: '"type": "number"',
|
|
1126
|
+
// Common descriptions
|
|
1127
|
+
$D01: "description:",
|
|
1128
|
+
$D02: '"description":',
|
|
1129
|
+
// Common instructions
|
|
1130
|
+
$I01: "You are a personal assistant",
|
|
1131
|
+
$I02: "Tool names are case-sensitive",
|
|
1132
|
+
$I03: "Call tools exactly as listed",
|
|
1133
|
+
$I04: "Use when",
|
|
1134
|
+
$I05: "without asking",
|
|
1135
|
+
// Safety phrases
|
|
1136
|
+
$S01: "Do not manipulate or persuade",
|
|
1137
|
+
$S02: "Prioritize safety and human oversight",
|
|
1138
|
+
$S03: "unless explicitly requested",
|
|
1139
|
+
// JSON patterns
|
|
1140
|
+
$J01: '"required": ["',
|
|
1141
|
+
$J02: '"properties": {',
|
|
1142
|
+
$J03: '"additionalProperties": false',
|
|
1143
|
+
// Heartbeat patterns
|
|
1144
|
+
$H01: "HEARTBEAT_OK",
|
|
1145
|
+
$H02: "Read HEARTBEAT.md if it exists",
|
|
1146
|
+
// Role markers
|
|
1147
|
+
$R01: '"role": "system"',
|
|
1148
|
+
$R02: '"role": "user"',
|
|
1149
|
+
$R03: '"role": "assistant"',
|
|
1150
|
+
$R04: '"role": "tool"',
|
|
1151
|
+
// Common endings/phrases
|
|
1152
|
+
$E01: "would you like to",
|
|
1153
|
+
$E02: "Let me know if you",
|
|
1154
|
+
$E03: "internal APIs",
|
|
1155
|
+
$E04: "session cookies",
|
|
1156
|
+
// ckcloud model aliases (common in prompts)
|
|
1157
|
+
$M01: "ckcloud/",
|
|
1158
|
+
$M02: "openai/",
|
|
1159
|
+
$M03: "anthropic/",
|
|
1160
|
+
$M04: "google/",
|
|
1161
|
+
$M05: "xai/"
|
|
812
1162
|
};
|
|
1163
|
+
function getInverseCodebook() {
|
|
1164
|
+
const inverse = {};
|
|
1165
|
+
for (const [code, phrase] of Object.entries(STATIC_CODEBOOK)) {
|
|
1166
|
+
inverse[phrase] = code;
|
|
1167
|
+
}
|
|
1168
|
+
return inverse;
|
|
1169
|
+
}
|
|
1170
|
+
function generateCodebookHeader(usedCodes, pathMap = {}) {
|
|
1171
|
+
if (usedCodes.size === 0 && Object.keys(pathMap).length === 0) {
|
|
1172
|
+
return "";
|
|
1173
|
+
}
|
|
1174
|
+
const parts = [];
|
|
1175
|
+
if (usedCodes.size > 0) {
|
|
1176
|
+
const codeEntries = Array.from(usedCodes).map((code) => `${code}=${STATIC_CODEBOOK[code]}`).join(", ");
|
|
1177
|
+
parts.push(`[Dict: ${codeEntries}]`);
|
|
1178
|
+
}
|
|
1179
|
+
if (Object.keys(pathMap).length > 0) {
|
|
1180
|
+
const pathEntries = Object.entries(pathMap).map(([code, path]) => `${code}=${path}`).join(", ");
|
|
1181
|
+
parts.push(`[Paths: ${pathEntries}]`);
|
|
1182
|
+
}
|
|
1183
|
+
return parts.join("\n");
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// src/compression/layers/dictionary.ts
|
|
1187
|
+
function encodeContent(content, inverseCodebook) {
|
|
1188
|
+
if (!content || typeof content !== "string") {
|
|
1189
|
+
return { encoded: content, substitutions: 0, codes: /* @__PURE__ */ new Set(), charsSaved: 0 };
|
|
1190
|
+
}
|
|
1191
|
+
let encoded = content;
|
|
1192
|
+
let substitutions = 0;
|
|
1193
|
+
let charsSaved = 0;
|
|
1194
|
+
const codes = /* @__PURE__ */ new Set();
|
|
1195
|
+
const phrases = Object.keys(inverseCodebook).sort((a, b) => b.length - a.length);
|
|
1196
|
+
for (const phrase of phrases) {
|
|
1197
|
+
const code = inverseCodebook[phrase];
|
|
1198
|
+
const regex = new RegExp(escapeRegex(phrase), "g");
|
|
1199
|
+
const matches = encoded.match(regex);
|
|
1200
|
+
if (matches && matches.length > 0) {
|
|
1201
|
+
encoded = encoded.replace(regex, code);
|
|
1202
|
+
substitutions += matches.length;
|
|
1203
|
+
charsSaved += matches.length * (phrase.length - code.length);
|
|
1204
|
+
codes.add(code);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
return { encoded, substitutions, codes, charsSaved };
|
|
1208
|
+
}
|
|
1209
|
+
function escapeRegex(str) {
|
|
1210
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1211
|
+
}
|
|
1212
|
+
function encodeMessages(messages) {
|
|
1213
|
+
const inverseCodebook = getInverseCodebook();
|
|
1214
|
+
let totalSubstitutions = 0;
|
|
1215
|
+
let totalCharsSaved = 0;
|
|
1216
|
+
const allUsedCodes = /* @__PURE__ */ new Set();
|
|
1217
|
+
const result = messages.map((message) => {
|
|
1218
|
+
if (!message.content || typeof message.content !== "string") return message;
|
|
1219
|
+
const { encoded, substitutions, codes, charsSaved } = encodeContent(
|
|
1220
|
+
message.content,
|
|
1221
|
+
inverseCodebook
|
|
1222
|
+
);
|
|
1223
|
+
totalSubstitutions += substitutions;
|
|
1224
|
+
totalCharsSaved += charsSaved;
|
|
1225
|
+
codes.forEach((code) => allUsedCodes.add(code));
|
|
1226
|
+
return {
|
|
1227
|
+
...message,
|
|
1228
|
+
content: encoded
|
|
1229
|
+
};
|
|
1230
|
+
});
|
|
1231
|
+
return {
|
|
1232
|
+
messages: result,
|
|
1233
|
+
substitutionCount: totalSubstitutions,
|
|
1234
|
+
usedCodes: allUsedCodes,
|
|
1235
|
+
charsSaved: totalCharsSaved
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/compression/layers/paths.ts
|
|
1240
|
+
var PATH_REGEX = /(?:\/[\w.-]+){3,}/g;
|
|
1241
|
+
function extractPaths(messages) {
|
|
1242
|
+
const paths = [];
|
|
1243
|
+
for (const message of messages) {
|
|
1244
|
+
if (!message.content || typeof message.content !== "string") continue;
|
|
1245
|
+
const matches = message.content.match(PATH_REGEX);
|
|
1246
|
+
if (matches) {
|
|
1247
|
+
paths.push(...matches);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
return paths;
|
|
1251
|
+
}
|
|
1252
|
+
function findFrequentPrefixes(paths) {
|
|
1253
|
+
const prefixCounts = /* @__PURE__ */ new Map();
|
|
1254
|
+
for (const path of paths) {
|
|
1255
|
+
const parts = path.split("/").filter(Boolean);
|
|
1256
|
+
for (let i = 2; i < parts.length; i++) {
|
|
1257
|
+
const prefix = "/" + parts.slice(0, i).join("/") + "/";
|
|
1258
|
+
prefixCounts.set(prefix, (prefixCounts.get(prefix) || 0) + 1);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
return Array.from(prefixCounts.entries()).filter(([, count]) => count >= 3).sort((a, b) => b[0].length - a[0].length).slice(0, 5).map(([prefix]) => prefix);
|
|
1262
|
+
}
|
|
1263
|
+
function shortenPaths(messages) {
|
|
1264
|
+
const allPaths = extractPaths(messages);
|
|
1265
|
+
if (allPaths.length < 5) {
|
|
1266
|
+
return {
|
|
1267
|
+
messages,
|
|
1268
|
+
pathMap: {},
|
|
1269
|
+
charsSaved: 0
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
const prefixes = findFrequentPrefixes(allPaths);
|
|
1273
|
+
if (prefixes.length === 0) {
|
|
1274
|
+
return {
|
|
1275
|
+
messages,
|
|
1276
|
+
pathMap: {},
|
|
1277
|
+
charsSaved: 0
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
const pathMap = {};
|
|
1281
|
+
prefixes.forEach((prefix, i) => {
|
|
1282
|
+
pathMap[`$P${i + 1}`] = prefix;
|
|
1283
|
+
});
|
|
1284
|
+
let charsSaved = 0;
|
|
1285
|
+
const result = messages.map((message) => {
|
|
1286
|
+
if (!message.content || typeof message.content !== "string") return message;
|
|
1287
|
+
let content = message.content;
|
|
1288
|
+
const originalLength = content.length;
|
|
1289
|
+
for (const [code, prefix] of Object.entries(pathMap)) {
|
|
1290
|
+
content = content.split(prefix).join(code + "/");
|
|
1291
|
+
}
|
|
1292
|
+
charsSaved += originalLength - content.length;
|
|
1293
|
+
return {
|
|
1294
|
+
...message,
|
|
1295
|
+
content
|
|
1296
|
+
};
|
|
1297
|
+
});
|
|
1298
|
+
return {
|
|
1299
|
+
messages: result,
|
|
1300
|
+
pathMap,
|
|
1301
|
+
charsSaved
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// src/compression/layers/json-compact.ts
|
|
1306
|
+
function compactJson(jsonString) {
|
|
1307
|
+
try {
|
|
1308
|
+
const parsed = JSON.parse(jsonString);
|
|
1309
|
+
return JSON.stringify(parsed);
|
|
1310
|
+
} catch {
|
|
1311
|
+
return jsonString;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
function looksLikeJson(str) {
|
|
1315
|
+
const trimmed = str.trim();
|
|
1316
|
+
return trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]");
|
|
1317
|
+
}
|
|
1318
|
+
function compactToolCalls(toolCalls) {
|
|
1319
|
+
return toolCalls.map((tc) => ({
|
|
1320
|
+
...tc,
|
|
1321
|
+
function: {
|
|
1322
|
+
...tc.function,
|
|
1323
|
+
arguments: compactJson(tc.function.arguments)
|
|
1324
|
+
}
|
|
1325
|
+
}));
|
|
1326
|
+
}
|
|
1327
|
+
function compactMessagesJson(messages) {
|
|
1328
|
+
let charsSaved = 0;
|
|
1329
|
+
const result = messages.map((message) => {
|
|
1330
|
+
const newMessage = { ...message };
|
|
1331
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
1332
|
+
const originalLength = JSON.stringify(message.tool_calls).length;
|
|
1333
|
+
newMessage.tool_calls = compactToolCalls(message.tool_calls);
|
|
1334
|
+
const newLength = JSON.stringify(newMessage.tool_calls).length;
|
|
1335
|
+
charsSaved += originalLength - newLength;
|
|
1336
|
+
}
|
|
1337
|
+
if (message.role === "tool" && message.content && typeof message.content === "string" && looksLikeJson(message.content)) {
|
|
1338
|
+
const originalLength = message.content.length;
|
|
1339
|
+
const compacted = compactJson(message.content);
|
|
1340
|
+
charsSaved += originalLength - compacted.length;
|
|
1341
|
+
newMessage.content = compacted;
|
|
1342
|
+
}
|
|
1343
|
+
return newMessage;
|
|
1344
|
+
});
|
|
1345
|
+
return {
|
|
1346
|
+
messages: result,
|
|
1347
|
+
charsSaved
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// src/compression/layers/observation.ts
|
|
1352
|
+
var TOOL_RESULT_THRESHOLD = 500;
|
|
1353
|
+
var COMPRESSED_RESULT_MAX = 300;
|
|
1354
|
+
function compressToolResult(content) {
|
|
1355
|
+
if (!content || content.length <= TOOL_RESULT_THRESHOLD) {
|
|
1356
|
+
return content;
|
|
1357
|
+
}
|
|
1358
|
+
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
1359
|
+
const errorLines = lines.filter(
|
|
1360
|
+
(l) => /error|exception|failed|denied|refused|timeout|invalid/i.test(l) && l.length < 200
|
|
1361
|
+
);
|
|
1362
|
+
const statusLines = lines.filter(
|
|
1363
|
+
(l) => /success|complete|created|updated|found|result|status|total|count/i.test(l) && l.length < 150
|
|
1364
|
+
);
|
|
1365
|
+
const jsonMatches = [];
|
|
1366
|
+
const jsonPattern = /"(id|name|status|error|message|count|total|url|path)":\s*"?([^",}\n]+)"?/gi;
|
|
1367
|
+
let match;
|
|
1368
|
+
while ((match = jsonPattern.exec(content)) !== null) {
|
|
1369
|
+
jsonMatches.push(`${match[1]}: ${match[2].slice(0, 50)}`);
|
|
1370
|
+
}
|
|
1371
|
+
const firstLine = lines[0]?.slice(0, 100);
|
|
1372
|
+
const lastLine = lines.length > 1 ? lines[lines.length - 1]?.slice(0, 100) : "";
|
|
1373
|
+
const parts = [];
|
|
1374
|
+
if (errorLines.length > 0) {
|
|
1375
|
+
parts.push("[ERR] " + errorLines.slice(0, 3).join(" | "));
|
|
1376
|
+
}
|
|
1377
|
+
if (statusLines.length > 0) {
|
|
1378
|
+
parts.push(statusLines.slice(0, 3).join(" | "));
|
|
1379
|
+
}
|
|
1380
|
+
if (jsonMatches.length > 0) {
|
|
1381
|
+
parts.push(jsonMatches.slice(0, 5).join(", "));
|
|
1382
|
+
}
|
|
1383
|
+
if (parts.length === 0) {
|
|
1384
|
+
parts.push(firstLine || "");
|
|
1385
|
+
if (lines.length > 2) {
|
|
1386
|
+
parts.push(`[...${lines.length - 2} lines...]`);
|
|
1387
|
+
}
|
|
1388
|
+
if (lastLine && lastLine !== firstLine) {
|
|
1389
|
+
parts.push(lastLine);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
let result = parts.join("\n");
|
|
1393
|
+
if (result.length > COMPRESSED_RESULT_MAX) {
|
|
1394
|
+
result = result.slice(0, COMPRESSED_RESULT_MAX - 20) + "\n[...truncated]";
|
|
1395
|
+
}
|
|
1396
|
+
return result;
|
|
1397
|
+
}
|
|
1398
|
+
function deduplicateLargeBlocks(messages) {
|
|
1399
|
+
const blockHashes = /* @__PURE__ */ new Map();
|
|
1400
|
+
let charsSaved = 0;
|
|
1401
|
+
const result = messages.map((msg, idx) => {
|
|
1402
|
+
if (!msg.content || typeof msg.content !== "string" || msg.content.length < 500) {
|
|
1403
|
+
return msg;
|
|
1404
|
+
}
|
|
1405
|
+
const blockKey = msg.content.slice(0, 200);
|
|
1406
|
+
if (blockHashes.has(blockKey)) {
|
|
1407
|
+
const firstIdx = blockHashes.get(blockKey);
|
|
1408
|
+
const original = msg.content;
|
|
1409
|
+
const compressed = `[See message #${firstIdx + 1} - same content]`;
|
|
1410
|
+
charsSaved += original.length - compressed.length;
|
|
1411
|
+
return { ...msg, content: compressed };
|
|
1412
|
+
}
|
|
1413
|
+
blockHashes.set(blockKey, idx);
|
|
1414
|
+
return msg;
|
|
1415
|
+
});
|
|
1416
|
+
return { messages: result, charsSaved };
|
|
1417
|
+
}
|
|
1418
|
+
function compressObservations(messages) {
|
|
1419
|
+
let charsSaved = 0;
|
|
1420
|
+
let observationsCompressed = 0;
|
|
1421
|
+
let result = messages.map((msg) => {
|
|
1422
|
+
if (msg.role !== "tool" || !msg.content || typeof msg.content !== "string") {
|
|
1423
|
+
return msg;
|
|
1424
|
+
}
|
|
1425
|
+
const original = msg.content;
|
|
1426
|
+
if (original.length <= TOOL_RESULT_THRESHOLD) {
|
|
1427
|
+
return msg;
|
|
1428
|
+
}
|
|
1429
|
+
const compressed = compressToolResult(original);
|
|
1430
|
+
const saved = original.length - compressed.length;
|
|
1431
|
+
if (saved > 50) {
|
|
1432
|
+
charsSaved += saved;
|
|
1433
|
+
observationsCompressed++;
|
|
1434
|
+
return { ...msg, content: compressed };
|
|
1435
|
+
}
|
|
1436
|
+
return msg;
|
|
1437
|
+
});
|
|
1438
|
+
const dedupResult = deduplicateLargeBlocks(result);
|
|
1439
|
+
result = dedupResult.messages;
|
|
1440
|
+
charsSaved += dedupResult.charsSaved;
|
|
1441
|
+
return {
|
|
1442
|
+
messages: result,
|
|
1443
|
+
charsSaved,
|
|
1444
|
+
observationsCompressed
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// src/compression/layers/dynamic-codebook.ts
|
|
1449
|
+
var MIN_PHRASE_LENGTH = 20;
|
|
1450
|
+
var MAX_PHRASE_LENGTH = 200;
|
|
1451
|
+
var MIN_FREQUENCY = 3;
|
|
1452
|
+
var MAX_ENTRIES = 100;
|
|
1453
|
+
var CODE_PREFIX = "$D";
|
|
1454
|
+
function findRepeatedPhrases(allContent) {
|
|
1455
|
+
const phrases = /* @__PURE__ */ new Map();
|
|
1456
|
+
const segments = allContent.split(/(?<=[.!?\n])\s+/);
|
|
1457
|
+
for (const segment of segments) {
|
|
1458
|
+
const trimmed = segment.trim();
|
|
1459
|
+
if (trimmed.length >= MIN_PHRASE_LENGTH && trimmed.length <= MAX_PHRASE_LENGTH) {
|
|
1460
|
+
phrases.set(trimmed, (phrases.get(trimmed) || 0) + 1);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
const lines = allContent.split("\n");
|
|
1464
|
+
for (const line of lines) {
|
|
1465
|
+
const trimmed = line.trim();
|
|
1466
|
+
if (trimmed.length >= MIN_PHRASE_LENGTH && trimmed.length <= MAX_PHRASE_LENGTH) {
|
|
1467
|
+
phrases.set(trimmed, (phrases.get(trimmed) || 0) + 1);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return phrases;
|
|
1471
|
+
}
|
|
1472
|
+
function buildDynamicCodebook(messages) {
|
|
1473
|
+
let allContent = "";
|
|
1474
|
+
for (const msg of messages) {
|
|
1475
|
+
if (msg.content && typeof msg.content === "string") {
|
|
1476
|
+
allContent += msg.content + "\n";
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const phrases = findRepeatedPhrases(allContent);
|
|
1480
|
+
const candidates = [];
|
|
1481
|
+
for (const [phrase, count] of phrases.entries()) {
|
|
1482
|
+
if (count >= MIN_FREQUENCY) {
|
|
1483
|
+
const codeLength = 4;
|
|
1484
|
+
const savings = (phrase.length - codeLength) * count;
|
|
1485
|
+
if (savings > 50) {
|
|
1486
|
+
candidates.push({ phrase, count, savings });
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
candidates.sort((a, b) => b.savings - a.savings);
|
|
1491
|
+
const topCandidates = candidates.slice(0, MAX_ENTRIES);
|
|
1492
|
+
const codebook = {};
|
|
1493
|
+
topCandidates.forEach((c, i) => {
|
|
1494
|
+
const code = `${CODE_PREFIX}${String(i + 1).padStart(2, "0")}`;
|
|
1495
|
+
codebook[code] = c.phrase;
|
|
1496
|
+
});
|
|
1497
|
+
return codebook;
|
|
1498
|
+
}
|
|
1499
|
+
function escapeRegex2(str) {
|
|
1500
|
+
if (!str || typeof str !== "string") return "";
|
|
1501
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1502
|
+
}
|
|
1503
|
+
function applyDynamicCodebook(messages) {
|
|
1504
|
+
const codebook = buildDynamicCodebook(messages);
|
|
1505
|
+
if (Object.keys(codebook).length === 0) {
|
|
1506
|
+
return {
|
|
1507
|
+
messages,
|
|
1508
|
+
charsSaved: 0,
|
|
1509
|
+
dynamicCodes: {},
|
|
1510
|
+
substitutions: 0
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
const phraseToCode = {};
|
|
1514
|
+
for (const [code, phrase] of Object.entries(codebook)) {
|
|
1515
|
+
phraseToCode[phrase] = code;
|
|
1516
|
+
}
|
|
1517
|
+
const sortedPhrases = Object.keys(phraseToCode).sort((a, b) => b.length - a.length);
|
|
1518
|
+
let charsSaved = 0;
|
|
1519
|
+
let substitutions = 0;
|
|
1520
|
+
const result = messages.map((msg) => {
|
|
1521
|
+
if (!msg.content || typeof msg.content !== "string") return msg;
|
|
1522
|
+
let content = msg.content;
|
|
1523
|
+
for (const phrase of sortedPhrases) {
|
|
1524
|
+
const code = phraseToCode[phrase];
|
|
1525
|
+
const regex = new RegExp(escapeRegex2(phrase), "g");
|
|
1526
|
+
const matches = content.match(regex);
|
|
1527
|
+
if (matches) {
|
|
1528
|
+
content = content.replace(regex, code);
|
|
1529
|
+
charsSaved += (phrase.length - code.length) * matches.length;
|
|
1530
|
+
substitutions += matches.length;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
return { ...msg, content };
|
|
1534
|
+
});
|
|
1535
|
+
return {
|
|
1536
|
+
messages: result,
|
|
1537
|
+
charsSaved,
|
|
1538
|
+
dynamicCodes: codebook,
|
|
1539
|
+
substitutions
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
function generateDynamicCodebookHeader(codebook) {
|
|
1543
|
+
if (Object.keys(codebook).length === 0) return "";
|
|
1544
|
+
const entries = Object.entries(codebook).slice(0, 20).map(([code, phrase]) => {
|
|
1545
|
+
const displayPhrase = phrase.length > 40 ? phrase.slice(0, 37) + "..." : phrase;
|
|
1546
|
+
return `${code}=${displayPhrase}`;
|
|
1547
|
+
}).join(", ");
|
|
1548
|
+
return `[DynDict: ${entries}]`;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// src/compression/index.ts
|
|
1552
|
+
function calculateTotalChars(messages) {
|
|
1553
|
+
return messages.reduce((total, msg) => {
|
|
1554
|
+
let chars = 0;
|
|
1555
|
+
if (typeof msg.content === "string") {
|
|
1556
|
+
chars = msg.content.length;
|
|
1557
|
+
} else if (Array.isArray(msg.content)) {
|
|
1558
|
+
chars = JSON.stringify(msg.content).length;
|
|
1559
|
+
}
|
|
1560
|
+
if (msg.tool_calls) {
|
|
1561
|
+
chars += JSON.stringify(msg.tool_calls).length;
|
|
1562
|
+
}
|
|
1563
|
+
return total + chars;
|
|
1564
|
+
}, 0);
|
|
1565
|
+
}
|
|
1566
|
+
function cloneMessages(messages) {
|
|
1567
|
+
return JSON.parse(JSON.stringify(messages));
|
|
1568
|
+
}
|
|
1569
|
+
function prependCodebookHeader(messages, usedCodes, pathMap) {
|
|
1570
|
+
const header = generateCodebookHeader(usedCodes, pathMap);
|
|
1571
|
+
if (!header) return messages;
|
|
1572
|
+
const userIndex = messages.findIndex((m) => m.role === "user");
|
|
1573
|
+
if (userIndex === -1) {
|
|
1574
|
+
return [{ role: "system", content: header }, ...messages];
|
|
1575
|
+
}
|
|
1576
|
+
return messages.map((msg, i) => {
|
|
1577
|
+
if (i === userIndex) {
|
|
1578
|
+
if (typeof msg.content === "string") {
|
|
1579
|
+
return {
|
|
1580
|
+
...msg,
|
|
1581
|
+
content: `${header}
|
|
1582
|
+
|
|
1583
|
+
${msg.content}`
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
return msg;
|
|
1588
|
+
});
|
|
1589
|
+
}
|
|
1590
|
+
async function compressContext(messages, config = {}) {
|
|
1591
|
+
const fullConfig = {
|
|
1592
|
+
...DEFAULT_COMPRESSION_CONFIG,
|
|
1593
|
+
...config,
|
|
1594
|
+
layers: {
|
|
1595
|
+
...DEFAULT_COMPRESSION_CONFIG.layers,
|
|
1596
|
+
...config.layers
|
|
1597
|
+
},
|
|
1598
|
+
dictionary: {
|
|
1599
|
+
...DEFAULT_COMPRESSION_CONFIG.dictionary,
|
|
1600
|
+
...config.dictionary
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
if (!fullConfig.enabled) {
|
|
1604
|
+
const originalChars2 = calculateTotalChars(messages);
|
|
1605
|
+
return {
|
|
1606
|
+
messages,
|
|
1607
|
+
originalMessages: messages,
|
|
1608
|
+
originalChars: originalChars2,
|
|
1609
|
+
compressedChars: originalChars2,
|
|
1610
|
+
compressionRatio: 1,
|
|
1611
|
+
stats: {
|
|
1612
|
+
duplicatesRemoved: 0,
|
|
1613
|
+
whitespaceSavedChars: 0,
|
|
1614
|
+
dictionarySubstitutions: 0,
|
|
1615
|
+
pathsShortened: 0,
|
|
1616
|
+
jsonCompactedChars: 0,
|
|
1617
|
+
observationsCompressed: 0,
|
|
1618
|
+
observationCharsSaved: 0,
|
|
1619
|
+
dynamicSubstitutions: 0,
|
|
1620
|
+
dynamicCharsSaved: 0
|
|
1621
|
+
},
|
|
1622
|
+
codebook: {},
|
|
1623
|
+
pathMap: {},
|
|
1624
|
+
dynamicCodes: {}
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
const originalMessages = fullConfig.preserveRaw ? cloneMessages(messages) : messages;
|
|
1628
|
+
const originalChars = calculateTotalChars(messages);
|
|
1629
|
+
const stats = {
|
|
1630
|
+
duplicatesRemoved: 0,
|
|
1631
|
+
whitespaceSavedChars: 0,
|
|
1632
|
+
dictionarySubstitutions: 0,
|
|
1633
|
+
pathsShortened: 0,
|
|
1634
|
+
jsonCompactedChars: 0,
|
|
1635
|
+
observationsCompressed: 0,
|
|
1636
|
+
observationCharsSaved: 0,
|
|
1637
|
+
dynamicSubstitutions: 0,
|
|
1638
|
+
dynamicCharsSaved: 0
|
|
1639
|
+
};
|
|
1640
|
+
let result = cloneMessages(messages);
|
|
1641
|
+
let usedCodes = /* @__PURE__ */ new Set();
|
|
1642
|
+
let pathMap = {};
|
|
1643
|
+
let dynamicCodes = {};
|
|
1644
|
+
if (fullConfig.layers.deduplication) {
|
|
1645
|
+
const dedupResult = deduplicateMessages(result);
|
|
1646
|
+
result = dedupResult.messages;
|
|
1647
|
+
stats.duplicatesRemoved = dedupResult.duplicatesRemoved;
|
|
1648
|
+
}
|
|
1649
|
+
if (fullConfig.layers.whitespace) {
|
|
1650
|
+
const wsResult = normalizeMessagesWhitespace(result);
|
|
1651
|
+
result = wsResult.messages;
|
|
1652
|
+
stats.whitespaceSavedChars = wsResult.charsSaved;
|
|
1653
|
+
}
|
|
1654
|
+
if (fullConfig.layers.dictionary) {
|
|
1655
|
+
const dictResult = encodeMessages(result);
|
|
1656
|
+
result = dictResult.messages;
|
|
1657
|
+
stats.dictionarySubstitutions = dictResult.substitutionCount;
|
|
1658
|
+
usedCodes = dictResult.usedCodes;
|
|
1659
|
+
}
|
|
1660
|
+
if (fullConfig.layers.paths) {
|
|
1661
|
+
const pathResult = shortenPaths(result);
|
|
1662
|
+
result = pathResult.messages;
|
|
1663
|
+
pathMap = pathResult.pathMap;
|
|
1664
|
+
stats.pathsShortened = Object.keys(pathMap).length;
|
|
1665
|
+
}
|
|
1666
|
+
if (fullConfig.layers.jsonCompact) {
|
|
1667
|
+
const jsonResult = compactMessagesJson(result);
|
|
1668
|
+
result = jsonResult.messages;
|
|
1669
|
+
stats.jsonCompactedChars = jsonResult.charsSaved;
|
|
1670
|
+
}
|
|
1671
|
+
if (fullConfig.layers.observation) {
|
|
1672
|
+
const obsResult = compressObservations(result);
|
|
1673
|
+
result = obsResult.messages;
|
|
1674
|
+
stats.observationsCompressed = obsResult.observationsCompressed;
|
|
1675
|
+
stats.observationCharsSaved = obsResult.charsSaved;
|
|
1676
|
+
}
|
|
1677
|
+
if (fullConfig.layers.dynamicCodebook) {
|
|
1678
|
+
const dynResult = applyDynamicCodebook(result);
|
|
1679
|
+
result = dynResult.messages;
|
|
1680
|
+
stats.dynamicSubstitutions = dynResult.substitutions;
|
|
1681
|
+
stats.dynamicCharsSaved = dynResult.charsSaved;
|
|
1682
|
+
dynamicCodes = dynResult.dynamicCodes;
|
|
1683
|
+
}
|
|
1684
|
+
if (fullConfig.dictionary.includeCodebookHeader && (usedCodes.size > 0 || Object.keys(pathMap).length > 0 || Object.keys(dynamicCodes).length > 0)) {
|
|
1685
|
+
result = prependCodebookHeader(result, usedCodes, pathMap);
|
|
1686
|
+
if (Object.keys(dynamicCodes).length > 0) {
|
|
1687
|
+
const dynHeader = generateDynamicCodebookHeader(dynamicCodes);
|
|
1688
|
+
if (dynHeader) {
|
|
1689
|
+
const systemIndex = result.findIndex((m) => m.role === "system");
|
|
1690
|
+
if (systemIndex >= 0 && typeof result[systemIndex].content === "string") {
|
|
1691
|
+
result[systemIndex] = {
|
|
1692
|
+
...result[systemIndex],
|
|
1693
|
+
content: `${dynHeader}
|
|
1694
|
+
${result[systemIndex].content}`
|
|
1695
|
+
};
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
const compressedChars = calculateTotalChars(result);
|
|
1701
|
+
const compressionRatio = compressedChars / originalChars;
|
|
1702
|
+
const usedCodebook = {};
|
|
1703
|
+
usedCodes.forEach((code) => {
|
|
1704
|
+
usedCodebook[code] = STATIC_CODEBOOK[code];
|
|
1705
|
+
});
|
|
1706
|
+
return {
|
|
1707
|
+
messages: result,
|
|
1708
|
+
originalMessages,
|
|
1709
|
+
originalChars,
|
|
1710
|
+
compressedChars,
|
|
1711
|
+
compressionRatio,
|
|
1712
|
+
stats,
|
|
1713
|
+
codebook: usedCodebook,
|
|
1714
|
+
pathMap,
|
|
1715
|
+
dynamicCodes
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
function shouldCompress(messages) {
|
|
1719
|
+
const chars = calculateTotalChars(messages);
|
|
1720
|
+
return chars > 5e3;
|
|
1721
|
+
}
|
|
813
1722
|
|
|
814
1723
|
// src/session.ts
|
|
815
1724
|
import { createHash } from "crypto";
|
|
@@ -1312,6 +2221,12 @@ function getFallbackChainFiltered(tier, tierConfigs, estimatedTotalTokens, getCo
|
|
|
1312
2221
|
}
|
|
1313
2222
|
|
|
1314
2223
|
// src/router/config.ts
|
|
2224
|
+
var PLACEHOLDER_TIER_CONFIG = () => ({
|
|
2225
|
+
SIMPLE: { primary: "auto", fallback: [] },
|
|
2226
|
+
MEDIUM: { primary: "auto", fallback: [] },
|
|
2227
|
+
COMPLEX: { primary: "auto", fallback: [] },
|
|
2228
|
+
REASONING: { primary: "auto", fallback: [] }
|
|
2229
|
+
});
|
|
1315
2230
|
var DEFAULT_ROUTING_CONFIG = {
|
|
1316
2231
|
version: "2.0",
|
|
1317
2232
|
classifier: {
|
|
@@ -2337,291 +3252,11 @@ var DEFAULT_ROUTING_CONFIG = {
|
|
|
2337
3252
|
// Below this confidence → ambiguous (null tier)
|
|
2338
3253
|
confidenceThreshold: 0.7
|
|
2339
3254
|
},
|
|
2340
|
-
//
|
|
2341
|
-
tiers:
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
fallback: [
|
|
2346
|
-
"openai/gpt-5-nano",
|
|
2347
|
-
// $0.05 / $0.40
|
|
2348
|
-
"gpt-4.1-nano",
|
|
2349
|
-
// $0.10 / $0.40
|
|
2350
|
-
"gemini-2.5-flash",
|
|
2351
|
-
// $0.10 / $0.40
|
|
2352
|
-
"gpt-4o-mini",
|
|
2353
|
-
// $0.15 / $0.60
|
|
2354
|
-
"openai/gpt-5-mini",
|
|
2355
|
-
// $0.25 / $2.00
|
|
2356
|
-
"deepseek-ai/DeepSeek-V3.2",
|
|
2357
|
-
// $0.28 / $0.42
|
|
2358
|
-
"MiniMax-M2.5",
|
|
2359
|
-
// $0.30 / $1.20
|
|
2360
|
-
"gemini-2.5-flash"
|
|
2361
|
-
// $0.30 / $2.50
|
|
2362
|
-
]
|
|
2363
|
-
},
|
|
2364
|
-
MEDIUM: {
|
|
2365
|
-
primary: "gpt-4.1-mini",
|
|
2366
|
-
// $0.40 / $1.60
|
|
2367
|
-
fallback: [
|
|
2368
|
-
"zai-org/glm-5",
|
|
2369
|
-
// $1.00 / $3.20
|
|
2370
|
-
"glm-5-turbo",
|
|
2371
|
-
// $1.20 / $4.00
|
|
2372
|
-
"openai/gpt-4.1",
|
|
2373
|
-
// $2.00 / $8.00
|
|
2374
|
-
"o3-2025-04-16",
|
|
2375
|
-
// $2.00 / $8.00
|
|
2376
|
-
"gemini-3-pro-preview",
|
|
2377
|
-
// $2.00 / $12.00
|
|
2378
|
-
"gemini-3.1-pro-preview"
|
|
2379
|
-
// $2.00 / $12.00
|
|
2380
|
-
]
|
|
2381
|
-
},
|
|
2382
|
-
COMPLEX: {
|
|
2383
|
-
primary: "gpt-5.4",
|
|
2384
|
-
// $2.50 / $15.00
|
|
2385
|
-
fallback: [
|
|
2386
|
-
"openai/gpt-5.2",
|
|
2387
|
-
// $1.75 / $14.00
|
|
2388
|
-
"gpt-5.3-codex",
|
|
2389
|
-
// $1.75 / $14.00
|
|
2390
|
-
"openai/gpt-4o",
|
|
2391
|
-
// $2.50 / $10.00
|
|
2392
|
-
"claude-sonnet-4-6",
|
|
2393
|
-
// $3.00 / $15.00
|
|
2394
|
-
"claude-opus-4-5-20251101",
|
|
2395
|
-
// $5.00 / $25.00
|
|
2396
|
-
"claude-opus-4-6"
|
|
2397
|
-
// $5.00 / $25.00
|
|
2398
|
-
]
|
|
2399
|
-
},
|
|
2400
|
-
REASONING: {
|
|
2401
|
-
primary: "o4-mini",
|
|
2402
|
-
// $1.10 / $4.40
|
|
2403
|
-
fallback: [
|
|
2404
|
-
"o1",
|
|
2405
|
-
// $15.00 / $60.00
|
|
2406
|
-
"gpt-5.2-pro"
|
|
2407
|
-
// $21.00 / $168.00
|
|
2408
|
-
]
|
|
2409
|
-
}
|
|
2410
|
-
},
|
|
2411
|
-
// Eco tier configs - absolute cheapest (ckcloud/eco)
|
|
2412
|
-
ecoTiers: {
|
|
2413
|
-
SIMPLE: {
|
|
2414
|
-
primary: "moonshot/kimi-k2.5",
|
|
2415
|
-
// $0.60 / $3.00
|
|
2416
|
-
fallback: [
|
|
2417
|
-
"openai/gpt-5-nano",
|
|
2418
|
-
// $0.05 / $0.40
|
|
2419
|
-
"gpt-4.1-nano",
|
|
2420
|
-
// $0.10 / $0.40
|
|
2421
|
-
"gemini-2.5-flash",
|
|
2422
|
-
// $0.10 / $0.40
|
|
2423
|
-
"gpt-4o-mini",
|
|
2424
|
-
// $0.15 / $0.60
|
|
2425
|
-
"openai/gpt-5-mini",
|
|
2426
|
-
// $0.25 / $2.00
|
|
2427
|
-
"deepseek-ai/DeepSeek-V3.2",
|
|
2428
|
-
// $0.28 / $0.42
|
|
2429
|
-
"MiniMax-M2.5",
|
|
2430
|
-
// $0.30 / $1.20
|
|
2431
|
-
"gemini-2.5-flash"
|
|
2432
|
-
// $0.30 / $2.50
|
|
2433
|
-
]
|
|
2434
|
-
},
|
|
2435
|
-
MEDIUM: {
|
|
2436
|
-
primary: "gpt-4.1-mini",
|
|
2437
|
-
// $0.40 / $1.60
|
|
2438
|
-
fallback: [
|
|
2439
|
-
"zai-org/glm-5",
|
|
2440
|
-
// $1.00 / $3.20
|
|
2441
|
-
"glm-5-turbo",
|
|
2442
|
-
// $1.20 / $4.00
|
|
2443
|
-
"openai/gpt-4.1",
|
|
2444
|
-
// $2.00 / $8.00
|
|
2445
|
-
"o3-2025-04-16",
|
|
2446
|
-
// $2.00 / $8.00
|
|
2447
|
-
"gemini-3-pro-preview",
|
|
2448
|
-
// $2.00 / $12.00
|
|
2449
|
-
"gemini-3.1-pro-preview"
|
|
2450
|
-
// $2.00 / $12.00
|
|
2451
|
-
]
|
|
2452
|
-
},
|
|
2453
|
-
COMPLEX: {
|
|
2454
|
-
primary: "gpt-5.4",
|
|
2455
|
-
// $2.50 / $15.00
|
|
2456
|
-
fallback: [
|
|
2457
|
-
"openai/gpt-5.2",
|
|
2458
|
-
// $1.75 / $14.00
|
|
2459
|
-
"gpt-5.3-codex",
|
|
2460
|
-
// $1.75 / $14.00
|
|
2461
|
-
"openai/gpt-4o",
|
|
2462
|
-
// $2.50 / $10.00
|
|
2463
|
-
"claude-sonnet-4-6",
|
|
2464
|
-
// $3.00 / $15.00
|
|
2465
|
-
"claude-opus-4-5-20251101",
|
|
2466
|
-
// $5.00 / $25.00
|
|
2467
|
-
"claude-opus-4-6"
|
|
2468
|
-
// $5.00 / $25.00
|
|
2469
|
-
]
|
|
2470
|
-
},
|
|
2471
|
-
REASONING: {
|
|
2472
|
-
primary: "o4-mini",
|
|
2473
|
-
// $1.10 / $4.40
|
|
2474
|
-
fallback: [
|
|
2475
|
-
"o1",
|
|
2476
|
-
// $15.00 / $60.00
|
|
2477
|
-
"gpt-5.2-pro"
|
|
2478
|
-
// $21.00 / $168.00
|
|
2479
|
-
]
|
|
2480
|
-
}
|
|
2481
|
-
},
|
|
2482
|
-
// Premium tier configs - best quality (ckcloud/premium)
|
|
2483
|
-
// codex=complex coding, kimi=simple coding, sonnet=reasoning/instructions, opus=architecture/PM/audits
|
|
2484
|
-
premiumTiers: {
|
|
2485
|
-
SIMPLE: {
|
|
2486
|
-
primary: "moonshot/kimi-k2.5",
|
|
2487
|
-
// $0.60 / $3.00
|
|
2488
|
-
fallback: [
|
|
2489
|
-
"openai/gpt-5-nano",
|
|
2490
|
-
// $0.05 / $0.40
|
|
2491
|
-
"gpt-4.1-nano",
|
|
2492
|
-
// $0.10 / $0.40
|
|
2493
|
-
"gemini-2.5-flash",
|
|
2494
|
-
// $0.10 / $0.40
|
|
2495
|
-
"gpt-4o-mini",
|
|
2496
|
-
// $0.15 / $0.60
|
|
2497
|
-
"openai/gpt-5-mini",
|
|
2498
|
-
// $0.25 / $2.00
|
|
2499
|
-
"deepseek-ai/DeepSeek-V3.2",
|
|
2500
|
-
// $0.28 / $0.42
|
|
2501
|
-
"MiniMax-M2.5",
|
|
2502
|
-
// $0.30 / $1.20
|
|
2503
|
-
"gemini-2.5-flash"
|
|
2504
|
-
// $0.30 / $2.50
|
|
2505
|
-
]
|
|
2506
|
-
},
|
|
2507
|
-
MEDIUM: {
|
|
2508
|
-
primary: "gpt-4.1-mini",
|
|
2509
|
-
// $0.40 / $1.60
|
|
2510
|
-
fallback: [
|
|
2511
|
-
"zai-org/glm-5",
|
|
2512
|
-
// $1.00 / $3.20
|
|
2513
|
-
"glm-5-turbo",
|
|
2514
|
-
// $1.20 / $4.00
|
|
2515
|
-
"openai/gpt-4.1",
|
|
2516
|
-
// $2.00 / $8.00
|
|
2517
|
-
"o3-2025-04-16",
|
|
2518
|
-
// $2.00 / $8.00
|
|
2519
|
-
"gemini-3-pro-preview",
|
|
2520
|
-
// $2.00 / $12.00
|
|
2521
|
-
"gemini-3.1-pro-preview"
|
|
2522
|
-
// $2.00 / $12.00
|
|
2523
|
-
]
|
|
2524
|
-
},
|
|
2525
|
-
COMPLEX: {
|
|
2526
|
-
primary: "gpt-5.4",
|
|
2527
|
-
// $2.50 / $15.00
|
|
2528
|
-
fallback: [
|
|
2529
|
-
"openai/gpt-5.2",
|
|
2530
|
-
// $1.75 / $14.00
|
|
2531
|
-
"gpt-5.3-codex",
|
|
2532
|
-
// $1.75 / $14.00
|
|
2533
|
-
"openai/gpt-4o",
|
|
2534
|
-
// $2.50 / $10.00
|
|
2535
|
-
"claude-sonnet-4-6",
|
|
2536
|
-
// $3.00 / $15.00
|
|
2537
|
-
"claude-opus-4-5-20251101",
|
|
2538
|
-
// $5.00 / $25.00
|
|
2539
|
-
"claude-opus-4-6"
|
|
2540
|
-
// $5.00 / $25.00
|
|
2541
|
-
]
|
|
2542
|
-
},
|
|
2543
|
-
REASONING: {
|
|
2544
|
-
primary: "o4-mini",
|
|
2545
|
-
// $1.10 / $4.40
|
|
2546
|
-
fallback: [
|
|
2547
|
-
"o1",
|
|
2548
|
-
// $15.00 / $60.00
|
|
2549
|
-
"gpt-5.2-pro"
|
|
2550
|
-
// $21.00 / $168.00
|
|
2551
|
-
]
|
|
2552
|
-
}
|
|
2553
|
-
},
|
|
2554
|
-
// Agentic tier configs - models that excel at multi-step autonomous tasks
|
|
2555
|
-
agenticTiers: {
|
|
2556
|
-
SIMPLE: {
|
|
2557
|
-
primary: "moonshot/kimi-k2.5",
|
|
2558
|
-
// $0.60 / $3.00
|
|
2559
|
-
fallback: [
|
|
2560
|
-
"openai/gpt-5-nano",
|
|
2561
|
-
// $0.05 / $0.40
|
|
2562
|
-
"gpt-4.1-nano",
|
|
2563
|
-
// $0.10 / $0.40
|
|
2564
|
-
"gemini-2.5-flash",
|
|
2565
|
-
// $0.10 / $0.40
|
|
2566
|
-
"gpt-4o-mini",
|
|
2567
|
-
// $0.15 / $0.60
|
|
2568
|
-
"openai/gpt-5-mini",
|
|
2569
|
-
// $0.25 / $2.00
|
|
2570
|
-
"deepseek-ai/DeepSeek-V3.2",
|
|
2571
|
-
// $0.28 / $0.42
|
|
2572
|
-
"MiniMax-M2.5",
|
|
2573
|
-
// $0.30 / $1.20
|
|
2574
|
-
"gemini-2.5-flash"
|
|
2575
|
-
// $0.30 / $2.50
|
|
2576
|
-
]
|
|
2577
|
-
},
|
|
2578
|
-
MEDIUM: {
|
|
2579
|
-
primary: "gpt-4.1-mini",
|
|
2580
|
-
// $0.40 / $1.60
|
|
2581
|
-
fallback: [
|
|
2582
|
-
"zai-org/glm-5",
|
|
2583
|
-
// $1.00 / $3.20
|
|
2584
|
-
"glm-5-turbo",
|
|
2585
|
-
// $1.20 / $4.00
|
|
2586
|
-
"openai/gpt-4.1",
|
|
2587
|
-
// $2.00 / $8.00
|
|
2588
|
-
"o3-2025-04-16",
|
|
2589
|
-
// $2.00 / $8.00
|
|
2590
|
-
"gemini-3-pro-preview",
|
|
2591
|
-
// $2.00 / $12.00
|
|
2592
|
-
"gemini-3.1-pro-preview"
|
|
2593
|
-
// $2.00 / $12.00
|
|
2594
|
-
]
|
|
2595
|
-
},
|
|
2596
|
-
COMPLEX: {
|
|
2597
|
-
primary: "gpt-5.4",
|
|
2598
|
-
// $2.50 / $15.00
|
|
2599
|
-
fallback: [
|
|
2600
|
-
"openai/gpt-5.2",
|
|
2601
|
-
// $1.75 / $14.00
|
|
2602
|
-
"gpt-5.3-codex",
|
|
2603
|
-
// $1.75 / $14.00
|
|
2604
|
-
"openai/gpt-4o",
|
|
2605
|
-
// $2.50 / $10.00
|
|
2606
|
-
"claude-sonnet-4-6",
|
|
2607
|
-
// $3.00 / $15.00
|
|
2608
|
-
"claude-opus-4-5-20251101",
|
|
2609
|
-
// $5.00 / $25.00
|
|
2610
|
-
"claude-opus-4-6"
|
|
2611
|
-
// $5.00 / $25.00
|
|
2612
|
-
]
|
|
2613
|
-
},
|
|
2614
|
-
REASONING: {
|
|
2615
|
-
primary: "o4-mini",
|
|
2616
|
-
// $1.10 / $4.40
|
|
2617
|
-
fallback: [
|
|
2618
|
-
"o1",
|
|
2619
|
-
// $15.00 / $60.00
|
|
2620
|
-
"gpt-5.2-pro"
|
|
2621
|
-
// $21.00 / $168.00
|
|
2622
|
-
]
|
|
2623
|
-
}
|
|
2624
|
-
},
|
|
3255
|
+
// Tier configs are populated at runtime based on initBaseModels().
|
|
3256
|
+
tiers: PLACEHOLDER_TIER_CONFIG(),
|
|
3257
|
+
ecoTiers: PLACEHOLDER_TIER_CONFIG(),
|
|
3258
|
+
premiumTiers: PLACEHOLDER_TIER_CONFIG(),
|
|
3259
|
+
agenticTiers: PLACEHOLDER_TIER_CONFIG(),
|
|
2625
3260
|
overrides: {
|
|
2626
3261
|
maxTokensForceComplex: 1e5,
|
|
2627
3262
|
structuredOutputMinTier: "MEDIUM",
|
|
@@ -3966,6 +4601,7 @@ var CKCLOUD_API = "https://t.ckcloudai.com/x402";
|
|
|
3966
4601
|
var CKCLOUD_SOLANA_API = "https://https://t.ckcloudai.com/x402";
|
|
3967
4602
|
var IMAGE_DIR = join5(homedir4(), ".openclaw", "ckcloud", "images");
|
|
3968
4603
|
var AUTO_MODEL = "ckcloud/auto";
|
|
4604
|
+
var BASE_AUTO_MODEL_ID = "auto";
|
|
3969
4605
|
var BALANCE_CHECK_BUFFER = 1.5;
|
|
3970
4606
|
var ROUTING_PROFILES = /* @__PURE__ */ new Set([
|
|
3971
4607
|
"ckcloud/eco",
|
|
@@ -4450,11 +5086,72 @@ async function checkExistingProxy(port) {
|
|
|
4450
5086
|
function buildModelPricing() {
|
|
4451
5087
|
const map = /* @__PURE__ */ new Map();
|
|
4452
5088
|
for (const m of BASE_MODELS) {
|
|
4453
|
-
if (m.id === AUTO_MODEL) continue;
|
|
5089
|
+
if (m.id === AUTO_MODEL || m.id === BASE_AUTO_MODEL_ID) continue;
|
|
4454
5090
|
map.set(m.id, { inputPrice: m.inputPrice, outputPrice: m.outputPrice });
|
|
4455
5091
|
}
|
|
4456
5092
|
return map;
|
|
4457
5093
|
}
|
|
5094
|
+
function buildTierConfigFromModels(candidates, fallbackPool) {
|
|
5095
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
5096
|
+
const priceDelta = a.outputPrice - b.outputPrice;
|
|
5097
|
+
if (priceDelta !== 0) return priceDelta;
|
|
5098
|
+
return a.id.localeCompare(b.id);
|
|
5099
|
+
});
|
|
5100
|
+
const pool = sorted.length > 0 ? sorted : fallbackPool;
|
|
5101
|
+
const primary = pool[0];
|
|
5102
|
+
if (!primary) {
|
|
5103
|
+
return { primary: BASE_AUTO_MODEL_ID, fallback: [] };
|
|
5104
|
+
}
|
|
5105
|
+
return {
|
|
5106
|
+
primary: primary.id,
|
|
5107
|
+
fallback: pool.slice(1).map((m) => m.id)
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
function buildTierConfigsFromBaseModels() {
|
|
5111
|
+
const available = BASE_MODELS.filter((m) => m.id !== BASE_AUTO_MODEL_ID);
|
|
5112
|
+
const sortedAll = [...available].sort((a, b) => {
|
|
5113
|
+
const priceDelta = a.outputPrice - b.outputPrice;
|
|
5114
|
+
if (priceDelta !== 0) return priceDelta;
|
|
5115
|
+
return a.id.localeCompare(b.id);
|
|
5116
|
+
});
|
|
5117
|
+
const fallbackPool = sortedAll.length > 0 ? sortedAll : available;
|
|
5118
|
+
const matchesThink = (name) => typeof name === "string" && name.toLowerCase().includes("think");
|
|
5119
|
+
const reasoningModels = available.filter((m) => matchesThink(m.id) || matchesThink(m.name));
|
|
5120
|
+
const reasoningTier = buildTierConfigFromModels(reasoningModels, fallbackPool);
|
|
5121
|
+
const inRange = (price, min, max, includeMax) => {
|
|
5122
|
+
if (!Number.isFinite(price)) return false;
|
|
5123
|
+
if (includeMax) return price >= min && price <= max;
|
|
5124
|
+
return price >= min && price < max;
|
|
5125
|
+
};
|
|
5126
|
+
const selectByRange = (min, max, includeMax) => buildTierConfigFromModels(
|
|
5127
|
+
available.filter((m) => inRange(m.outputPrice, min, max, includeMax)),
|
|
5128
|
+
fallbackPool
|
|
5129
|
+
);
|
|
5130
|
+
const tiers = {
|
|
5131
|
+
SIMPLE: selectByRange(0, 1.5, false),
|
|
5132
|
+
MEDIUM: selectByRange(1.5, 3, false),
|
|
5133
|
+
COMPLEX: selectByRange(3, 100, true),
|
|
5134
|
+
REASONING: reasoningTier
|
|
5135
|
+
};
|
|
5136
|
+
const ecoTiers = {
|
|
5137
|
+
SIMPLE: selectByRange(0, 1, false),
|
|
5138
|
+
MEDIUM: selectByRange(1, 2, false),
|
|
5139
|
+
COMPLEX: selectByRange(2, 3, true),
|
|
5140
|
+
REASONING: reasoningTier
|
|
5141
|
+
};
|
|
5142
|
+
const premiumTiers = {
|
|
5143
|
+
SIMPLE: selectByRange(1, 3, false),
|
|
5144
|
+
MEDIUM: selectByRange(3, 10, false),
|
|
5145
|
+
COMPLEX: selectByRange(10, 100, true),
|
|
5146
|
+
REASONING: reasoningTier
|
|
5147
|
+
};
|
|
5148
|
+
return {
|
|
5149
|
+
tiers,
|
|
5150
|
+
ecoTiers,
|
|
5151
|
+
premiumTiers,
|
|
5152
|
+
agenticTiers: tiers
|
|
5153
|
+
};
|
|
5154
|
+
}
|
|
4458
5155
|
function buildProxyModelList(createdAt = Math.floor(Date.now() / 1e3)) {
|
|
4459
5156
|
const seen = /* @__PURE__ */ new Set();
|
|
4460
5157
|
return OPENCLAW_MODELS.filter((model) => {
|
|
@@ -4476,6 +5173,9 @@ function mergeRoutingConfig(overrides) {
|
|
|
4476
5173
|
classifier: { ...DEFAULT_ROUTING_CONFIG.classifier, ...overrides.classifier },
|
|
4477
5174
|
scoring: { ...DEFAULT_ROUTING_CONFIG.scoring, ...overrides.scoring },
|
|
4478
5175
|
tiers: { ...DEFAULT_ROUTING_CONFIG.tiers, ...overrides.tiers },
|
|
5176
|
+
ecoTiers: { ...DEFAULT_ROUTING_CONFIG.ecoTiers, ...overrides.ecoTiers },
|
|
5177
|
+
premiumTiers: { ...DEFAULT_ROUTING_CONFIG.premiumTiers, ...overrides.premiumTiers },
|
|
5178
|
+
agenticTiers: { ...DEFAULT_ROUTING_CONFIG.agenticTiers, ...overrides.agenticTiers },
|
|
4479
5179
|
overrides: { ...DEFAULT_ROUTING_CONFIG.overrides, ...overrides.overrides }
|
|
4480
5180
|
};
|
|
4481
5181
|
}
|
|
@@ -4589,7 +5289,13 @@ async function startProxy(options) {
|
|
|
4589
5289
|
const solanaPrivateKeyBytes = typeof options.wallet === "string" ? void 0 : options.wallet.solanaPrivateKeyBytes;
|
|
4590
5290
|
setPluginLogger(options.logger ?? console);
|
|
4591
5291
|
await initBaseModels();
|
|
5292
|
+
const tierConfigs = buildTierConfigsFromBaseModels();
|
|
5293
|
+
DEFAULT_ROUTING_CONFIG.tiers = tierConfigs.tiers;
|
|
5294
|
+
DEFAULT_ROUTING_CONFIG.ecoTiers = tierConfigs.ecoTiers;
|
|
5295
|
+
DEFAULT_ROUTING_CONFIG.premiumTiers = tierConfigs.premiumTiers;
|
|
5296
|
+
DEFAULT_ROUTING_CONFIG.agenticTiers = tierConfigs.agenticTiers;
|
|
4592
5297
|
const paymentChain = options.paymentChain ?? await resolvePaymentChain();
|
|
5298
|
+
logger.info(`paymentChain: ${paymentChain}`);
|
|
4593
5299
|
const apiBase = options.apiBase ?? (paymentChain === "solana" && solanaPrivateKeyBytes ? CKCLOUD_SOLANA_API : CKCLOUD_API);
|
|
4594
5300
|
if (paymentChain === "solana" && !solanaPrivateKeyBytes) {
|
|
4595
5301
|
logger.warn(`[ckcloud] \u26A0 Payment chain is Solana but no mnemonic found \u2014 falling back to Base (EVM).`);
|
|
@@ -6238,6 +6944,8 @@ var plugin = {
|
|
|
6238
6944
|
const healthy = await waitForProxyHealth(port, 5e3);
|
|
6239
6945
|
if (!healthy) {
|
|
6240
6946
|
api.logger.warn(`Proxy health check timed out, commands may not work immediately`);
|
|
6947
|
+
} else {
|
|
6948
|
+
api.logger.info(`Proxy health check ok`);
|
|
6241
6949
|
}
|
|
6242
6950
|
}).catch((err) => {
|
|
6243
6951
|
api.logger.error(
|
|
@@ -6627,7 +7335,7 @@ async function startProxyInBackground(api) {
|
|
|
6627
7335
|
proxy.balanceMonitor.checkBalance().then((balance) => {
|
|
6628
7336
|
if (balance.isEmpty) {
|
|
6629
7337
|
api.logger.info(`Wallet: ${displayAddress} | Balance: $0.00`);
|
|
6630
|
-
api.logger.info(`Using
|
|
7338
|
+
api.logger.info(`Using AUTO model. Fund wallet for premium models.`);
|
|
6631
7339
|
} else if (balance.isLow) {
|
|
6632
7340
|
api.logger.info(`Wallet: ${displayAddress} | Balance: ${balance.balanceUSD} (low)`);
|
|
6633
7341
|
} else {
|