@elizaos/plugin-openrouter 1.3.1 → 1.5.10
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 +32 -0
- package/dist/browser/index.browser.js +968 -0
- package/dist/browser/index.browser.js.map +20 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.node.cjs +630 -0
- package/dist/cjs/index.node.js.map +19 -0
- package/dist/index.browser.d.ts +2 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.node.d.ts +2 -0
- package/dist/init.d.ts +6 -0
- package/dist/models/image.d.ts +14 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/object.d.ts +9 -0
- package/dist/models/text.d.ts +17 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/{index.js → node/index.node.js} +178 -118
- package/dist/node/index.node.js.map +19 -0
- package/dist/providers/index.d.ts +1 -0
- package/dist/providers/openrouter.d.ts +8 -0
- package/dist/types/index.d.ts +60 -0
- package/dist/utils/config.d.ts +57 -0
- package/dist/utils/events.d.ts +6 -0
- package/dist/utils/helpers.d.ts +29 -0
- package/dist/utils/image-storage.d.ts +8 -0
- package/dist/utils/index.d.ts +2 -0
- package/package.json +27 -20
- package/dist/index.js.map +0 -1
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
|
+
|
|
1
20
|
// src/index.ts
|
|
2
21
|
import {
|
|
3
22
|
ModelType as ModelType3
|
|
@@ -12,11 +31,11 @@ function getSetting(runtime, key, defaultValue) {
|
|
|
12
31
|
return runtime.getSetting(key) ?? process.env[key] ?? defaultValue;
|
|
13
32
|
}
|
|
14
33
|
function getBaseURL(runtime) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
) || "https://openrouter.ai/api/v1";
|
|
34
|
+
const browserURL = getSetting(runtime, "OPENROUTER_BROWSER_BASE_URL");
|
|
35
|
+
if (typeof globalThis !== "undefined" && globalThis.document && browserURL) {
|
|
36
|
+
return browserURL;
|
|
37
|
+
}
|
|
38
|
+
return getSetting(runtime, "OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1") || "https://openrouter.ai/api/v1";
|
|
20
39
|
}
|
|
21
40
|
function getApiKey(runtime) {
|
|
22
41
|
return getSetting(runtime, "OPENROUTER_API_KEY");
|
|
@@ -25,11 +44,7 @@ function getSmallModel(runtime) {
|
|
|
25
44
|
return getSetting(runtime, "OPENROUTER_SMALL_MODEL") ?? getSetting(runtime, "SMALL_MODEL", "google/gemini-2.0-flash-001") ?? "google/gemini-2.0-flash-001";
|
|
26
45
|
}
|
|
27
46
|
function getLargeModel(runtime) {
|
|
28
|
-
return getSetting(runtime, "OPENROUTER_LARGE_MODEL") ?? getSetting(
|
|
29
|
-
runtime,
|
|
30
|
-
"LARGE_MODEL",
|
|
31
|
-
"google/gemini-2.5-flash"
|
|
32
|
-
) ?? "google/gemini-2.5-flash";
|
|
47
|
+
return getSetting(runtime, "OPENROUTER_LARGE_MODEL") ?? getSetting(runtime, "LARGE_MODEL", "google/gemini-2.5-flash") ?? "google/gemini-2.5-flash";
|
|
33
48
|
}
|
|
34
49
|
function getImageModel(runtime) {
|
|
35
50
|
return getSetting(runtime, "OPENROUTER_IMAGE_MODEL") ?? getSetting(runtime, "IMAGE_MODEL", "x-ai/grok-2-vision-1212") ?? "x-ai/grok-2-vision-1212";
|
|
@@ -46,10 +61,12 @@ function shouldAutoCleanupImages(runtime) {
|
|
|
46
61
|
function initializeOpenRouter(_config, runtime) {
|
|
47
62
|
(async () => {
|
|
48
63
|
try {
|
|
64
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.document;
|
|
65
|
+
if (isBrowser) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
49
68
|
if (!getApiKey(runtime)) {
|
|
50
|
-
logger.warn(
|
|
51
|
-
"OPENROUTER_API_KEY is not set in environment - OpenRouter functionality will be limited"
|
|
52
|
-
);
|
|
69
|
+
logger.warn("OPENROUTER_API_KEY is not set in environment - OpenRouter functionality will be limited");
|
|
53
70
|
return;
|
|
54
71
|
}
|
|
55
72
|
try {
|
|
@@ -58,73 +75,62 @@ function initializeOpenRouter(_config, runtime) {
|
|
|
58
75
|
headers: { Authorization: `Bearer ${getApiKey(runtime)}` }
|
|
59
76
|
});
|
|
60
77
|
if (!response.ok) {
|
|
61
|
-
logger.warn(
|
|
62
|
-
|
|
63
|
-
);
|
|
64
|
-
logger.warn(
|
|
65
|
-
"OpenRouter functionality will be limited until a valid API key is provided"
|
|
66
|
-
);
|
|
78
|
+
logger.warn(`OpenRouter API key validation failed: ${response.statusText}`);
|
|
79
|
+
logger.warn("OpenRouter functionality will be limited until a valid API key is provided");
|
|
67
80
|
} else {
|
|
68
81
|
logger.log("OpenRouter API key validated successfully");
|
|
69
82
|
}
|
|
70
83
|
} catch (fetchError) {
|
|
71
84
|
const message = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
72
85
|
logger.warn(`Error validating OpenRouter API key: ${message}`);
|
|
73
|
-
logger.warn(
|
|
74
|
-
"OpenRouter functionality will be limited until a valid API key is provided"
|
|
75
|
-
);
|
|
86
|
+
logger.warn("OpenRouter functionality will be limited until a valid API key is provided");
|
|
76
87
|
}
|
|
77
88
|
} catch (error) {
|
|
78
89
|
const message = error?.errors?.map((e) => e.message).join(", ") || (error instanceof Error ? error.message : String(error));
|
|
79
|
-
logger.warn(
|
|
80
|
-
`OpenRouter plugin configuration issue: ${message} - You need to configure the OPENROUTER_API_KEY in your environment variables`
|
|
81
|
-
);
|
|
90
|
+
logger.warn(`OpenRouter plugin configuration issue: ${message} - You need to configure the OPENROUTER_API_KEY in your environment variables`);
|
|
82
91
|
}
|
|
83
92
|
})();
|
|
84
93
|
return;
|
|
85
94
|
}
|
|
86
95
|
|
|
87
96
|
// src/models/text.ts
|
|
88
|
-
import { logger as
|
|
97
|
+
import { logger as logger3, ModelType } from "@elizaos/core";
|
|
89
98
|
import { generateText } from "ai";
|
|
90
99
|
|
|
91
100
|
// src/providers/openrouter.ts
|
|
92
101
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
93
|
-
import { logger as logger2 } from "@elizaos/core";
|
|
94
102
|
function createOpenRouterProvider(runtime) {
|
|
95
103
|
const apiKey = getApiKey(runtime);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"OpenRouter API Key is missing when trying to create provider"
|
|
99
|
-
);
|
|
100
|
-
throw new Error("OpenRouter API Key is missing.");
|
|
101
|
-
}
|
|
104
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.document;
|
|
105
|
+
const baseURL = getBaseURL(runtime);
|
|
102
106
|
return createOpenRouter({
|
|
103
|
-
apiKey
|
|
104
|
-
|
|
105
|
-
// The @ai-sdk/provider utils might handle OPENROUTER_BASE_URL env var.
|
|
107
|
+
apiKey: isBrowser ? undefined : apiKey,
|
|
108
|
+
baseURL
|
|
106
109
|
});
|
|
107
110
|
}
|
|
108
|
-
|
|
109
111
|
// src/utils/events.ts
|
|
110
112
|
import {
|
|
111
113
|
EventType
|
|
112
114
|
} from "@elizaos/core";
|
|
113
115
|
function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
116
|
+
const truncatedPrompt = typeof prompt === "string" ? prompt.length > 200 ? `${prompt.slice(0, 200)}…` : prompt : "";
|
|
117
|
+
const inputTokens = Number(usage.inputTokens || 0);
|
|
118
|
+
const outputTokens = Number(usage.outputTokens || 0);
|
|
119
|
+
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
114
120
|
runtime.emitEvent(EventType.MODEL_USED, {
|
|
115
121
|
provider: "openrouter",
|
|
116
122
|
type,
|
|
117
|
-
prompt,
|
|
123
|
+
prompt: truncatedPrompt,
|
|
118
124
|
tokens: {
|
|
119
|
-
prompt:
|
|
120
|
-
completion:
|
|
121
|
-
total:
|
|
125
|
+
prompt: inputTokens,
|
|
126
|
+
completion: outputTokens,
|
|
127
|
+
total: totalTokens
|
|
122
128
|
}
|
|
123
129
|
});
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
// src/utils/helpers.ts
|
|
127
|
-
import { logger as
|
|
133
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
128
134
|
import { JSONParseError } from "ai";
|
|
129
135
|
function getJsonRepairFunction() {
|
|
130
136
|
return async ({ text, error }) => {
|
|
@@ -137,15 +143,15 @@ function getJsonRepairFunction() {
|
|
|
137
143
|
return null;
|
|
138
144
|
} catch (jsonError) {
|
|
139
145
|
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
140
|
-
|
|
146
|
+
logger2.warn(`Failed to repair JSON text: ${message}`);
|
|
141
147
|
return null;
|
|
142
148
|
}
|
|
143
149
|
};
|
|
144
150
|
}
|
|
145
151
|
function handleEmptyToolResponse(modelType) {
|
|
146
|
-
|
|
152
|
+
logger2.warn(`[${modelType}] No text generated after tool execution`);
|
|
147
153
|
const fallbackText = "I executed the requested action. The tool completed successfully.";
|
|
148
|
-
|
|
154
|
+
logger2.warn(`[${modelType}] Using fallback response text`);
|
|
149
155
|
return fallbackText;
|
|
150
156
|
}
|
|
151
157
|
function parseImageDescriptionResponse(responseText) {
|
|
@@ -155,7 +161,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
155
161
|
return jsonResponse;
|
|
156
162
|
}
|
|
157
163
|
} catch (e) {
|
|
158
|
-
|
|
164
|
+
logger2.debug(`Parsing as JSON failed, processing as text: ${e}`);
|
|
159
165
|
}
|
|
160
166
|
const titleMatch = responseText.match(/title[:\s]+(.+?)(?:\n|$)/i);
|
|
161
167
|
const title = titleMatch?.[1]?.trim() || "Image Analysis";
|
|
@@ -164,7 +170,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
164
170
|
}
|
|
165
171
|
async function handleObjectGenerationError(error) {
|
|
166
172
|
if (error instanceof JSONParseError) {
|
|
167
|
-
|
|
173
|
+
logger2.error(`[generateObject] Failed to parse JSON: ${error.message}`);
|
|
168
174
|
const repairFunction = getJsonRepairFunction();
|
|
169
175
|
const repairedJsonString = await repairFunction({
|
|
170
176
|
text: error.text,
|
|
@@ -173,47 +179,54 @@ async function handleObjectGenerationError(error) {
|
|
|
173
179
|
if (repairedJsonString) {
|
|
174
180
|
try {
|
|
175
181
|
const repairedObject = JSON.parse(repairedJsonString);
|
|
176
|
-
|
|
182
|
+
logger2.log("[generateObject] Successfully repaired JSON.");
|
|
177
183
|
return repairedObject;
|
|
178
184
|
} catch (repairParseError) {
|
|
179
185
|
const message = repairParseError instanceof Error ? repairParseError.message : String(repairParseError);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (repairParseError instanceof Error) throw repairParseError;
|
|
186
|
+
logger2.error(`[generateObject] Failed to parse repaired JSON: ${message}`);
|
|
187
|
+
if (repairParseError instanceof Error)
|
|
188
|
+
throw repairParseError;
|
|
184
189
|
throw Object.assign(new Error(message), { cause: repairParseError });
|
|
185
190
|
}
|
|
186
191
|
} else {
|
|
187
|
-
|
|
192
|
+
logger2.error("[generateObject] JSON repair failed.");
|
|
188
193
|
throw error;
|
|
189
194
|
}
|
|
190
195
|
} else {
|
|
191
196
|
const message = error instanceof Error ? error.message : String(error);
|
|
192
|
-
|
|
193
|
-
if (error instanceof Error)
|
|
197
|
+
logger2.error(`[generateObject] Unknown error: ${message}`);
|
|
198
|
+
if (error instanceof Error)
|
|
199
|
+
throw error;
|
|
194
200
|
throw Object.assign(new Error(message), { cause: error });
|
|
195
201
|
}
|
|
196
202
|
}
|
|
197
203
|
function isLikelyBase64(key, value) {
|
|
198
204
|
const base64KeyPattern = /^(data|content|body|payload|encoded|b64|base64|document)$/i;
|
|
199
|
-
if (!base64KeyPattern.test(key))
|
|
200
|
-
|
|
201
|
-
if (value.length
|
|
202
|
-
|
|
205
|
+
if (!base64KeyPattern.test(key))
|
|
206
|
+
return false;
|
|
207
|
+
if (value.length < 20 || value.length > 1024 * 1024)
|
|
208
|
+
return false;
|
|
209
|
+
if (value.length % 4 !== 0)
|
|
210
|
+
return false;
|
|
211
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
212
|
+
return false;
|
|
203
213
|
return true;
|
|
204
214
|
}
|
|
205
215
|
function decodeBase64Fields(obj, depth = 0) {
|
|
206
|
-
if (depth > 5)
|
|
207
|
-
|
|
208
|
-
if (
|
|
216
|
+
if (depth > 5)
|
|
217
|
+
return obj;
|
|
218
|
+
if (!obj || typeof obj !== "object")
|
|
219
|
+
return obj;
|
|
220
|
+
if (Array.isArray(obj))
|
|
221
|
+
return obj.map((item) => decodeBase64Fields(item, depth + 1));
|
|
209
222
|
const decoded = {};
|
|
210
223
|
for (const [key, value] of Object.entries(obj)) {
|
|
211
224
|
if (typeof value === "string" && isLikelyBase64(key, value)) {
|
|
212
225
|
try {
|
|
213
226
|
decoded[key] = Buffer.from(value, "base64").toString("utf8");
|
|
214
|
-
|
|
227
|
+
logger2.debug(`[decodeBase64] Decoded field '${key}' (${value.length} chars)`);
|
|
215
228
|
} catch (error) {
|
|
216
|
-
|
|
229
|
+
logger2.warn(`[decodeBase64] Failed to decode field '${key}': ${error}`);
|
|
217
230
|
decoded[key] = value;
|
|
218
231
|
}
|
|
219
232
|
} else if (value && typeof value === "object") {
|
|
@@ -231,31 +244,23 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
231
244
|
const temperature = params.temperature ?? 0.7;
|
|
232
245
|
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
233
246
|
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
234
|
-
const
|
|
247
|
+
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
235
248
|
const openrouter = createOpenRouterProvider(runtime);
|
|
236
249
|
const modelName = modelType === ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
237
250
|
const modelLabel = modelType === ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
238
|
-
|
|
239
|
-
`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`
|
|
240
|
-
);
|
|
251
|
+
logger3.log(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
241
252
|
const generateParams = {
|
|
242
253
|
model: openrouter.chat(modelName),
|
|
243
254
|
prompt,
|
|
244
|
-
system: runtime.character.system ??
|
|
255
|
+
system: runtime.character.system ?? undefined,
|
|
245
256
|
temperature,
|
|
246
|
-
maxTokens: maxResponseLength,
|
|
247
257
|
frequencyPenalty,
|
|
248
258
|
presencePenalty,
|
|
249
259
|
stopSequences
|
|
250
260
|
};
|
|
261
|
+
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
251
262
|
if (tools) {
|
|
252
263
|
generateParams.tools = tools;
|
|
253
|
-
generateParams.maxSteps = 10;
|
|
254
|
-
generateParams.extra_body = {
|
|
255
|
-
provider: {
|
|
256
|
-
require_parameters: true
|
|
257
|
-
}
|
|
258
|
-
};
|
|
259
264
|
}
|
|
260
265
|
if (toolChoice) {
|
|
261
266
|
generateParams.toolChoice = toolChoice;
|
|
@@ -265,11 +270,14 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
265
270
|
if (tools) {
|
|
266
271
|
generateParams.onStepFinish = async (stepResult) => {
|
|
267
272
|
if (stepResult.toolCalls && stepResult.toolCalls.length > 0) {
|
|
268
|
-
capturedToolCalls = [
|
|
273
|
+
capturedToolCalls = [
|
|
274
|
+
...capturedToolCalls,
|
|
275
|
+
...stepResult.toolCalls
|
|
276
|
+
];
|
|
269
277
|
}
|
|
270
278
|
if (stepResult.toolResults && stepResult.toolResults.length > 0) {
|
|
271
279
|
const decodedToolResults = stepResult.toolResults.map((result) => ({
|
|
272
|
-
|
|
280
|
+
toolCallId: result.toolCallId,
|
|
273
281
|
result: decodeBase64Fields(result.result)
|
|
274
282
|
}));
|
|
275
283
|
capturedToolResults = [...capturedToolResults, ...decodedToolResults];
|
|
@@ -291,7 +299,6 @@ async function generateTextWithModel(runtime, modelType, params) {
|
|
|
291
299
|
text: responseText,
|
|
292
300
|
toolCalls: capturedToolCalls,
|
|
293
301
|
toolResults: capturedToolResults,
|
|
294
|
-
// Include other useful properties
|
|
295
302
|
usage: response.usage,
|
|
296
303
|
finishReason: response.finishReason
|
|
297
304
|
};
|
|
@@ -308,14 +315,14 @@ async function handleTextLarge(runtime, params) {
|
|
|
308
315
|
// src/models/object.ts
|
|
309
316
|
import {
|
|
310
317
|
ModelType as ModelType2,
|
|
311
|
-
logger as
|
|
318
|
+
logger as logger4
|
|
312
319
|
} from "@elizaos/core";
|
|
313
320
|
import { generateObject } from "ai";
|
|
314
321
|
async function generateObjectWithModel(runtime, modelType, params) {
|
|
315
322
|
const openrouter = createOpenRouterProvider(runtime);
|
|
316
323
|
const modelName = modelType === ModelType2.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
317
324
|
const modelLabel = modelType === ModelType2.OBJECT_SMALL ? "OBJECT_SMALL" : "OBJECT_LARGE";
|
|
318
|
-
|
|
325
|
+
logger4.log(`[OpenRouter] Using ${modelLabel} model: ${modelName}`);
|
|
319
326
|
const temperature = params.temperature ?? 0.7;
|
|
320
327
|
try {
|
|
321
328
|
const { object, usage } = await generateObject({
|
|
@@ -342,18 +349,61 @@ async function handleObjectLarge(runtime, params) {
|
|
|
342
349
|
|
|
343
350
|
// src/models/image.ts
|
|
344
351
|
import {
|
|
345
|
-
logger as
|
|
352
|
+
logger as logger6
|
|
346
353
|
} from "@elizaos/core";
|
|
347
354
|
import { generateText as generateText2 } from "ai";
|
|
348
355
|
|
|
349
356
|
// src/utils/image-storage.ts
|
|
350
|
-
import {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
357
|
+
import { logger as logger5, getGeneratedDir } from "@elizaos/core";
|
|
358
|
+
function isBrowser() {
|
|
359
|
+
return typeof globalThis !== "undefined" && globalThis.document;
|
|
360
|
+
}
|
|
361
|
+
function sanitizeId(id) {
|
|
362
|
+
const src = (id ?? "").toString();
|
|
363
|
+
const normalized = src.normalize("NFKC");
|
|
364
|
+
let safe = normalized.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
365
|
+
safe = safe.replace(/_+/g, "_");
|
|
366
|
+
safe = safe.slice(0, 64);
|
|
367
|
+
safe = safe.replace(/^_+|_+$/g, "");
|
|
368
|
+
return safe || "agent";
|
|
369
|
+
}
|
|
370
|
+
function base64ToBytes(base64) {
|
|
371
|
+
const cleaned = base64.replace(/\s+/g, "");
|
|
372
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
373
|
+
const lookup = new Array(256).fill(-1);
|
|
374
|
+
for (let i = 0;i < chars.length; i++)
|
|
375
|
+
lookup[chars.charCodeAt(i)] = i;
|
|
376
|
+
const len = cleaned.length;
|
|
377
|
+
let pad = 0;
|
|
378
|
+
if (len >= 2 && cleaned[len - 1] === "=")
|
|
379
|
+
pad++;
|
|
380
|
+
if (len >= 2 && cleaned[len - 2] === "=")
|
|
381
|
+
pad++;
|
|
382
|
+
const outLen = (len * 3 >> 2) - pad;
|
|
383
|
+
const out = new Uint8Array(outLen);
|
|
384
|
+
let o = 0;
|
|
385
|
+
for (let i = 0;i < len; i += 4) {
|
|
386
|
+
const c0 = lookup[cleaned.charCodeAt(i)];
|
|
387
|
+
const c1 = lookup[cleaned.charCodeAt(i + 1)];
|
|
388
|
+
const c2 = lookup[cleaned.charCodeAt(i + 2)];
|
|
389
|
+
const c3 = lookup[cleaned.charCodeAt(i + 3)];
|
|
390
|
+
const n = c0 << 18 | c1 << 12 | (c2 & 63) << 6 | c3 & 63;
|
|
391
|
+
if (o < outLen)
|
|
392
|
+
out[o++] = n >> 16 & 255;
|
|
393
|
+
if (o < outLen)
|
|
394
|
+
out[o++] = n >> 8 & 255;
|
|
395
|
+
if (o < outLen)
|
|
396
|
+
out[o++] = n & 255;
|
|
397
|
+
}
|
|
398
|
+
return out;
|
|
399
|
+
}
|
|
354
400
|
async function saveBase64Image(base64Url, agentId, index = 0) {
|
|
401
|
+
if (isBrowser()) {
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
355
404
|
const m = base64Url.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)$/);
|
|
356
|
-
if (!m)
|
|
405
|
+
if (!m)
|
|
406
|
+
return null;
|
|
357
407
|
const mime = m[1];
|
|
358
408
|
const base64Data = m[2];
|
|
359
409
|
const extMap = {
|
|
@@ -366,27 +416,41 @@ async function saveBase64Image(base64Url, agentId, index = 0) {
|
|
|
366
416
|
"image/tiff": "tiff"
|
|
367
417
|
};
|
|
368
418
|
const extension = extMap[mime];
|
|
369
|
-
if (!extension)
|
|
370
|
-
|
|
419
|
+
if (!extension)
|
|
420
|
+
return null;
|
|
421
|
+
const { join } = await import("node:path");
|
|
422
|
+
const safeAgentId = sanitizeId(agentId);
|
|
423
|
+
const baseDir = join(getGeneratedDir(), safeAgentId);
|
|
424
|
+
const { existsSync } = await import("node:fs");
|
|
371
425
|
if (!existsSync(baseDir)) {
|
|
426
|
+
const { mkdir } = await import("node:fs/promises");
|
|
372
427
|
await mkdir(baseDir, { recursive: true });
|
|
373
428
|
}
|
|
374
429
|
const timestamp = Date.now();
|
|
375
430
|
const filename = `image_${timestamp}_${index}.${extension}`;
|
|
376
431
|
const filepath = join(baseDir, filename);
|
|
377
|
-
const buffer =
|
|
432
|
+
const buffer = base64ToBytes(base64Data);
|
|
433
|
+
const { writeFile } = await import("node:fs/promises");
|
|
378
434
|
await writeFile(filepath, buffer);
|
|
379
|
-
|
|
435
|
+
logger5.info(`[OpenRouter] Saved generated image to ${filepath}`);
|
|
380
436
|
return filepath;
|
|
381
437
|
}
|
|
382
438
|
function deleteImage(filepath) {
|
|
439
|
+
if (isBrowser()) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
383
442
|
try {
|
|
384
|
-
|
|
385
|
-
unlinkSync(
|
|
386
|
-
|
|
387
|
-
|
|
443
|
+
(async () => {
|
|
444
|
+
const { existsSync, unlinkSync } = await import("node:fs");
|
|
445
|
+
if (existsSync(filepath)) {
|
|
446
|
+
unlinkSync(filepath);
|
|
447
|
+
logger5.debug(`[OpenRouter] Deleted image: ${filepath}`);
|
|
448
|
+
}
|
|
449
|
+
})().catch((error) => {
|
|
450
|
+
logger5.warn(`[OpenRouter] Failed to delete image ${filepath}:`, String(error));
|
|
451
|
+
});
|
|
388
452
|
} catch (error) {
|
|
389
|
-
|
|
453
|
+
logger5.warn(`[OpenRouter] Failed to delete image ${filepath}:`, String(error));
|
|
390
454
|
}
|
|
391
455
|
}
|
|
392
456
|
|
|
@@ -395,8 +459,8 @@ async function handleImageDescription(runtime, params) {
|
|
|
395
459
|
let imageUrl;
|
|
396
460
|
let promptText;
|
|
397
461
|
const modelName = getImageModel(runtime);
|
|
398
|
-
|
|
399
|
-
const
|
|
462
|
+
logger6.log(`[OpenRouter] Using IMAGE_DESCRIPTION model: ${modelName}`);
|
|
463
|
+
const maxOutputTokens = 300;
|
|
400
464
|
if (typeof params === "string") {
|
|
401
465
|
imageUrl = params;
|
|
402
466
|
promptText = "Please analyze this image and provide a title and detailed description.";
|
|
@@ -419,12 +483,12 @@ async function handleImageDescription(runtime, params) {
|
|
|
419
483
|
const { text: responseText } = await generateText2({
|
|
420
484
|
model,
|
|
421
485
|
messages,
|
|
422
|
-
|
|
486
|
+
maxOutputTokens
|
|
423
487
|
});
|
|
424
488
|
return parseImageDescriptionResponse(responseText);
|
|
425
489
|
} catch (error) {
|
|
426
490
|
const message = error instanceof Error ? error.message : String(error);
|
|
427
|
-
|
|
491
|
+
logger6.error(`Error analyzing image: ${message}`);
|
|
428
492
|
return {
|
|
429
493
|
title: "Failed to analyze image",
|
|
430
494
|
description: `Error: ${message}`
|
|
@@ -433,18 +497,15 @@ async function handleImageDescription(runtime, params) {
|
|
|
433
497
|
}
|
|
434
498
|
async function handleImageGeneration(runtime, params) {
|
|
435
499
|
const modelName = getImageGenerationModel(runtime);
|
|
436
|
-
|
|
500
|
+
logger6.log(`[OpenRouter] Using IMAGE_GENERATION model: ${modelName}`);
|
|
437
501
|
const apiKey = getApiKey(runtime);
|
|
438
502
|
try {
|
|
439
|
-
if (!apiKey) {
|
|
440
|
-
logger7.error("[OpenRouter] OpenRouter API key is missing");
|
|
441
|
-
return [];
|
|
442
|
-
}
|
|
443
503
|
const baseUrl = getBaseURL(runtime);
|
|
504
|
+
const isBrowser2 = typeof globalThis !== "undefined" && globalThis.document;
|
|
444
505
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
445
506
|
method: "POST",
|
|
446
507
|
headers: {
|
|
447
|
-
Authorization: `Bearer ${apiKey}
|
|
508
|
+
...isBrowser2 ? {} : { Authorization: `Bearer ${apiKey}` },
|
|
448
509
|
"Content-Type": "application/json"
|
|
449
510
|
},
|
|
450
511
|
body: JSON.stringify({
|
|
@@ -457,8 +518,7 @@ async function handleImageGeneration(runtime, params) {
|
|
|
457
518
|
],
|
|
458
519
|
modalities: ["image", "text"]
|
|
459
520
|
}),
|
|
460
|
-
|
|
461
|
-
signal: AbortSignal.timeout ? AbortSignal.timeout(6e4) : void 0
|
|
521
|
+
signal: AbortSignal.timeout ? AbortSignal.timeout(60000) : undefined
|
|
462
522
|
});
|
|
463
523
|
if (!response.ok) {
|
|
464
524
|
const errorText = await response.text().catch(() => "");
|
|
@@ -472,16 +532,15 @@ async function handleImageGeneration(runtime, params) {
|
|
|
472
532
|
const base64Url = image.image_url.url;
|
|
473
533
|
const filepath = await saveBase64Image(base64Url, runtime.agentId, index);
|
|
474
534
|
if (filepath) {
|
|
475
|
-
|
|
535
|
+
logger6.log(`[OpenRouter] Returning image with filepath: ${filepath}`);
|
|
476
536
|
images.push({
|
|
477
537
|
url: filepath
|
|
478
|
-
// Use actual file path
|
|
479
538
|
});
|
|
480
539
|
savedPaths.push(filepath);
|
|
481
540
|
} else if (!base64Url.startsWith("data:")) {
|
|
482
541
|
images.push({ url: base64Url });
|
|
483
542
|
} else {
|
|
484
|
-
|
|
543
|
+
logger6.warn(`[OpenRouter] Failed to save image ${index + 1}, skipping`);
|
|
485
544
|
}
|
|
486
545
|
}
|
|
487
546
|
}
|
|
@@ -490,16 +549,16 @@ async function handleImageGeneration(runtime, params) {
|
|
|
490
549
|
savedPaths.forEach((path) => {
|
|
491
550
|
deleteImage(path);
|
|
492
551
|
});
|
|
493
|
-
},
|
|
552
|
+
}, 30000);
|
|
494
553
|
}
|
|
495
554
|
if (images.length === 0) {
|
|
496
555
|
throw new Error("No images generated in response");
|
|
497
556
|
}
|
|
498
|
-
|
|
557
|
+
logger6.log(`[OpenRouter] Generated ${images.length} image(s)`);
|
|
499
558
|
return images;
|
|
500
559
|
} catch (error) {
|
|
501
560
|
const message = error instanceof Error ? error.message : String(error);
|
|
502
|
-
|
|
561
|
+
logger6.error(`[OpenRouter] Error generating image: ${message}`);
|
|
503
562
|
return [];
|
|
504
563
|
}
|
|
505
564
|
}
|
|
@@ -545,9 +604,10 @@ var openrouterPlugin = {
|
|
|
545
604
|
}
|
|
546
605
|
}
|
|
547
606
|
};
|
|
548
|
-
var
|
|
607
|
+
var src_default = openrouterPlugin;
|
|
549
608
|
export {
|
|
550
|
-
|
|
551
|
-
|
|
609
|
+
openrouterPlugin,
|
|
610
|
+
src_default as default
|
|
552
611
|
};
|
|
553
|
-
|
|
612
|
+
|
|
613
|
+
//# debugId=E1E5C3C96B97EF5164756E2164756E21
|