@joshuaswarren/openclaw-engram 9.1.16 → 9.1.18
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/README.md +11 -4
- package/dist/access-cli.js +4 -4
- package/dist/{calibration-JLSDBR4C.js → calibration-WQM24F3Z.js} +2 -2
- package/dist/{causal-consolidation-HCZHJO2J.js → causal-consolidation-GNGU4MUV.js} +2 -2
- package/dist/{chunk-5YCBFXXX.js → chunk-AVOL6JGC.js} +219 -5
- package/dist/chunk-AVOL6JGC.js.map +1 -0
- package/dist/{chunk-TS3YC3MY.js → chunk-DTCQ2KEX.js} +1 -1
- package/dist/{chunk-TS3YC3MY.js.map → chunk-DTCQ2KEX.js.map} +1 -1
- package/dist/{chunk-W7435EXA.js → chunk-HOO364J6.js} +21 -8
- package/dist/chunk-HOO364J6.js.map +1 -0
- package/dist/{chunk-MQ5EZ6VE.js → chunk-IEFSMVQV.js} +4 -4
- package/dist/{engine-SBLMS64G.js → engine-CNQZ35RB.js} +3 -3
- package/dist/{fallback-llm-HZFQ7ADY.js → fallback-llm-GF6NMVWP.js} +2 -2
- package/dist/index.js +7 -7
- package/dist/{storage-BIOBCHNN.js → storage-GSXR3Q63.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-5YCBFXXX.js.map +0 -1
- package/dist/chunk-W7435EXA.js.map +0 -1
- /package/dist/{calibration-JLSDBR4C.js.map → calibration-WQM24F3Z.js.map} +0 -0
- /package/dist/{causal-consolidation-HCZHJO2J.js.map → causal-consolidation-GNGU4MUV.js.map} +0 -0
- /package/dist/{chunk-MQ5EZ6VE.js.map → chunk-IEFSMVQV.js.map} +0 -0
- /package/dist/{engine-SBLMS64G.js.map → engine-CNQZ35RB.js.map} +0 -0
- /package/dist/{fallback-llm-HZFQ7ADY.js.map → fallback-llm-GF6NMVWP.js.map} +0 -0
- /package/dist/{storage-BIOBCHNN.js.map → storage-GSXR3Q63.js.map} +0 -0
package/README.md
CHANGED
|
@@ -68,13 +68,18 @@ After installation, add Engram to your `openclaw.json`:
|
|
|
68
68
|
"openclaw-engram": {
|
|
69
69
|
"enabled": true,
|
|
70
70
|
"config": {
|
|
71
|
-
// Use OpenAI for extraction:
|
|
71
|
+
// Option 1: Use OpenAI for extraction:
|
|
72
72
|
"openaiApiKey": "${OPENAI_API_KEY}"
|
|
73
73
|
|
|
74
|
-
//
|
|
74
|
+
// Option 2: Use a local LLM (no API key needed):
|
|
75
75
|
// "localLlmEnabled": true,
|
|
76
76
|
// "localLlmUrl": "http://localhost:1234/v1",
|
|
77
77
|
// "localLlmModel": "qwen2.5-32b-instruct"
|
|
78
|
+
|
|
79
|
+
// Option 3: Use the gateway model chain (multi-provider fallback):
|
|
80
|
+
// "modelSource": "gateway",
|
|
81
|
+
// "gatewayAgentId": "engram-llm",
|
|
82
|
+
// "fastGatewayAgentId": "engram-llm-fast"
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
}
|
|
@@ -82,6 +87,8 @@ After installation, add Engram to your `openclaw.json`:
|
|
|
82
87
|
}
|
|
83
88
|
```
|
|
84
89
|
|
|
90
|
+
> **Gateway model source:** When `modelSource` is `"gateway"`, Engram routes all LLM calls (extraction, consolidation, reranking) through an OpenClaw agent persona's model chain instead of its own config. Define agent personas in `openclaw.json → agents.list[]` with a `primary` model and `fallbacks[]` array — Engram tries each in order until one succeeds. This lets you build multi-provider fallback chains like Fireworks → local LLM → cloud OpenAI. See the [Gateway Model Source](docs/config-reference.md#gateway-model-source) guide for full setup.
|
|
91
|
+
|
|
85
92
|
Restart the gateway:
|
|
86
93
|
|
|
87
94
|
```bash
|
|
@@ -192,9 +199,9 @@ OpenClaw's built-in memory is basic — it works for getting started, but lacks
|
|
|
192
199
|
|
|
193
200
|
Engram uses hybrid search (BM25 + vector + reranking via [QMD](https://github.com/tobilu/qmd)) to find semantically relevant memories. It doesn't just match keywords — it understands what you're working on and surfaces the right context.
|
|
194
201
|
|
|
195
|
-
### OpenAI
|
|
202
|
+
### Flexible LLM routing — OpenAI, local, or gateway model chain
|
|
196
203
|
|
|
197
|
-
Use OpenAI for extraction and reranking,
|
|
204
|
+
Use OpenAI for extraction and reranking, run entirely offline with a local LLM (Ollama, LM Studio), or route through the **gateway model chain** to use any provider with automatic fallback. The `local-llm-heavy` preset is optimized for fully local operation. See the [Local LLM Guide](docs/guides/local-llm.md) and the [Gateway Model Source](docs/config-reference.md#gateway-model-source) section for multi-provider setups.
|
|
198
205
|
|
|
199
206
|
### Progressive complexity
|
|
200
207
|
|
package/dist/access-cli.js
CHANGED
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
EngramAccessService,
|
|
4
4
|
Orchestrator,
|
|
5
5
|
parseConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-HOO364J6.js";
|
|
7
|
+
import "./chunk-IEFSMVQV.js";
|
|
8
8
|
import "./chunk-IMMYYNXG.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-DTCQ2KEX.js";
|
|
10
10
|
import "./chunk-6KX4XLQJ.js";
|
|
11
|
-
import "./chunk-
|
|
11
|
+
import "./chunk-AVOL6JGC.js";
|
|
12
12
|
import "./chunk-MKM2BCQH.js";
|
|
13
13
|
import "./chunk-DEIBZP3O.js";
|
|
14
14
|
import "./chunk-SSIIJJKA.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
2
|
import {
|
|
3
3
|
FallbackLlmClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AVOL6JGC.js";
|
|
5
5
|
import {
|
|
6
6
|
listJsonFiles
|
|
7
7
|
} from "./chunk-DEIBZP3O.js";
|
|
@@ -233,4 +233,4 @@ export {
|
|
|
233
233
|
runCalibrationIfEnabled,
|
|
234
234
|
synthesizeCalibrationRules
|
|
235
235
|
};
|
|
236
|
-
//# sourceMappingURL=calibration-
|
|
236
|
+
//# sourceMappingURL=calibration-WQM24F3Z.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// openclaw-engram: Local-first memory plugin
|
|
2
2
|
import {
|
|
3
3
|
FallbackLlmClient
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-AVOL6JGC.js";
|
|
5
5
|
import {
|
|
6
6
|
readChainIndex,
|
|
7
7
|
resolveChainsDir
|
|
@@ -203,4 +203,4 @@ export {
|
|
|
203
203
|
deriveCausalPromotionCandidates,
|
|
204
204
|
synthesizeCausalPreferencesViaLlm
|
|
205
205
|
};
|
|
206
|
-
//# sourceMappingURL=causal-consolidation-
|
|
206
|
+
//# sourceMappingURL=causal-consolidation-GNGU4MUV.js.map
|
|
@@ -94,6 +94,202 @@ function buildChatCompletionTokenLimit(model, maxTokens, options) {
|
|
|
94
94
|
return { max_tokens: safeMaxTokens };
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// src/resolve-provider-secret.ts
|
|
98
|
+
import { execFileSync } from "child_process";
|
|
99
|
+
import { readFileSync, existsSync } from "fs";
|
|
100
|
+
import path from "path";
|
|
101
|
+
import os from "os";
|
|
102
|
+
var resolvedCache = /* @__PURE__ */ new Map();
|
|
103
|
+
async function resolveProviderApiKey(providerId, apiKeyValue, gatewayConfig) {
|
|
104
|
+
if (typeof apiKeyValue === "string") {
|
|
105
|
+
if (apiKeyValue === "secretref-managed") {
|
|
106
|
+
return resolveSecretRefManaged(providerId, gatewayConfig);
|
|
107
|
+
}
|
|
108
|
+
if (apiKeyValue.endsWith("-oauth") || apiKeyValue.endsWith("-local") || apiKeyValue === "lm-studio" || apiKeyValue.startsWith("gcp-")) {
|
|
109
|
+
return void 0;
|
|
110
|
+
}
|
|
111
|
+
return apiKeyValue;
|
|
112
|
+
}
|
|
113
|
+
if (isSecretRefObject(apiKeyValue)) {
|
|
114
|
+
return resolveSecretRef(providerId, apiKeyValue);
|
|
115
|
+
}
|
|
116
|
+
return void 0;
|
|
117
|
+
}
|
|
118
|
+
function isSecretRefObject(value) {
|
|
119
|
+
return typeof value === "object" && value !== null && "source" in value && "id" in value && typeof value.source === "string" && typeof value.id === "string";
|
|
120
|
+
}
|
|
121
|
+
async function resolveSecretRef(providerId, ref) {
|
|
122
|
+
const cacheKey = `ref:${ref.source}:${ref.provider ?? ""}:${ref.id}:${ref.command ?? ""}:${(ref.args ?? []).join(",")}`;
|
|
123
|
+
const cached = resolvedCache.get(cacheKey);
|
|
124
|
+
if (cached !== void 0) {
|
|
125
|
+
if (cached !== null) return cached;
|
|
126
|
+
}
|
|
127
|
+
let resolved;
|
|
128
|
+
try {
|
|
129
|
+
switch (ref.source) {
|
|
130
|
+
case "exec":
|
|
131
|
+
resolved = resolveExecSecret(ref);
|
|
132
|
+
break;
|
|
133
|
+
case "file":
|
|
134
|
+
resolved = resolveFileSecret(ref);
|
|
135
|
+
break;
|
|
136
|
+
case "env":
|
|
137
|
+
resolved = process.env[ref.id] ?? void 0;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
log.warn(
|
|
142
|
+
`secret resolution failed for provider "${providerId}" (${ref.source}/${ref.provider}): ${err instanceof Error ? err.message : String(err)}`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
if (resolved) {
|
|
146
|
+
resolvedCache.set(cacheKey, resolved);
|
|
147
|
+
}
|
|
148
|
+
if (resolved) {
|
|
149
|
+
log.debug(`resolved API key for provider "${providerId}" via ${ref.source}/${ref.provider ?? "default"}`);
|
|
150
|
+
}
|
|
151
|
+
return resolved;
|
|
152
|
+
}
|
|
153
|
+
function resolveExecSecret(ref) {
|
|
154
|
+
const command = ref.command ?? (ref.provider === "op" ? "op" : void 0);
|
|
155
|
+
if (!command) {
|
|
156
|
+
log.warn(`exec secret ref has no command and provider "${ref.provider}" is not a known exec provider`);
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
const args = ref.args ?? (ref.provider === "op" ? ["read", ref.id] : [ref.id]);
|
|
160
|
+
try {
|
|
161
|
+
const result = execFileSync(command, args, {
|
|
162
|
+
encoding: "utf-8",
|
|
163
|
+
timeout: 1e4,
|
|
164
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
165
|
+
}).trim();
|
|
166
|
+
return result || void 0;
|
|
167
|
+
} catch (err) {
|
|
168
|
+
log.warn(`exec secret resolution failed (${command}): ${err instanceof Error ? err.message : String(err)}`);
|
|
169
|
+
return void 0;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function resolveFileSecret(ref) {
|
|
173
|
+
if (ref.provider === "op") {
|
|
174
|
+
const secretsDir = path.join(os.homedir(), ".openclaw", "secrets");
|
|
175
|
+
const filePath = path.join(secretsDir, ref.id.replace(/^\//, ""));
|
|
176
|
+
if (existsSync(filePath)) {
|
|
177
|
+
try {
|
|
178
|
+
return readFileSync(filePath, "utf-8").trim() || void 0;
|
|
179
|
+
} catch {
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
const result = execFileSync("op", ["read", ref.id], {
|
|
184
|
+
encoding: "utf-8",
|
|
185
|
+
timeout: 1e4,
|
|
186
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
187
|
+
}).trim();
|
|
188
|
+
return result || void 0;
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
const filePath = path.isAbsolute(ref.id) ? ref.id : path.join(os.homedir(), ".openclaw", "secrets", ref.id.replace(/^\//, ""));
|
|
193
|
+
if (existsSync(filePath)) {
|
|
194
|
+
try {
|
|
195
|
+
return readFileSync(filePath, "utf-8").trim() || void 0;
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
function resolveSecretRefManaged(providerId, gatewayConfig) {
|
|
203
|
+
const cacheKey = `managed:${providerId}`;
|
|
204
|
+
const cached = resolvedCache.get(cacheKey);
|
|
205
|
+
if (cached !== void 0 && cached !== null) {
|
|
206
|
+
return cached;
|
|
207
|
+
}
|
|
208
|
+
let resolved;
|
|
209
|
+
const profiles = gatewayConfig?.auth?.profiles;
|
|
210
|
+
if (profiles) {
|
|
211
|
+
const candidates = [
|
|
212
|
+
`${providerId}:default`,
|
|
213
|
+
`${providerId}:manual`,
|
|
214
|
+
providerId
|
|
215
|
+
];
|
|
216
|
+
for (const profileKey of candidates) {
|
|
217
|
+
const raw = profiles[profileKey];
|
|
218
|
+
if (!raw || typeof raw !== "object") continue;
|
|
219
|
+
const profile = raw;
|
|
220
|
+
if (profile.token) {
|
|
221
|
+
if (typeof profile.token === "string" && !profile.token.startsWith("{")) {
|
|
222
|
+
resolved = profile.token;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
if (isSecretRefObject(profile.token)) {
|
|
226
|
+
try {
|
|
227
|
+
resolved = resolveSecretRefSync(providerId, profile.token);
|
|
228
|
+
} catch {
|
|
229
|
+
}
|
|
230
|
+
if (resolved) break;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (profile.apiKey && typeof profile.apiKey === "string") {
|
|
234
|
+
resolved = profile.apiKey;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
if (profile.access && typeof profile.access === "string") {
|
|
238
|
+
resolved = profile.access;
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!resolved) {
|
|
244
|
+
const envCandidates = [
|
|
245
|
+
`${providerId.toUpperCase().replace(/-/g, "_")}_API_KEY`,
|
|
246
|
+
`${providerId.toUpperCase().replace(/-/g, "_")}_TOKEN`
|
|
247
|
+
];
|
|
248
|
+
for (const envVar of envCandidates) {
|
|
249
|
+
if (process.env[envVar]) {
|
|
250
|
+
resolved = process.env[envVar];
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (resolved) {
|
|
256
|
+
resolvedCache.set(cacheKey, resolved);
|
|
257
|
+
log.debug(`resolved managed API key for provider "${providerId}" via auth profile`);
|
|
258
|
+
} else {
|
|
259
|
+
log.debug(`could not resolve managed API key for provider "${providerId}"`);
|
|
260
|
+
}
|
|
261
|
+
return resolved;
|
|
262
|
+
}
|
|
263
|
+
function resolveSecretRefSync(providerId, ref) {
|
|
264
|
+
const cacheKey = `ref:${ref.source}:${ref.provider ?? ""}:${ref.id}:${ref.command ?? ""}:${(ref.args ?? []).join(",")}`;
|
|
265
|
+
const cached = resolvedCache.get(cacheKey);
|
|
266
|
+
if (cached !== void 0 && cached !== null) {
|
|
267
|
+
return cached;
|
|
268
|
+
}
|
|
269
|
+
let resolved;
|
|
270
|
+
try {
|
|
271
|
+
switch (ref.source) {
|
|
272
|
+
case "exec":
|
|
273
|
+
resolved = resolveExecSecret(ref);
|
|
274
|
+
break;
|
|
275
|
+
case "file":
|
|
276
|
+
resolved = resolveFileSecret(ref);
|
|
277
|
+
break;
|
|
278
|
+
case "env":
|
|
279
|
+
resolved = process.env[ref.id] ?? void 0;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
log.warn(
|
|
284
|
+
`sync secret resolution failed for provider "${providerId}": ${err instanceof Error ? err.message : String(err)}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
if (resolved) {
|
|
288
|
+
resolvedCache.set(cacheKey, resolved);
|
|
289
|
+
}
|
|
290
|
+
return resolved;
|
|
291
|
+
}
|
|
292
|
+
|
|
97
293
|
// src/fallback-llm.ts
|
|
98
294
|
var FallbackLlmClient = class {
|
|
99
295
|
gatewayConfig;
|
|
@@ -260,17 +456,35 @@ var FallbackLlmClient = class {
|
|
|
260
456
|
}
|
|
261
457
|
return { providerId, modelId, providerConfig, modelString };
|
|
262
458
|
}
|
|
459
|
+
/**
|
|
460
|
+
* Resolve the API key for a provider, handling OpenClaw secret ref formats.
|
|
461
|
+
* Results are cached per provider so exec calls only happen once.
|
|
462
|
+
*/
|
|
463
|
+
async resolveApiKey(providerId, providerConfig) {
|
|
464
|
+
return resolveProviderApiKey(
|
|
465
|
+
providerId,
|
|
466
|
+
providerConfig.apiKey,
|
|
467
|
+
this.gatewayConfig
|
|
468
|
+
);
|
|
469
|
+
}
|
|
263
470
|
/**
|
|
264
471
|
* Try to call a single model.
|
|
265
472
|
*/
|
|
266
473
|
async tryModel(model, messages, options) {
|
|
474
|
+
const rawKey = model.providerConfig.apiKey;
|
|
475
|
+
const needsResolution = rawKey === "secretref-managed" || typeof rawKey === "object" && rawKey !== null;
|
|
476
|
+
const resolvedApiKey = await this.resolveApiKey(model.providerId, model.providerConfig);
|
|
477
|
+
if (needsResolution && !resolvedApiKey) {
|
|
478
|
+
throw new Error(`API key for provider "${model.providerId}" could not be resolved from secret ref`);
|
|
479
|
+
}
|
|
480
|
+
const configWithResolvedKey = resolvedApiKey ? { ...model.providerConfig, apiKey: resolvedApiKey } : model.providerConfig;
|
|
267
481
|
switch (model.providerConfig.api) {
|
|
268
482
|
case "anthropic-messages":
|
|
269
|
-
return await this.callAnthropic(
|
|
483
|
+
return await this.callAnthropic(configWithResolvedKey, model.modelId, messages, options);
|
|
270
484
|
case "openai-completions":
|
|
271
485
|
default:
|
|
272
486
|
return await this.callOpenAI(
|
|
273
|
-
|
|
487
|
+
configWithResolvedKey,
|
|
274
488
|
model.modelId,
|
|
275
489
|
messages,
|
|
276
490
|
options,
|
|
@@ -288,7 +502,7 @@ var FallbackLlmClient = class {
|
|
|
288
502
|
"Content-Type": "application/json",
|
|
289
503
|
...config.headers
|
|
290
504
|
};
|
|
291
|
-
if (config.apiKey) {
|
|
505
|
+
if (config.apiKey && typeof config.apiKey === "string") {
|
|
292
506
|
if (config.authHeader !== false) {
|
|
293
507
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
294
508
|
}
|
|
@@ -334,7 +548,7 @@ var FallbackLlmClient = class {
|
|
|
334
548
|
"anthropic-version": "2023-06-01",
|
|
335
549
|
...config.headers
|
|
336
550
|
};
|
|
337
|
-
if (config.apiKey) {
|
|
551
|
+
if (config.apiKey && typeof config.apiKey === "string") {
|
|
338
552
|
headers["x-api-key"] = config.apiKey;
|
|
339
553
|
}
|
|
340
554
|
const systemMessage = messages.find((m) => m.role === "system")?.content;
|
|
@@ -383,4 +597,4 @@ export {
|
|
|
383
597
|
buildChatCompletionTokenLimit,
|
|
384
598
|
FallbackLlmClient
|
|
385
599
|
};
|
|
386
|
-
//# sourceMappingURL=chunk-
|
|
600
|
+
//# sourceMappingURL=chunk-AVOL6JGC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/json-extract.ts","../src/openai-chat-compat.ts","../src/resolve-provider-secret.ts","../src/fallback-llm.ts"],"sourcesContent":["/**\n * Utilities for extracting JSON payloads from LLM outputs.\n *\n * We see common failure modes:\n * - \"Here's an example: {..}\\nHere's the real answer: {..}\" (multiple JSON blocks)\n * - fenced ```json blocks\n * - leading/trailing prose around JSON\n *\n * These helpers attempt multiple candidates and let callers validate with schemas.\n */\n\nexport function stripCodeFences(text: string): string {\n return text.replace(/```(?:json)?\\s*([\\s\\S]*?)```/gi, (_m, inner) => String(inner).trim());\n}\n\nexport function extractJsonCandidates(text: string): string[] {\n const trimmed = text.trim();\n const cleaned = stripCodeFences(trimmed);\n const candidates: string[] = [];\n\n if (cleaned.length > 0) candidates.push(cleaned);\n candidates.push(...scanBalancedJsonBlocks(cleaned));\n\n // Legacy regex fallback (single object)\n const objMatch = cleaned.match(/\\{[\\s\\S]*\\}/);\n if (objMatch) candidates.push(objMatch[0]);\n\n const seen = new Set<string>();\n return candidates\n .map((c) => c.trim())\n .filter((c) => c.length > 0)\n .filter((c) => {\n if (seen.has(c)) return false;\n seen.add(c);\n return true;\n });\n}\n\nfunction scanBalancedJsonBlocks(text: string): string[] {\n const out: string[] = [];\n const opens = new Set([\"{\", \"[\"]);\n const closes: Record<string, string> = { \"{\": \"}\", \"[\": \"]\" };\n\n for (let i = 0; i < text.length; i++) {\n const start = text[i];\n if (!opens.has(start)) continue;\n\n const expectedClose = closes[start];\n let depth = 0;\n let inString = false;\n let escape = false;\n\n for (let j = i; j < text.length; j++) {\n const ch = text[j];\n\n if (inString) {\n if (escape) {\n escape = false;\n } else if (ch === \"\\\\\") {\n escape = true;\n } else if (ch === \"\\\"\") {\n inString = false;\n }\n continue;\n }\n\n if (ch === \"\\\"\") {\n inString = true;\n continue;\n }\n\n if (ch === start) depth++;\n if (ch === expectedClose) depth--;\n\n if (depth === 0) {\n out.push(text.slice(i, j + 1).trim());\n i = j;\n break;\n }\n }\n }\n\n return out;\n}\n\n","function normalizedModel(model: string): string {\n return model.trim().toLowerCase();\n}\n\nfunction matchesModelFamily(normalized: string, familyPattern: RegExp): boolean {\n return familyPattern.test(normalized);\n}\n\nexport function shouldAssumeOpenAiChatCompletions(baseUrl?: string): boolean {\n if (!baseUrl) return true;\n try {\n return new URL(baseUrl).hostname.toLowerCase() === \"api.openai.com\";\n } catch {\n return false;\n }\n}\n\nexport function usesMaxCompletionTokens(model: string, options?: { assumeOpenAI?: boolean }): boolean {\n const normalized = normalizedModel(model);\n if (options?.assumeOpenAI !== true) return false;\n if (matchesModelFamily(normalized, /^gpt-5(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4o(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^gpt-4\\.1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o1(?:$|[-.])/)) return true;\n if (matchesModelFamily(normalized, /^o3(?:$|[-.])/)) return true;\n return matchesModelFamily(normalized, /^o4-mini(?:$|[-.])/);\n}\n\nexport function buildChatCompletionTokenLimit(\n model: string,\n maxTokens: number,\n options?: { assumeOpenAI?: boolean },\n): { max_tokens: number } | { max_completion_tokens: number } {\n const safeMaxTokens = Math.max(0, Math.floor(maxTokens));\n if (usesMaxCompletionTokens(model, options)) {\n return { max_completion_tokens: safeMaxTokens };\n }\n return { max_tokens: safeMaxTokens };\n}\n","import { log } from \"./logger.js\";\nimport { execFileSync } from \"node:child_process\";\nimport { readFileSync, existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\n/**\n * Secret reference object format used by OpenClaw's models.json.\n */\ninterface SecretRef {\n source: \"exec\" | \"file\" | \"env\";\n provider?: string;\n id: string;\n command?: string;\n args?: string[];\n}\n\n/**\n * Auth profile entry from agent auth-profiles.json files.\n */\ninterface AuthProfile {\n type?: string;\n provider?: string;\n token?: string | SecretRef;\n access?: string;\n apiKey?: string;\n}\n\nconst resolvedCache = new Map<string, string | null>();\n\n/**\n * Resolve a provider API key from various OpenClaw secret formats.\n *\n * Supported formats:\n * - Plain string (not a marker) → used as-is\n * - SecretRef object (`{ source, provider, id }`) → resolved via exec/file/env\n * - `\"secretref-managed\"` → looked up from auth profiles and openclaw.json auth config\n * - `\"minimax-oauth\"`, `\"ollama-local\"`, etc. → treated as non-secret markers, skipped\n *\n * Results are cached per provider to avoid repeated exec calls.\n */\nexport async function resolveProviderApiKey(\n providerId: string,\n apiKeyValue: unknown,\n gatewayConfig?: { auth?: { profiles?: Record<string, unknown> } },\n): Promise<string | undefined> {\n // Fast path: plain string that isn't a marker\n if (typeof apiKeyValue === \"string\") {\n if (apiKeyValue === \"secretref-managed\") {\n return resolveSecretRefManaged(providerId, gatewayConfig);\n }\n // Known non-secret markers — skip\n if (\n apiKeyValue.endsWith(\"-oauth\") ||\n apiKeyValue.endsWith(\"-local\") ||\n apiKeyValue === \"lm-studio\" ||\n apiKeyValue.startsWith(\"gcp-\")\n ) {\n return undefined;\n }\n // Looks like an actual key\n return apiKeyValue;\n }\n\n // SecretRef object\n if (isSecretRefObject(apiKeyValue)) {\n return resolveSecretRef(providerId, apiKeyValue as SecretRef);\n }\n\n return undefined;\n}\n\nfunction isSecretRefObject(value: unknown): value is SecretRef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"source\" in value &&\n \"id\" in value &&\n typeof (value as SecretRef).source === \"string\" &&\n typeof (value as SecretRef).id === \"string\"\n );\n}\n\nasync function resolveSecretRef(\n providerId: string,\n ref: SecretRef,\n): Promise<string | undefined> {\n const cacheKey = `ref:${ref.source}:${ref.provider ?? \"\"}:${ref.id}:${ref.command ?? \"\"}:${(ref.args ?? []).join(\",\")}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined) {\n // Don't cache failures permanently — only cache successes\n if (cached !== null) return cached;\n }\n\n let resolved: string | undefined;\n\n try {\n switch (ref.source) {\n case \"exec\":\n resolved = resolveExecSecret(ref);\n break;\n case \"file\":\n resolved = resolveFileSecret(ref);\n break;\n case \"env\":\n resolved = process.env[ref.id] ?? undefined;\n break;\n }\n } catch (err) {\n log.warn(\n `secret resolution failed for provider \"${providerId}\" (${ref.source}/${ref.provider}): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // Only cache successful resolutions; failures are retried next time\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n\n if (resolved) {\n log.debug(`resolved API key for provider \"${providerId}\" via ${ref.source}/${ref.provider ?? \"default\"}`);\n }\n\n return resolved;\n}\n\nfunction resolveExecSecret(ref: SecretRef): string | undefined {\n const command = ref.command ?? (ref.provider === \"op\" ? \"op\" : undefined);\n if (!command) {\n log.warn(`exec secret ref has no command and provider \"${ref.provider}\" is not a known exec provider`);\n return undefined;\n }\n\n // Use execFileSync (not execSync) to avoid shell injection —\n // arguments are passed as an array, never interpolated into a shell string.\n const args = ref.args ?? (ref.provider === \"op\" ? [\"read\", ref.id] : [ref.id]);\n try {\n const result = execFileSync(command, args, {\n encoding: \"utf-8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n return result || undefined;\n } catch (err) {\n log.warn(`exec secret resolution failed (${command}): ${err instanceof Error ? err.message : String(err)}`);\n return undefined;\n }\n}\n\nfunction resolveFileSecret(ref: SecretRef): string | undefined {\n if (ref.provider === \"op\") {\n // Try reading from the OpenClaw secrets directory\n const secretsDir = path.join(os.homedir(), \".openclaw\", \"secrets\");\n const filePath = path.join(secretsDir, ref.id.replace(/^\\//, \"\"));\n if (existsSync(filePath)) {\n try {\n return readFileSync(filePath, \"utf-8\").trim() || undefined;\n } catch {\n // Fall through to op exec\n }\n }\n\n // Fall back to `op read` via execFileSync (no shell injection)\n try {\n const result = execFileSync(\"op\", [\"read\", ref.id], {\n encoding: \"utf-8\",\n timeout: 10_000,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n }).trim();\n return result || undefined;\n } catch {\n // Silent — op may not be available\n }\n } else {\n // Non-OP file provider: treat ref.id as a file path (absolute or relative to secrets dir)\n const filePath = path.isAbsolute(ref.id)\n ? ref.id\n : path.join(os.homedir(), \".openclaw\", \"secrets\", ref.id.replace(/^\\//, \"\"));\n if (existsSync(filePath)) {\n try {\n return readFileSync(filePath, \"utf-8\").trim() || undefined;\n } catch {\n // Silent\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveSecretRefManaged(\n providerId: string,\n gatewayConfig?: { auth?: { profiles?: Record<string, unknown> } },\n): string | undefined {\n const cacheKey = `managed:${providerId}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n let resolved: string | undefined;\n\n // Look up auth profiles for this provider\n const profiles = gatewayConfig?.auth?.profiles;\n if (profiles) {\n // Try provider:default, provider:manual, etc.\n const candidates = [\n `${providerId}:default`,\n `${providerId}:manual`,\n providerId,\n ];\n\n for (const profileKey of candidates) {\n const raw = profiles[profileKey];\n if (!raw || typeof raw !== \"object\") continue;\n const profile = raw as AuthProfile;\n\n // Token-based auth\n if (profile.token) {\n if (typeof profile.token === \"string\" && !profile.token.startsWith(\"{\")) {\n resolved = profile.token;\n break;\n }\n if (isSecretRefObject(profile.token)) {\n // Synchronously resolve — this is at init time\n try {\n resolved = resolveSecretRefSync(providerId, profile.token as SecretRef);\n } catch {\n // Continue to next profile\n }\n if (resolved) break;\n }\n }\n\n // API key auth\n if (profile.apiKey && typeof profile.apiKey === \"string\") {\n resolved = profile.apiKey;\n break;\n }\n\n // Access token (OAuth)\n if (profile.access && typeof profile.access === \"string\") {\n resolved = profile.access;\n break;\n }\n }\n }\n\n // Fall back to environment variables\n if (!resolved) {\n const envCandidates = [\n `${providerId.toUpperCase().replace(/-/g, \"_\")}_API_KEY`,\n `${providerId.toUpperCase().replace(/-/g, \"_\")}_TOKEN`,\n ];\n for (const envVar of envCandidates) {\n if (process.env[envVar]) {\n resolved = process.env[envVar];\n break;\n }\n }\n }\n\n // Only cache successful resolutions; failures are retried next time\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n log.debug(`resolved managed API key for provider \"${providerId}\" via auth profile`);\n } else {\n log.debug(`could not resolve managed API key for provider \"${providerId}\"`);\n }\n\n return resolved;\n}\n\nfunction resolveSecretRefSync(\n providerId: string,\n ref: SecretRef,\n): string | undefined {\n const cacheKey = `ref:${ref.source}:${ref.provider ?? \"\"}:${ref.id}:${ref.command ?? \"\"}:${(ref.args ?? []).join(\",\")}`;\n const cached = resolvedCache.get(cacheKey);\n if (cached !== undefined && cached !== null) {\n return cached;\n }\n\n let resolved: string | undefined;\n\n try {\n switch (ref.source) {\n case \"exec\":\n resolved = resolveExecSecret(ref);\n break;\n case \"file\":\n resolved = resolveFileSecret(ref);\n break;\n case \"env\":\n resolved = process.env[ref.id] ?? undefined;\n break;\n }\n } catch (err) {\n log.warn(\n `sync secret resolution failed for provider \"${providerId}\": ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (resolved) {\n resolvedCache.set(cacheKey, resolved);\n }\n return resolved;\n}\n\n/**\n * Clear the resolution cache (useful for testing or key rotation).\n */\nexport function clearSecretCache(): void {\n resolvedCache.clear();\n}\n","import { log } from \"./logger.js\";\nimport type { GatewayConfig, ModelProviderConfig, AgentPersona } from \"./types.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport {\n buildChatCompletionTokenLimit,\n shouldAssumeOpenAiChatCompletions,\n} from \"./openai-chat-compat.js\";\nimport { resolveProviderApiKey } from \"./resolve-provider-secret.js\";\n\nexport interface FallbackLlmOptions {\n temperature?: number;\n maxTokens?: number;\n timeoutMs?: number;\n /** Override which agent persona's model chain to use (by ID from agents.list[]). */\n agentId?: string;\n}\n\nexport interface FallbackLlmResponse {\n content: string;\n modelUsed: string;\n usage?: {\n inputTokens?: number;\n outputTokens?: number;\n totalTokens?: number;\n };\n}\n\ninterface ModelRef {\n providerId: string;\n modelId: string;\n providerConfig: ModelProviderConfig;\n modelString: string;\n}\n\n/**\n * Generic fallback LLM client that uses the gateway's default AI configuration\n * and walks through the full fallback chain (primary + fallbacks).\n * Supports OpenAI and Anthropic API formats.\n */\nexport class FallbackLlmClient {\n private gatewayConfig: GatewayConfig | undefined;\n\n constructor(gatewayConfig?: GatewayConfig) {\n this.gatewayConfig = gatewayConfig;\n }\n\n /**\n * Check if fallback is available (gateway config has at least one model).\n */\n isAvailable(agentId?: string): boolean {\n const models = this.getModelChain(agentId);\n return models.length > 0;\n }\n\n /**\n * Make a chat completion request using the gateway's default AI chain.\n * Tries primary first, then each fallback in order.\n * When agentId is provided, uses that agent persona's model chain instead of defaults.\n */\n async chatCompletion(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions = {},\n ): Promise<FallbackLlmResponse | null> {\n const models = this.getModelChain(options.agentId);\n if (models.length === 0) {\n log.warn(\"fallback LLM: no models configured in gateway\");\n return null;\n }\n\n const runChain = async (): Promise<FallbackLlmResponse | null> => {\n // Try each model in the chain\n for (let i = 0; i < models.length; i++) {\n const model = models[i];\n const isFallback = i > 0;\n\n try {\n const result = await this.tryModel(model, messages, options);\n if (result) {\n if (isFallback) {\n log.info(`fallback LLM: succeeded using ${model.modelString} (fallback ${i})`);\n }\n return {\n content: result.content,\n modelUsed: model.modelString,\n usage: result.usage,\n };\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n log.debug(`fallback LLM: ${model.modelString} failed (${errorMsg}), trying next...`);\n // Continue to next model in chain\n }\n }\n\n log.warn(`fallback LLM: all ${models.length} models in chain failed`);\n return null;\n };\n\n if (typeof options.timeoutMs === \"number\") {\n if (options.timeoutMs <= 0) {\n log.warn(\"fallback LLM: timed out before request started\");\n return null;\n }\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n runChain(),\n new Promise<null>((resolve) => {\n timeoutHandle = setTimeout(() => {\n log.warn(`fallback LLM: timed out after ${options.timeoutMs}ms`);\n resolve(null);\n }, options.timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutHandle) clearTimeout(timeoutHandle);\n }\n }\n\n return await runChain();\n }\n\n /**\n * Make a request with structured output (Zod schema).\n * Returns parsed JSON or null on failure.\n */\n async parseWithSchema<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<T | null> {\n const detailed = await this.parseWithSchemaDetailed(messages, schema, options);\n return detailed?.result ?? null;\n }\n\n /**\n * Like parseWithSchema but also returns the model that was used,\n * so callers can emit accurate trace events.\n */\n async parseWithSchemaDetailed<T>(\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n schema: { parse: (data: unknown) => T },\n options: FallbackLlmOptions = {},\n ): Promise<{ result: T; modelUsed: string } | null> {\n const response = await this.chatCompletion(messages, options);\n if (!response?.content) return null;\n\n try {\n const candidates = extractJsonCandidates(response.content);\n for (const c of candidates) {\n try {\n const parsed = JSON.parse(c);\n return { result: schema.parse(parsed), modelUsed: response.modelUsed };\n } catch {\n // keep trying other candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"fallback LLM: failed to parse structured output:\", err);\n return null;\n }\n }\n\n /**\n * Get the full model chain from gateway config.\n * Returns array of models in order: [primary, fallback1, fallback2, ...]\n *\n * When agentId is provided, looks up the matching entry in agents.list[]\n * and uses that persona's model chain. Falls back to agents.defaults.model\n * if agentId is not found or not provided.\n */\n private getModelChain(agentId?: string): ModelRef[] {\n const chain: ModelRef[] = [];\n const providers = this.gatewayConfig?.models?.providers;\n\n if (!providers) return chain;\n\n // Resolve the model config: agent persona chain or global defaults\n let modelConfig: { primary?: string; fallbacks?: string[] } | undefined;\n\n if (agentId) {\n const persona = this.gatewayConfig?.agents?.list?.find(\n (a) => a.id === agentId,\n );\n if (persona?.model) {\n modelConfig = persona.model;\n log.debug(`fallback LLM: using agent persona \"${agentId}\" model chain`);\n } else {\n log.warn(\n `fallback LLM: agent persona \"${agentId}\" not found or has no model config, falling back to defaults`,\n );\n }\n }\n\n if (!modelConfig) {\n modelConfig = this.gatewayConfig?.agents?.defaults?.model;\n }\n\n // Build list of model strings: primary + fallbacks\n const modelStrings: string[] = [];\n\n if (modelConfig?.primary) {\n modelStrings.push(modelConfig.primary);\n }\n\n if (Array.isArray(modelConfig?.fallbacks)) {\n for (const fb of modelConfig.fallbacks) {\n if (typeof fb === \"string\" && !modelStrings.includes(fb)) {\n modelStrings.push(fb);\n }\n }\n }\n\n // Parse each model string and look up provider config\n for (const modelString of modelStrings) {\n const modelRef = this.parseModelString(modelString, providers);\n if (modelRef) {\n chain.push(modelRef);\n }\n }\n\n return chain;\n }\n\n /**\n * Parse a \"provider/model\" string and look up its config.\n */\n private parseModelString(\n modelString: string,\n providers: Record<string, ModelProviderConfig>,\n ): ModelRef | null {\n // Parse \"provider/model\" format (e.g., \"openai/gpt-5.2\", \"anthropic/claude-opus-4-6\")\n const parts = modelString.split(\"/\");\n if (parts.length < 2) {\n log.warn(`fallback LLM: invalid model format: ${modelString}`);\n return null;\n }\n\n const providerId = parts[0];\n const modelId = parts.slice(1).join(\"/\"); // Handle cases like \"openai/gpt-5.2-turbo\"\n\n const providerConfig = providers[providerId];\n if (!providerConfig) {\n log.warn(`fallback LLM: provider not found: ${providerId}`);\n return null;\n }\n\n return { providerId, modelId, providerConfig, modelString };\n }\n\n /**\n * Resolve the API key for a provider, handling OpenClaw secret ref formats.\n * Results are cached per provider so exec calls only happen once.\n */\n private async resolveApiKey(\n providerId: string,\n providerConfig: ModelProviderConfig,\n ): Promise<string | undefined> {\n return resolveProviderApiKey(\n providerId,\n providerConfig.apiKey,\n this.gatewayConfig as { auth?: { profiles?: Record<string, unknown> } } | undefined,\n );\n }\n\n /**\n * Try to call a single model.\n */\n private async tryModel(\n model: ModelRef,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n // Resolve the API key from secret refs before making the call.\n // If the raw key looks like an unresolved secret ref and resolution fails,\n // skip this provider entirely so the chain falls through to the next.\n const rawKey = model.providerConfig.apiKey;\n const needsResolution = rawKey === \"secretref-managed\"\n || (typeof rawKey === \"object\" && rawKey !== null);\n const resolvedApiKey = await this.resolveApiKey(model.providerId, model.providerConfig);\n\n if (needsResolution && !resolvedApiKey) {\n throw new Error(`API key for provider \"${model.providerId}\" could not be resolved from secret ref`);\n }\n\n const configWithResolvedKey = resolvedApiKey\n ? { ...model.providerConfig, apiKey: resolvedApiKey }\n : model.providerConfig;\n\n switch (model.providerConfig.api) {\n case \"anthropic-messages\":\n return await this.callAnthropic(configWithResolvedKey, model.modelId, messages, options);\n case \"openai-completions\":\n default:\n return await this.callOpenAI(\n configWithResolvedKey,\n model.modelId,\n messages,\n options,\n shouldAssumeOpenAiChatCompletions(model.providerConfig.baseUrl),\n );\n }\n }\n\n /**\n * Call OpenAI-compatible API.\n */\n private async callOpenAI(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n assumeOpenAI: boolean,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const base = config.baseUrl.replace(/\\/$/, \"\");\n const url = base.endsWith(\"/v1\")\n ? `${base}/chat/completions`\n : `${base}/v1/chat/completions`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n };\n\n // Handle auth — apiKey is already resolved to a string by tryModel()\n if (config.apiKey && typeof config.apiKey === \"string\") {\n if (config.authHeader !== false) {\n headers[\"Authorization\"] = `Bearer ${config.apiKey}`;\n }\n }\n\n const body = {\n model: modelId,\n messages,\n temperature: options.temperature ?? 0.3,\n ...buildChatCompletionTokenLimit(modelId, options.maxTokens ?? 4096, {\n assumeOpenAI,\n }),\n };\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`OpenAI API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n choices: Array<{\n message: {\n content: string;\n };\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n };\n\n const content = data.choices?.[0]?.message?.content;\n if (!content) {\n throw new Error(\"Empty response from OpenAI API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.prompt_tokens,\n outputTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n }\n\n /**\n * Call Anthropic Messages API.\n */\n private async callAnthropic(\n config: ModelProviderConfig,\n modelId: string,\n messages: Array<{ role: \"system\" | \"user\" | \"assistant\"; content: string }>,\n options: FallbackLlmOptions,\n ): Promise<{ content: string; usage?: FallbackLlmResponse[\"usage\"] } | null> {\n const url = `${config.baseUrl.replace(/\\/$/, \"\")}/messages`;\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n \"anthropic-version\": \"2023-06-01\",\n ...config.headers,\n };\n\n // Handle auth - Anthropic uses x-api-key header (apiKey resolved by tryModel)\n if (config.apiKey && typeof config.apiKey === \"string\") {\n headers[\"x-api-key\"] = config.apiKey;\n }\n\n // Extract system message (Anthropic handles it separately)\n const systemMessage = messages.find((m) => m.role === \"system\")?.content;\n const nonSystemMessages = messages.filter((m) => m.role !== \"system\");\n\n // Convert messages to Anthropic format\n const anthropicMessages = nonSystemMessages.map((m) => ({\n role: m.role,\n content: m.content,\n }));\n\n const body: Record<string, unknown> = {\n model: modelId,\n messages: anthropicMessages,\n max_tokens: options.maxTokens ?? 4096,\n temperature: options.temperature ?? 0.3,\n };\n\n if (systemMessage) {\n body.system = systemMessage;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic API error: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n content: Array<{\n type: string;\n text: string;\n }>;\n usage?: {\n input_tokens?: number;\n output_tokens?: number;\n };\n };\n\n const content = data.content?.[0]?.text;\n if (!content) {\n throw new Error(\"Empty response from Anthropic API\");\n }\n\n return {\n content,\n usage: data.usage\n ? {\n inputTokens: data.usage.input_tokens,\n outputTokens: data.usage.output_tokens,\n totalTokens: (data.usage.input_tokens ?? 0) + (data.usage.output_tokens ?? 0),\n }\n : undefined,\n };\n }\n}\n"],"mappings":"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","names":[]}
|