@blockrun/clawrouter 0.12.94 → 0.12.96

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 (282) hide show
  1. package/dist/auth.d.ts +82 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +388 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/balance.d.ts +97 -0
  6. package/dist/balance.d.ts.map +1 -0
  7. package/dist/balance.js +157 -0
  8. package/dist/balance.js.map +1 -0
  9. package/dist/cli.d.ts +15 -0
  10. package/dist/cli.d.ts.map +1 -0
  11. package/dist/cli.js +256 -54991
  12. package/dist/cli.js.map +1 -1
  13. package/dist/compression/codebook.d.ts +24 -0
  14. package/dist/compression/codebook.d.ts.map +1 -0
  15. package/dist/compression/codebook.js +119 -0
  16. package/dist/compression/codebook.js.map +1 -0
  17. package/dist/compression/index.d.ts +33 -0
  18. package/dist/compression/index.d.ts.map +1 -0
  19. package/dist/compression/index.js +246 -0
  20. package/dist/compression/index.js.map +1 -0
  21. package/dist/compression/layers/deduplication.d.ts +28 -0
  22. package/dist/compression/layers/deduplication.d.ts.map +1 -0
  23. package/dist/compression/layers/deduplication.js +101 -0
  24. package/dist/compression/layers/deduplication.js.map +1 -0
  25. package/dist/compression/layers/dictionary.d.ts +21 -0
  26. package/dist/compression/layers/dictionary.d.ts.map +1 -0
  27. package/dist/compression/layers/dictionary.js +73 -0
  28. package/dist/compression/layers/dictionary.js.map +1 -0
  29. package/dist/compression/layers/dynamic-codebook.d.ts +26 -0
  30. package/dist/compression/layers/dynamic-codebook.d.ts.map +1 -0
  31. package/dist/compression/layers/dynamic-codebook.js +149 -0
  32. package/dist/compression/layers/dynamic-codebook.js.map +1 -0
  33. package/dist/compression/layers/json-compact.d.ts +23 -0
  34. package/dist/compression/layers/json-compact.d.ts.map +1 -0
  35. package/dist/compression/layers/json-compact.js +79 -0
  36. package/dist/compression/layers/json-compact.js.map +1 -0
  37. package/dist/compression/layers/observation.d.ts +21 -0
  38. package/dist/compression/layers/observation.d.ts.map +1 -0
  39. package/dist/compression/layers/observation.js +130 -0
  40. package/dist/compression/layers/observation.js.map +1 -0
  41. package/dist/compression/layers/paths.d.ts +24 -0
  42. package/dist/compression/layers/paths.d.ts.map +1 -0
  43. package/dist/compression/layers/paths.js +110 -0
  44. package/dist/compression/layers/paths.js.map +1 -0
  45. package/dist/compression/layers/whitespace.d.ts +27 -0
  46. package/dist/compression/layers/whitespace.d.ts.map +1 -0
  47. package/dist/compression/layers/whitespace.js +60 -0
  48. package/dist/compression/layers/whitespace.js.map +1 -0
  49. package/dist/compression/types.d.ts +84 -0
  50. package/dist/compression/types.d.ts.map +1 -0
  51. package/dist/compression/types.js +27 -0
  52. package/dist/compression/types.js.map +1 -0
  53. package/dist/config.d.ts +12 -0
  54. package/dist/config.d.ts.map +1 -0
  55. package/dist/config.js +22 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/dedup.d.ts +34 -0
  58. package/dist/dedup.d.ts.map +1 -0
  59. package/dist/dedup.js +150 -0
  60. package/dist/dedup.js.map +1 -0
  61. package/dist/doctor.d.ts +8 -0
  62. package/dist/doctor.d.ts.map +1 -0
  63. package/dist/doctor.js +375 -0
  64. package/dist/doctor.js.map +1 -0
  65. package/dist/error-classification.test.d.ts +2 -0
  66. package/dist/error-classification.test.d.ts.map +1 -0
  67. package/dist/error-classification.test.js +59 -0
  68. package/dist/error-classification.test.js.map +1 -0
  69. package/dist/errors.d.ts +54 -0
  70. package/dist/errors.d.ts.map +1 -0
  71. package/dist/errors.js +85 -0
  72. package/dist/errors.js.map +1 -0
  73. package/dist/exclude-models.d.ts +28 -0
  74. package/dist/exclude-models.d.ts.map +1 -0
  75. package/dist/exclude-models.integration.test.d.ts +2 -0
  76. package/dist/exclude-models.integration.test.d.ts.map +1 -0
  77. package/dist/exclude-models.integration.test.js +46 -0
  78. package/dist/exclude-models.integration.test.js.map +1 -0
  79. package/dist/exclude-models.js +70 -0
  80. package/dist/exclude-models.js.map +1 -0
  81. package/dist/exclude-models.test.d.ts +5 -0
  82. package/dist/exclude-models.test.d.ts.map +1 -0
  83. package/dist/exclude-models.test.js +95 -0
  84. package/dist/exclude-models.test.js.map +1 -0
  85. package/dist/fs-read.d.ts +11 -0
  86. package/dist/fs-read.d.ts.map +1 -0
  87. package/dist/fs-read.js +47 -0
  88. package/dist/fs-read.js.map +1 -0
  89. package/dist/index.d.ts +34 -1368
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +1071 -56191
  92. package/dist/index.js.map +1 -1
  93. package/dist/journal.d.ts +67 -0
  94. package/dist/journal.d.ts.map +1 -0
  95. package/dist/journal.js +184 -0
  96. package/dist/journal.js.map +1 -0
  97. package/dist/journal.test.d.ts +2 -0
  98. package/dist/journal.test.d.ts.map +1 -0
  99. package/dist/journal.test.js +201 -0
  100. package/dist/journal.test.js.map +1 -0
  101. package/dist/logger.d.ts +33 -0
  102. package/dist/logger.d.ts.map +1 -0
  103. package/dist/logger.js +35 -0
  104. package/dist/logger.js.map +1 -0
  105. package/dist/models.d.ts +107 -0
  106. package/dist/models.d.ts.map +1 -0
  107. package/dist/models.js +941 -0
  108. package/dist/models.js.map +1 -0
  109. package/dist/models.test.d.ts +2 -0
  110. package/dist/models.test.d.ts.map +1 -0
  111. package/dist/models.test.js +38 -0
  112. package/dist/models.test.js.map +1 -0
  113. package/dist/partners/index.d.ts +8 -0
  114. package/dist/partners/index.d.ts.map +1 -0
  115. package/dist/partners/index.js +6 -0
  116. package/dist/partners/index.js.map +1 -0
  117. package/dist/partners/registry.d.ts +51 -0
  118. package/dist/partners/registry.d.ts.map +1 -0
  119. package/dist/partners/registry.js +256 -0
  120. package/dist/partners/registry.js.map +1 -0
  121. package/dist/partners/tools.d.ts +24 -0
  122. package/dist/partners/tools.d.ts.map +1 -0
  123. package/dist/partners/tools.js +97 -0
  124. package/dist/partners/tools.js.map +1 -0
  125. package/dist/payment-preauth.d.ts +17 -0
  126. package/dist/payment-preauth.d.ts.map +1 -0
  127. package/dist/payment-preauth.js +102 -0
  128. package/dist/payment-preauth.js.map +1 -0
  129. package/dist/provider.d.ts +19 -0
  130. package/dist/provider.d.ts.map +1 -0
  131. package/dist/provider.js +45 -0
  132. package/dist/provider.js.map +1 -0
  133. package/dist/proxy.d.ts +210 -0
  134. package/dist/proxy.d.ts.map +1 -0
  135. package/dist/proxy.debrand.test.d.ts +2 -0
  136. package/dist/proxy.debrand.test.d.ts.map +1 -0
  137. package/dist/proxy.debrand.test.js +101 -0
  138. package/dist/proxy.debrand.test.js.map +1 -0
  139. package/dist/proxy.degraded-response.test.d.ts +2 -0
  140. package/dist/proxy.degraded-response.test.d.ts.map +1 -0
  141. package/dist/proxy.degraded-response.test.js +105 -0
  142. package/dist/proxy.degraded-response.test.js.map +1 -0
  143. package/dist/proxy.image-download.test.d.ts +2 -0
  144. package/dist/proxy.image-download.test.d.ts.map +1 -0
  145. package/dist/proxy.image-download.test.js +92 -0
  146. package/dist/proxy.image-download.test.js.map +1 -0
  147. package/dist/proxy.js +3810 -0
  148. package/dist/proxy.js.map +1 -0
  149. package/dist/proxy.models-endpoint.test.d.ts +2 -0
  150. package/dist/proxy.models-endpoint.test.d.ts.map +1 -0
  151. package/dist/proxy.models-endpoint.test.js +19 -0
  152. package/dist/proxy.models-endpoint.test.js.map +1 -0
  153. package/dist/proxy.multiturn-thinking.test.d.ts +2 -0
  154. package/dist/proxy.multiturn-thinking.test.d.ts.map +1 -0
  155. package/dist/proxy.multiturn-thinking.test.js +82 -0
  156. package/dist/proxy.multiturn-thinking.test.js.map +1 -0
  157. package/dist/proxy.payment-chain-reuse.test.d.ts +2 -0
  158. package/dist/proxy.payment-chain-reuse.test.d.ts.map +1 -0
  159. package/dist/proxy.payment-chain-reuse.test.js +30 -0
  160. package/dist/proxy.payment-chain-reuse.test.js.map +1 -0
  161. package/dist/proxy.solana-resilience.test.d.ts +12 -0
  162. package/dist/proxy.solana-resilience.test.d.ts.map +1 -0
  163. package/dist/proxy.solana-resilience.test.js +39 -0
  164. package/dist/proxy.solana-resilience.test.js.map +1 -0
  165. package/dist/report.d.ts +6 -0
  166. package/dist/report.d.ts.map +1 -0
  167. package/dist/report.js +44 -0
  168. package/dist/report.js.map +1 -0
  169. package/dist/response-cache.advanced.test.d.ts +2 -0
  170. package/dist/response-cache.advanced.test.d.ts.map +1 -0
  171. package/dist/response-cache.advanced.test.js +566 -0
  172. package/dist/response-cache.advanced.test.js.map +1 -0
  173. package/dist/response-cache.d.ts +85 -0
  174. package/dist/response-cache.d.ts.map +1 -0
  175. package/dist/response-cache.edge-cases.test.d.ts +2 -0
  176. package/dist/response-cache.edge-cases.test.d.ts.map +1 -0
  177. package/dist/response-cache.edge-cases.test.js +488 -0
  178. package/dist/response-cache.edge-cases.test.js.map +1 -0
  179. package/dist/response-cache.extreme.test.d.ts +2 -0
  180. package/dist/response-cache.extreme.test.d.ts.map +1 -0
  181. package/dist/response-cache.extreme.test.js +561 -0
  182. package/dist/response-cache.extreme.test.js.map +1 -0
  183. package/dist/response-cache.js +240 -0
  184. package/dist/response-cache.js.map +1 -0
  185. package/dist/response-cache.test.d.ts +2 -0
  186. package/dist/response-cache.test.d.ts.map +1 -0
  187. package/dist/response-cache.test.js +242 -0
  188. package/dist/response-cache.test.js.map +1 -0
  189. package/dist/retry.d.ts +42 -0
  190. package/dist/retry.d.ts.map +1 -0
  191. package/dist/retry.js +99 -0
  192. package/dist/retry.js.map +1 -0
  193. package/dist/router/config.d.ts +11 -0
  194. package/dist/router/config.d.ts.map +1 -0
  195. package/dist/router/config.js +1215 -0
  196. package/dist/router/config.js.map +1 -0
  197. package/dist/router/index.d.ts +18 -0
  198. package/dist/router/index.d.ts.map +1 -0
  199. package/dist/router/index.js +19 -0
  200. package/dist/router/index.js.map +1 -0
  201. package/dist/router/llm-classifier.d.ts +29 -0
  202. package/dist/router/llm-classifier.d.ts.map +1 -0
  203. package/dist/router/llm-classifier.js +99 -0
  204. package/dist/router/llm-classifier.js.map +1 -0
  205. package/dist/router/rules.d.ts +12 -0
  206. package/dist/router/rules.d.ts.map +1 -0
  207. package/dist/router/rules.js +202 -0
  208. package/dist/router/rules.js.map +1 -0
  209. package/dist/router/selector.d.ts +58 -0
  210. package/dist/router/selector.d.ts.map +1 -0
  211. package/dist/router/selector.js +165 -0
  212. package/dist/router/selector.js.map +1 -0
  213. package/dist/router/selector.test.d.ts +2 -0
  214. package/dist/router/selector.test.d.ts.map +1 -0
  215. package/dist/router/selector.test.js +67 -0
  216. package/dist/router/selector.test.js.map +1 -0
  217. package/dist/router/strategy.d.ts +19 -0
  218. package/dist/router/strategy.d.ts.map +1 -0
  219. package/dist/router/strategy.js +138 -0
  220. package/dist/router/strategy.js.map +1 -0
  221. package/dist/router/strategy.test.d.ts +2 -0
  222. package/dist/router/strategy.test.d.ts.map +1 -0
  223. package/dist/router/strategy.test.js +131 -0
  224. package/dist/router/strategy.test.js.map +1 -0
  225. package/dist/router/types.d.ts +127 -0
  226. package/dist/router/types.d.ts.map +1 -0
  227. package/dist/router/types.js +11 -0
  228. package/dist/router/types.js.map +1 -0
  229. package/dist/session.d.ts +124 -0
  230. package/dist/session.d.ts.map +1 -0
  231. package/dist/session.js +256 -0
  232. package/dist/session.js.map +1 -0
  233. package/dist/session.test.d.ts +2 -0
  234. package/dist/session.test.d.ts.map +1 -0
  235. package/dist/session.test.js +163 -0
  236. package/dist/session.test.js.map +1 -0
  237. package/dist/solana-balance.d.ts +48 -0
  238. package/dist/solana-balance.d.ts.map +1 -0
  239. package/dist/solana-balance.js +142 -0
  240. package/dist/solana-balance.js.map +1 -0
  241. package/dist/spend-control.d.ts +112 -0
  242. package/dist/spend-control.d.ts.map +1 -0
  243. package/dist/spend-control.js +278 -0
  244. package/dist/spend-control.js.map +1 -0
  245. package/dist/spend-control.test.d.ts +5 -0
  246. package/dist/spend-control.test.d.ts.map +1 -0
  247. package/dist/spend-control.test.js +210 -0
  248. package/dist/spend-control.test.js.map +1 -0
  249. package/dist/stats.d.ts +64 -0
  250. package/dist/stats.d.ts.map +1 -0
  251. package/dist/stats.js +295 -0
  252. package/dist/stats.js.map +1 -0
  253. package/dist/types.d.ts +150 -0
  254. package/dist/types.d.ts.map +1 -0
  255. package/dist/types.js +9 -0
  256. package/dist/types.js.map +1 -0
  257. package/dist/update-hint.test.d.ts +9 -0
  258. package/dist/update-hint.test.d.ts.map +1 -0
  259. package/dist/update-hint.test.js +156 -0
  260. package/dist/update-hint.test.js.map +1 -0
  261. package/dist/updater.d.ts +10 -0
  262. package/dist/updater.d.ts.map +1 -0
  263. package/dist/updater.js +55 -0
  264. package/dist/updater.js.map +1 -0
  265. package/dist/version.d.ts +3 -0
  266. package/dist/version.d.ts.map +1 -0
  267. package/dist/version.js +16 -0
  268. package/dist/version.js.map +1 -0
  269. package/dist/wallet.d.ts +52 -0
  270. package/dist/wallet.d.ts.map +1 -0
  271. package/dist/wallet.js +93 -0
  272. package/dist/wallet.js.map +1 -0
  273. package/dist/wallet.test.d.ts +6 -0
  274. package/dist/wallet.test.d.ts.map +1 -0
  275. package/dist/wallet.test.js +117 -0
  276. package/dist/wallet.test.js.map +1 -0
  277. package/dist/x402-sdk.test.d.ts +6 -0
  278. package/dist/x402-sdk.test.d.ts.map +1 -0
  279. package/dist/x402-sdk.test.js +161 -0
  280. package/dist/x402-sdk.test.js.map +1 -0
  281. package/package.json +2 -2
  282. package/scripts/e2e-multiturn.mjs +4 -1
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Response Cache for LLM Completions
3
+ *
4
+ * Caches LLM responses by request hash (model + messages + params).
5
+ * Inspired by LiteLLM's caching system. Returns cached responses for
6
+ * identical requests, saving both cost and latency.
7
+ *
8
+ * Features:
9
+ * - TTL-based expiration (default 10 minutes)
10
+ * - LRU eviction when cache is full
11
+ * - Size limits per item (1MB max)
12
+ * - Heap-based expiration tracking for efficient pruning
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ const DEFAULT_CONFIG = {
16
+ maxSize: 200,
17
+ defaultTTL: 600,
18
+ maxItemSize: 1_048_576, // 1MB
19
+ enabled: true,
20
+ };
21
+ /**
22
+ * Canonicalize JSON by sorting object keys recursively.
23
+ * Ensures identical logical content produces identical hash.
24
+ */
25
+ function canonicalize(obj) {
26
+ if (obj === null || typeof obj !== "object") {
27
+ return obj;
28
+ }
29
+ if (Array.isArray(obj)) {
30
+ return obj.map(canonicalize);
31
+ }
32
+ const sorted = {};
33
+ for (const key of Object.keys(obj).sort()) {
34
+ sorted[key] = canonicalize(obj[key]);
35
+ }
36
+ return sorted;
37
+ }
38
+ /**
39
+ * Strip fields that shouldn't affect cache key:
40
+ * - stream (we handle streaming separately)
41
+ * - timestamps injected by OpenClaw
42
+ * - request IDs
43
+ */
44
+ const TIMESTAMP_PATTERN = /^\[\w{3}\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+\w+\]\s*/;
45
+ function normalizeForCache(obj) {
46
+ const result = {};
47
+ for (const [key, value] of Object.entries(obj)) {
48
+ // Skip fields that don't affect response content
49
+ if (["stream", "user", "request_id", "x-request-id"].includes(key)) {
50
+ continue;
51
+ }
52
+ if (key === "messages" && Array.isArray(value)) {
53
+ // Strip timestamps from message content
54
+ result[key] = value.map((msg) => {
55
+ if (typeof msg === "object" && msg !== null) {
56
+ const m = msg;
57
+ if (typeof m.content === "string") {
58
+ return { ...m, content: m.content.replace(TIMESTAMP_PATTERN, "") };
59
+ }
60
+ }
61
+ return msg;
62
+ });
63
+ }
64
+ else {
65
+ result[key] = value;
66
+ }
67
+ }
68
+ return result;
69
+ }
70
+ export class ResponseCache {
71
+ cache = new Map();
72
+ expirationHeap = [];
73
+ config;
74
+ // Stats for monitoring
75
+ stats = {
76
+ hits: 0,
77
+ misses: 0,
78
+ evictions: 0,
79
+ };
80
+ constructor(config = {}) {
81
+ // Filter out undefined values so they don't override defaults
82
+ const filtered = Object.fromEntries(Object.entries(config).filter(([, v]) => v !== undefined));
83
+ this.config = { ...DEFAULT_CONFIG, ...filtered };
84
+ }
85
+ /**
86
+ * Generate cache key from request body.
87
+ * Hashes: model + messages + temperature + max_tokens + other params
88
+ */
89
+ static generateKey(body) {
90
+ try {
91
+ const parsed = JSON.parse(typeof body === "string" ? body : body.toString());
92
+ const normalized = normalizeForCache(parsed);
93
+ const canonical = canonicalize(normalized);
94
+ const keyContent = JSON.stringify(canonical);
95
+ return createHash("sha256").update(keyContent).digest("hex").slice(0, 32);
96
+ }
97
+ catch {
98
+ // Fallback: hash raw body
99
+ const content = typeof body === "string" ? body : body.toString();
100
+ return createHash("sha256").update(content).digest("hex").slice(0, 32);
101
+ }
102
+ }
103
+ /**
104
+ * Check if caching is enabled for this request.
105
+ * Respects cache control headers and request params.
106
+ */
107
+ shouldCache(body, headers) {
108
+ if (!this.config.enabled)
109
+ return false;
110
+ // Respect Cache-Control: no-cache header
111
+ if (headers?.["cache-control"]?.includes("no-cache")) {
112
+ return false;
113
+ }
114
+ // Check for explicit cache disable in body
115
+ try {
116
+ const parsed = JSON.parse(typeof body === "string" ? body : body.toString());
117
+ if (parsed.cache === false || parsed.no_cache === true) {
118
+ return false;
119
+ }
120
+ }
121
+ catch {
122
+ // Not JSON, allow caching
123
+ }
124
+ return true;
125
+ }
126
+ /**
127
+ * Get cached response if available and not expired.
128
+ */
129
+ get(key) {
130
+ const entry = this.cache.get(key);
131
+ if (!entry) {
132
+ this.stats.misses++;
133
+ return undefined;
134
+ }
135
+ // Check expiration
136
+ if (Date.now() > entry.expiresAt) {
137
+ this.cache.delete(key);
138
+ this.stats.misses++;
139
+ return undefined;
140
+ }
141
+ this.stats.hits++;
142
+ return entry;
143
+ }
144
+ /**
145
+ * Cache a response with optional custom TTL.
146
+ */
147
+ set(key, response, ttlSeconds) {
148
+ // Don't cache if disabled or maxSize is 0
149
+ if (!this.config.enabled || this.config.maxSize <= 0)
150
+ return;
151
+ // Don't cache if item too large
152
+ if (response.body.length > this.config.maxItemSize) {
153
+ console.log(`[ResponseCache] Skipping cache - item too large: ${response.body.length} bytes`);
154
+ return;
155
+ }
156
+ // Don't cache error responses
157
+ if (response.status >= 400) {
158
+ return;
159
+ }
160
+ // Evict if at capacity
161
+ if (this.cache.size >= this.config.maxSize) {
162
+ this.evict();
163
+ }
164
+ const now = Date.now();
165
+ const ttl = ttlSeconds ?? this.config.defaultTTL;
166
+ const expiresAt = now + ttl * 1000;
167
+ const entry = {
168
+ ...response,
169
+ cachedAt: now,
170
+ expiresAt,
171
+ };
172
+ this.cache.set(key, entry);
173
+ this.expirationHeap.push({ expiresAt, key });
174
+ }
175
+ /**
176
+ * Evict expired and oldest entries to make room.
177
+ */
178
+ evict() {
179
+ const now = Date.now();
180
+ // First pass: remove expired entries
181
+ this.expirationHeap.sort((a, b) => a.expiresAt - b.expiresAt);
182
+ while (this.expirationHeap.length > 0) {
183
+ const oldest = this.expirationHeap[0];
184
+ // Check if entry still exists and matches
185
+ const entry = this.cache.get(oldest.key);
186
+ if (!entry || entry.expiresAt !== oldest.expiresAt) {
187
+ // Stale heap entry, remove it
188
+ this.expirationHeap.shift();
189
+ continue;
190
+ }
191
+ if (oldest.expiresAt <= now) {
192
+ // Expired, remove both
193
+ this.cache.delete(oldest.key);
194
+ this.expirationHeap.shift();
195
+ this.stats.evictions++;
196
+ }
197
+ else {
198
+ // Not expired, stop
199
+ break;
200
+ }
201
+ }
202
+ // Second pass: if still at capacity, evict oldest
203
+ while (this.cache.size >= this.config.maxSize && this.expirationHeap.length > 0) {
204
+ const oldest = this.expirationHeap.shift();
205
+ if (this.cache.has(oldest.key)) {
206
+ this.cache.delete(oldest.key);
207
+ this.stats.evictions++;
208
+ }
209
+ }
210
+ }
211
+ /**
212
+ * Get cache statistics.
213
+ */
214
+ getStats() {
215
+ const total = this.stats.hits + this.stats.misses;
216
+ const hitRate = total > 0 ? ((this.stats.hits / total) * 100).toFixed(1) + "%" : "0%";
217
+ return {
218
+ size: this.cache.size,
219
+ maxSize: this.config.maxSize,
220
+ hits: this.stats.hits,
221
+ misses: this.stats.misses,
222
+ evictions: this.stats.evictions,
223
+ hitRate,
224
+ };
225
+ }
226
+ /**
227
+ * Clear all cached entries.
228
+ */
229
+ clear() {
230
+ this.cache.clear();
231
+ this.expirationHeap = [];
232
+ }
233
+ /**
234
+ * Check if cache is enabled.
235
+ */
236
+ isEnabled() {
237
+ return this.config.enabled;
238
+ }
239
+ }
240
+ //# sourceMappingURL=response-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-cache.js","sourceRoot":"","sources":["../src/response-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=response-cache.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-cache.test.d.ts","sourceRoot":"","sources":["../src/response-cache.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,242 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { ResponseCache } from "./response-cache.js";
3
+ describe("ResponseCache", () => {
4
+ let cache;
5
+ beforeEach(() => {
6
+ cache = new ResponseCache();
7
+ });
8
+ describe("generateKey", () => {
9
+ it("should generate consistent keys for identical requests", () => {
10
+ const body1 = JSON.stringify({
11
+ model: "gpt-4",
12
+ messages: [{ role: "user", content: "hello" }],
13
+ });
14
+ const body2 = JSON.stringify({
15
+ model: "gpt-4",
16
+ messages: [{ role: "user", content: "hello" }],
17
+ });
18
+ expect(ResponseCache.generateKey(body1)).toBe(ResponseCache.generateKey(body2));
19
+ });
20
+ it("should generate different keys for different messages", () => {
21
+ const body1 = JSON.stringify({
22
+ model: "gpt-4",
23
+ messages: [{ role: "user", content: "hello" }],
24
+ });
25
+ const body2 = JSON.stringify({
26
+ model: "gpt-4",
27
+ messages: [{ role: "user", content: "goodbye" }],
28
+ });
29
+ expect(ResponseCache.generateKey(body1)).not.toBe(ResponseCache.generateKey(body2));
30
+ });
31
+ it("should generate different keys for different models", () => {
32
+ const body1 = JSON.stringify({
33
+ model: "gpt-4",
34
+ messages: [{ role: "user", content: "hello" }],
35
+ });
36
+ const body2 = JSON.stringify({
37
+ model: "gpt-3.5-turbo",
38
+ messages: [{ role: "user", content: "hello" }],
39
+ });
40
+ expect(ResponseCache.generateKey(body1)).not.toBe(ResponseCache.generateKey(body2));
41
+ });
42
+ it("should ignore stream parameter for cache key", () => {
43
+ const body1 = JSON.stringify({
44
+ model: "gpt-4",
45
+ messages: [{ role: "user", content: "hello" }],
46
+ stream: true,
47
+ });
48
+ const body2 = JSON.stringify({
49
+ model: "gpt-4",
50
+ messages: [{ role: "user", content: "hello" }],
51
+ stream: false,
52
+ });
53
+ expect(ResponseCache.generateKey(body1)).toBe(ResponseCache.generateKey(body2));
54
+ });
55
+ it("should strip timestamps from messages", () => {
56
+ const body1 = JSON.stringify({
57
+ model: "gpt-4",
58
+ messages: [{ role: "user", content: "[Mon 2024-01-15 10:30 PST] hello" }],
59
+ });
60
+ const body2 = JSON.stringify({
61
+ model: "gpt-4",
62
+ messages: [{ role: "user", content: "hello" }],
63
+ });
64
+ expect(ResponseCache.generateKey(body1)).toBe(ResponseCache.generateKey(body2));
65
+ });
66
+ it("should handle Buffer input", () => {
67
+ const body = Buffer.from(JSON.stringify({
68
+ model: "gpt-4",
69
+ messages: [{ role: "user", content: "hello" }],
70
+ }));
71
+ expect(ResponseCache.generateKey(body)).toHaveLength(32);
72
+ });
73
+ });
74
+ describe("shouldCache", () => {
75
+ it("should return true by default", () => {
76
+ const body = JSON.stringify({ model: "gpt-4", messages: [] });
77
+ expect(cache.shouldCache(body)).toBe(true);
78
+ });
79
+ it("should return false when Cache-Control: no-cache header is present", () => {
80
+ const body = JSON.stringify({ model: "gpt-4", messages: [] });
81
+ expect(cache.shouldCache(body, { "cache-control": "no-cache" })).toBe(false);
82
+ });
83
+ it("should return true for other Cache-Control values", () => {
84
+ const body = JSON.stringify({ model: "gpt-4", messages: [] });
85
+ expect(cache.shouldCache(body, { "cache-control": "max-age=300" })).toBe(true);
86
+ });
87
+ it("should return false when cache=false in body", () => {
88
+ const body = JSON.stringify({ model: "gpt-4", messages: [], cache: false });
89
+ expect(cache.shouldCache(body)).toBe(false);
90
+ });
91
+ it("should return false when no_cache=true in body", () => {
92
+ const body = JSON.stringify({ model: "gpt-4", messages: [], no_cache: true });
93
+ expect(cache.shouldCache(body)).toBe(false);
94
+ });
95
+ it("should return false when cache is disabled", () => {
96
+ const disabledCache = new ResponseCache({ enabled: false });
97
+ const body = JSON.stringify({ model: "gpt-4", messages: [] });
98
+ expect(disabledCache.shouldCache(body)).toBe(false);
99
+ });
100
+ });
101
+ describe("get/set", () => {
102
+ it("should return undefined for non-existent key", () => {
103
+ expect(cache.get("nonexistent")).toBeUndefined();
104
+ });
105
+ it("should store and retrieve cached response", () => {
106
+ const key = "test-key";
107
+ const response = {
108
+ body: Buffer.from('{"result": "ok"}'),
109
+ status: 200,
110
+ headers: { "content-type": "application/json" },
111
+ model: "gpt-4",
112
+ };
113
+ cache.set(key, response);
114
+ const cached = cache.get(key);
115
+ expect(cached).toBeDefined();
116
+ expect(cached.body.toString()).toBe('{"result": "ok"}');
117
+ expect(cached.status).toBe(200);
118
+ expect(cached.model).toBe("gpt-4");
119
+ });
120
+ it("should not cache error responses (status >= 400)", () => {
121
+ const key = "error-key";
122
+ const response = {
123
+ body: Buffer.from('{"error": "bad request"}'),
124
+ status: 400,
125
+ headers: {},
126
+ model: "gpt-4",
127
+ };
128
+ cache.set(key, response);
129
+ expect(cache.get(key)).toBeUndefined();
130
+ });
131
+ it("should not cache items exceeding maxItemSize", () => {
132
+ const smallCache = new ResponseCache({ maxItemSize: 1 }); // 1KB max
133
+ const key = "large-key";
134
+ const response = {
135
+ body: Buffer.alloc(2 * 1024), // 2KB
136
+ status: 200,
137
+ headers: {},
138
+ model: "gpt-4",
139
+ };
140
+ smallCache.set(key, response);
141
+ expect(smallCache.get(key)).toBeUndefined();
142
+ });
143
+ it("should track hits and misses", () => {
144
+ const key = "stats-key";
145
+ const response = {
146
+ body: Buffer.from("test"),
147
+ status: 200,
148
+ headers: {},
149
+ model: "gpt-4",
150
+ };
151
+ cache.get("missing"); // miss
152
+ cache.set(key, response);
153
+ cache.get(key); // hit
154
+ cache.get(key); // hit
155
+ const stats = cache.getStats();
156
+ expect(stats.misses).toBe(1);
157
+ expect(stats.hits).toBe(2);
158
+ expect(stats.hitRate).toBe("66.7%");
159
+ });
160
+ });
161
+ describe("TTL expiration", () => {
162
+ it("should expire entries after TTL", () => {
163
+ vi.useFakeTimers();
164
+ const key = "ttl-key";
165
+ const response = {
166
+ body: Buffer.from("test"),
167
+ status: 200,
168
+ headers: {},
169
+ model: "gpt-4",
170
+ };
171
+ cache.set(key, response, 60); // 60 second TTL
172
+ // Should exist before expiration
173
+ expect(cache.get(key)).toBeDefined();
174
+ // Advance time past TTL
175
+ vi.advanceTimersByTime(61 * 1000);
176
+ // Should be expired
177
+ expect(cache.get(key)).toBeUndefined();
178
+ vi.useRealTimers();
179
+ });
180
+ it("should use custom TTL when provided", () => {
181
+ vi.useFakeTimers();
182
+ const key = "custom-ttl";
183
+ const response = {
184
+ body: Buffer.from("test"),
185
+ status: 200,
186
+ headers: {},
187
+ model: "gpt-4",
188
+ };
189
+ cache.set(key, response, 30); // 30 second TTL
190
+ vi.advanceTimersByTime(25 * 1000);
191
+ expect(cache.get(key)).toBeDefined();
192
+ vi.advanceTimersByTime(10 * 1000); // Now 35 seconds total
193
+ expect(cache.get(key)).toBeUndefined();
194
+ vi.useRealTimers();
195
+ });
196
+ });
197
+ describe("LRU eviction", () => {
198
+ it("should evict oldest entries when at capacity", () => {
199
+ const smallCache = new ResponseCache({ maxSize: 3, defaultTTL: 600 });
200
+ for (let i = 0; i < 5; i++) {
201
+ smallCache.set(`key-${i}`, {
202
+ body: Buffer.from(`response-${i}`),
203
+ status: 200,
204
+ headers: {},
205
+ model: "gpt-4",
206
+ });
207
+ }
208
+ const stats = smallCache.getStats();
209
+ expect(stats.size).toBeLessThanOrEqual(3);
210
+ expect(stats.evictions).toBeGreaterThan(0);
211
+ });
212
+ });
213
+ describe("clear", () => {
214
+ it("should remove all entries", () => {
215
+ cache.set("key-1", {
216
+ body: Buffer.from("test"),
217
+ status: 200,
218
+ headers: {},
219
+ model: "gpt-4",
220
+ });
221
+ cache.set("key-2", {
222
+ body: Buffer.from("test"),
223
+ status: 200,
224
+ headers: {},
225
+ model: "gpt-4",
226
+ });
227
+ expect(cache.getStats().size).toBe(2);
228
+ cache.clear();
229
+ expect(cache.getStats().size).toBe(0);
230
+ });
231
+ });
232
+ describe("isEnabled", () => {
233
+ it("should return true when enabled", () => {
234
+ expect(cache.isEnabled()).toBe(true);
235
+ });
236
+ it("should return false when disabled", () => {
237
+ const disabledCache = new ResponseCache({ enabled: false });
238
+ expect(disabledCache.isEnabled()).toBe(false);
239
+ });
240
+ });
241
+ });
242
+ //# sourceMappingURL=response-cache.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"response-cache.test.js","sourceRoot":"","sources":["../src/response-cache.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Retry Logic for ClawRouter
3
+ *
4
+ * Provides fetch wrapper with exponential backoff for transient errors.
5
+ * Retries on 429 (rate limit), 502, 503, 504 (server errors).
6
+ */
7
+ /** Configuration for retry behavior */
8
+ export type RetryConfig = {
9
+ /** Maximum number of retries (default: 2) */
10
+ maxRetries: number;
11
+ /** Base delay in ms for exponential backoff (default: 500) */
12
+ baseDelayMs: number;
13
+ /** HTTP status codes that trigger a retry (default: [429, 502, 503, 504]) */
14
+ retryableCodes: number[];
15
+ };
16
+ /** Default retry configuration */
17
+ export declare const DEFAULT_RETRY_CONFIG: RetryConfig;
18
+ /**
19
+ * Wrap a fetch-like function with retry logic and exponential backoff.
20
+ *
21
+ * @param fetchFn - The fetch function to wrap (can be standard fetch or x402 payFetch)
22
+ * @param url - URL to fetch
23
+ * @param init - Fetch init options
24
+ * @param config - Retry configuration (optional, uses defaults)
25
+ * @returns Response from successful fetch or last failed attempt
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const response = await fetchWithRetry(
30
+ * fetch,
31
+ * "https://api.example.com/endpoint",
32
+ * { method: "POST", body: JSON.stringify(data) },
33
+ * { maxRetries: 3 }
34
+ * );
35
+ * ```
36
+ */
37
+ export declare function fetchWithRetry(fetchFn: (url: string, init?: RequestInit) => Promise<Response>, url: string, init?: RequestInit, config?: Partial<RetryConfig>): Promise<Response>;
38
+ /**
39
+ * Check if an error or response indicates a retryable condition.
40
+ */
41
+ export declare function isRetryable(errorOrResponse: Error | Response, config?: Partial<RetryConfig>): boolean;
42
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,uCAAuC;AACvC,MAAM,MAAM,WAAW,GAAG;IACxB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,kCAAkC;AAClC,eAAO,MAAM,oBAAoB,EAAE,WAIlC,CAAC;AAOF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,EAC/D,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,WAAW,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAsDnB;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,eAAe,EAAE,KAAK,GAAG,QAAQ,EACjC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAgBT"}
package/dist/retry.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Retry Logic for ClawRouter
3
+ *
4
+ * Provides fetch wrapper with exponential backoff for transient errors.
5
+ * Retries on 429 (rate limit), 502, 503, 504 (server errors).
6
+ */
7
+ /** Default retry configuration */
8
+ export const DEFAULT_RETRY_CONFIG = {
9
+ maxRetries: 2,
10
+ baseDelayMs: 500,
11
+ retryableCodes: [429, 502, 503, 504],
12
+ };
13
+ /** Sleep for a given number of milliseconds */
14
+ function sleep(ms) {
15
+ return new Promise((resolve) => setTimeout(resolve, ms));
16
+ }
17
+ /**
18
+ * Wrap a fetch-like function with retry logic and exponential backoff.
19
+ *
20
+ * @param fetchFn - The fetch function to wrap (can be standard fetch or x402 payFetch)
21
+ * @param url - URL to fetch
22
+ * @param init - Fetch init options
23
+ * @param config - Retry configuration (optional, uses defaults)
24
+ * @returns Response from successful fetch or last failed attempt
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const response = await fetchWithRetry(
29
+ * fetch,
30
+ * "https://api.example.com/endpoint",
31
+ * { method: "POST", body: JSON.stringify(data) },
32
+ * { maxRetries: 3 }
33
+ * );
34
+ * ```
35
+ */
36
+ export async function fetchWithRetry(fetchFn, url, init, config) {
37
+ const cfg = {
38
+ ...DEFAULT_RETRY_CONFIG,
39
+ ...config,
40
+ };
41
+ let lastError;
42
+ let lastResponse;
43
+ for (let attempt = 0; attempt <= cfg.maxRetries; attempt++) {
44
+ try {
45
+ const response = await fetchFn(url, init);
46
+ // Success or non-retryable status — return immediately
47
+ if (!cfg.retryableCodes.includes(response.status)) {
48
+ return response;
49
+ }
50
+ // Retryable status — save response and maybe retry
51
+ lastResponse = response;
52
+ // Check for Retry-After header (common with 429)
53
+ const retryAfter = response.headers.get("retry-after");
54
+ let delay;
55
+ if (retryAfter) {
56
+ // Retry-After can be seconds or HTTP-date
57
+ const seconds = parseInt(retryAfter, 10);
58
+ delay = isNaN(seconds) ? cfg.baseDelayMs * Math.pow(2, attempt) : seconds * 1000;
59
+ }
60
+ else {
61
+ delay = cfg.baseDelayMs * Math.pow(2, attempt);
62
+ }
63
+ // Only retry if we have attempts left
64
+ if (attempt < cfg.maxRetries) {
65
+ await sleep(delay);
66
+ }
67
+ }
68
+ catch (err) {
69
+ lastError = err instanceof Error ? err : new Error(String(err));
70
+ // Network errors are retryable
71
+ if (attempt < cfg.maxRetries) {
72
+ const delay = cfg.baseDelayMs * Math.pow(2, attempt);
73
+ await sleep(delay);
74
+ }
75
+ }
76
+ }
77
+ // All retries exhausted — return last response or throw last error
78
+ if (lastResponse) {
79
+ return lastResponse;
80
+ }
81
+ throw lastError ?? new Error("Max retries exceeded");
82
+ }
83
+ /**
84
+ * Check if an error or response indicates a retryable condition.
85
+ */
86
+ export function isRetryable(errorOrResponse, config) {
87
+ const retryableCodes = config?.retryableCodes ?? DEFAULT_RETRY_CONFIG.retryableCodes;
88
+ if (errorOrResponse instanceof Response) {
89
+ return retryableCodes.includes(errorOrResponse.status);
90
+ }
91
+ // Network errors are generally retryable
92
+ const message = errorOrResponse.message.toLowerCase();
93
+ return (message.includes("network") ||
94
+ message.includes("timeout") ||
95
+ message.includes("econnreset") ||
96
+ message.includes("econnrefused") ||
97
+ message.includes("socket hang up"));
98
+ }
99
+ //# sourceMappingURL=retry.js.map