@elizaos/plugin-openrouter 1.5.15 → 2.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.browser.js +389 -929
- package/dist/browser/index.browser.js.map +13 -15
- package/dist/cjs/index.node.cjs +391 -566
- package/dist/cjs/index.node.cjs.map +19 -0
- package/dist/index.d.ts +4 -6
- package/dist/node/index.node.js +389 -574
- package/dist/node/index.node.js.map +13 -14
- package/package.json +67 -22
- package/LICENSE +0 -21
- package/README.md +0 -109
- package/dist/cjs/index.node.js.map +0 -20
- package/dist/index.browser.d.ts +0 -2
- package/dist/index.node.d.ts +0 -2
- package/dist/init.d.ts +0 -6
- package/dist/models/embedding.d.ts +0 -5
- package/dist/models/image.d.ts +0 -14
- package/dist/models/index.d.ts +0 -4
- package/dist/models/object.d.ts +0 -9
- package/dist/models/text.d.ts +0 -17
- package/dist/providers/index.d.ts +0 -1
- package/dist/providers/openrouter.d.ts +0 -8
- package/dist/types/index.d.ts +0 -74
- package/dist/utils/config.d.ts +0 -71
- package/dist/utils/events.d.ts +0 -6
- package/dist/utils/helpers.d.ts +0 -29
- package/dist/utils/image-storage.d.ts +0 -8
- package/dist/utils/index.d.ts +0 -2
package/dist/node/index.node.js
CHANGED
|
@@ -1,75 +1,70 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
20
|
-
// src/index.ts
|
|
1
|
+
// plugin.ts
|
|
21
2
|
import {
|
|
22
|
-
|
|
3
|
+
logger as logger6,
|
|
4
|
+
ModelType as ModelType5
|
|
23
5
|
} from "@elizaos/core";
|
|
24
6
|
|
|
25
|
-
//
|
|
7
|
+
// init.ts
|
|
26
8
|
import { logger } from "@elizaos/core";
|
|
27
|
-
import { fetch as fetch2 } from "undici";
|
|
28
9
|
|
|
29
|
-
//
|
|
10
|
+
// utils/config.ts
|
|
11
|
+
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
12
|
+
var DEFAULT_SMALL_MODEL = "google/gemini-2.0-flash-001";
|
|
13
|
+
var DEFAULT_LARGE_MODEL = "google/gemini-2.5-flash";
|
|
14
|
+
var DEFAULT_IMAGE_MODEL = "x-ai/grok-2-vision-1212";
|
|
15
|
+
var DEFAULT_IMAGE_GENERATION_MODEL = "google/gemini-2.5-flash-image-preview";
|
|
16
|
+
var DEFAULT_EMBEDDING_MODEL = "openai/text-embedding-3-small";
|
|
17
|
+
var DEFAULT_EMBEDDING_DIMENSIONS = 1536;
|
|
18
|
+
function getEnvValue(key) {
|
|
19
|
+
if (typeof process === "undefined" || !process.env) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const value = process.env[key];
|
|
23
|
+
return value === undefined ? undefined : String(value);
|
|
24
|
+
}
|
|
30
25
|
function getSetting(runtime, key, defaultValue) {
|
|
31
|
-
|
|
26
|
+
const value = runtime.getSetting(key);
|
|
27
|
+
if (value !== undefined && value !== null) {
|
|
28
|
+
return String(value);
|
|
29
|
+
}
|
|
30
|
+
return getEnvValue(key) ?? defaultValue;
|
|
32
31
|
}
|
|
33
32
|
function getBaseURL(runtime) {
|
|
34
33
|
const browserURL = getSetting(runtime, "OPENROUTER_BROWSER_BASE_URL");
|
|
35
34
|
if (typeof globalThis !== "undefined" && globalThis.document && browserURL) {
|
|
36
35
|
return browserURL;
|
|
37
36
|
}
|
|
38
|
-
return getSetting(runtime, "OPENROUTER_BASE_URL",
|
|
37
|
+
return getSetting(runtime, "OPENROUTER_BASE_URL", DEFAULT_BASE_URL) || DEFAULT_BASE_URL;
|
|
39
38
|
}
|
|
40
39
|
function getApiKey(runtime) {
|
|
41
40
|
return getSetting(runtime, "OPENROUTER_API_KEY");
|
|
42
41
|
}
|
|
43
42
|
function getSmallModel(runtime) {
|
|
44
|
-
return getSetting(runtime, "OPENROUTER_SMALL_MODEL") ?? getSetting(runtime, "SMALL_MODEL",
|
|
43
|
+
return getSetting(runtime, "OPENROUTER_SMALL_MODEL") ?? getSetting(runtime, "SMALL_MODEL", DEFAULT_SMALL_MODEL) ?? DEFAULT_SMALL_MODEL;
|
|
45
44
|
}
|
|
46
45
|
function getLargeModel(runtime) {
|
|
47
|
-
return getSetting(runtime, "OPENROUTER_LARGE_MODEL") ?? getSetting(runtime, "LARGE_MODEL",
|
|
46
|
+
return getSetting(runtime, "OPENROUTER_LARGE_MODEL") ?? getSetting(runtime, "LARGE_MODEL", DEFAULT_LARGE_MODEL) ?? DEFAULT_LARGE_MODEL;
|
|
48
47
|
}
|
|
49
48
|
function getImageModel(runtime) {
|
|
50
|
-
return getSetting(runtime, "OPENROUTER_IMAGE_MODEL") ?? getSetting(runtime, "IMAGE_MODEL",
|
|
49
|
+
return getSetting(runtime, "OPENROUTER_IMAGE_MODEL") ?? getSetting(runtime, "IMAGE_MODEL", DEFAULT_IMAGE_MODEL) ?? DEFAULT_IMAGE_MODEL;
|
|
51
50
|
}
|
|
52
51
|
function getImageGenerationModel(runtime) {
|
|
53
|
-
return getSetting(runtime, "OPENROUTER_IMAGE_GENERATION_MODEL") ?? getSetting(runtime, "IMAGE_GENERATION_MODEL",
|
|
52
|
+
return getSetting(runtime, "OPENROUTER_IMAGE_GENERATION_MODEL") ?? getSetting(runtime, "IMAGE_GENERATION_MODEL", DEFAULT_IMAGE_GENERATION_MODEL) ?? DEFAULT_IMAGE_GENERATION_MODEL;
|
|
54
53
|
}
|
|
55
54
|
function getEmbeddingModel(runtime) {
|
|
56
|
-
return getSetting(runtime, "OPENROUTER_EMBEDDING_MODEL") ?? getSetting(runtime, "EMBEDDING_MODEL",
|
|
55
|
+
return getSetting(runtime, "OPENROUTER_EMBEDDING_MODEL") ?? getSetting(runtime, "EMBEDDING_MODEL", DEFAULT_EMBEDDING_MODEL) ?? DEFAULT_EMBEDDING_MODEL;
|
|
56
|
+
}
|
|
57
|
+
function getEmbeddingDimensions(runtime) {
|
|
58
|
+
const setting = getSetting(runtime, "OPENROUTER_EMBEDDING_DIMENSIONS") ?? getSetting(runtime, "EMBEDDING_DIMENSIONS");
|
|
59
|
+
return setting ? parseInt(setting, 10) : DEFAULT_EMBEDDING_DIMENSIONS;
|
|
57
60
|
}
|
|
58
61
|
function shouldAutoCleanupImages(runtime) {
|
|
59
62
|
const setting = getSetting(runtime, "OPENROUTER_AUTO_CLEANUP_IMAGES", "false");
|
|
60
63
|
return setting?.toLowerCase() === "true";
|
|
61
64
|
}
|
|
62
|
-
function getToolExecutionMaxSteps(runtime) {
|
|
63
|
-
const setting = getSetting(runtime, "OPENROUTER_TOOL_EXECUTION_MAX_STEPS", "15");
|
|
64
|
-
const value = parseInt(setting || "15", 10);
|
|
65
|
-
if (Number.isNaN(value) || value < 1)
|
|
66
|
-
return 15;
|
|
67
|
-
if (value > 100)
|
|
68
|
-
return 100;
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
65
|
|
|
72
|
-
//
|
|
66
|
+
// init.ts
|
|
67
|
+
globalThis.AI_SDK_LOG_WARNINGS ??= false;
|
|
73
68
|
function initializeOpenRouter(_config, runtime) {
|
|
74
69
|
(async () => {
|
|
75
70
|
try {
|
|
@@ -81,519 +76,55 @@ function initializeOpenRouter(_config, runtime) {
|
|
|
81
76
|
logger.warn("OPENROUTER_API_KEY is not set in environment - OpenRouter functionality will be limited");
|
|
82
77
|
return;
|
|
83
78
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
} else {
|
|
93
|
-
logger.log("OpenRouter API key validated successfully");
|
|
94
|
-
}
|
|
95
|
-
} catch (fetchError) {
|
|
96
|
-
const message = fetchError instanceof Error ? fetchError.message : String(fetchError);
|
|
97
|
-
logger.warn(`Error validating OpenRouter API key: ${message}`);
|
|
98
|
-
logger.warn("OpenRouter functionality will be limited until a valid API key is provided");
|
|
79
|
+
const baseURL = getBaseURL(runtime);
|
|
80
|
+
const response = await fetch(`${baseURL}/models`, {
|
|
81
|
+
headers: { Authorization: `Bearer ${getApiKey(runtime)}` }
|
|
82
|
+
});
|
|
83
|
+
if (!response.ok) {
|
|
84
|
+
logger.warn(`OpenRouter API key validation failed: ${response.statusText}`);
|
|
85
|
+
} else {
|
|
86
|
+
logger.log("OpenRouter API key validated successfully");
|
|
99
87
|
}
|
|
100
88
|
} catch (error) {
|
|
101
|
-
const message = error
|
|
102
|
-
logger.warn(`OpenRouter
|
|
89
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
logger.warn(`Error validating OpenRouter API key: ${message}`);
|
|
103
91
|
}
|
|
104
92
|
})();
|
|
105
|
-
return;
|
|
106
93
|
}
|
|
107
94
|
|
|
108
|
-
//
|
|
109
|
-
import { logger as logger3, ModelType } from "@elizaos/core";
|
|
110
|
-
import { generateText, stepCountIs } from "ai";
|
|
95
|
+
// models/embedding.ts
|
|
96
|
+
import { logger as logger3, ModelType, VECTOR_DIMS } from "@elizaos/core";
|
|
111
97
|
|
|
112
|
-
//
|
|
113
|
-
import {
|
|
114
|
-
function
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
// src/utils/events.ts
|
|
124
|
-
import {
|
|
125
|
-
EventType
|
|
126
|
-
} from "@elizaos/core";
|
|
127
|
-
function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
128
|
-
const truncatedPrompt = typeof prompt === "string" ? prompt.length > 200 ? `${prompt.slice(0, 200)}…` : prompt : "";
|
|
129
|
-
const inputTokens = Number(usage.inputTokens || 0);
|
|
130
|
-
const outputTokens = Number(usage.outputTokens || 0);
|
|
131
|
-
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
132
|
-
runtime.emitEvent(EventType.MODEL_USED, {
|
|
98
|
+
// utils/events.ts
|
|
99
|
+
import { logger as logger2 } from "@elizaos/core";
|
|
100
|
+
function emitModelUsageEvent(_runtime, modelType, prompt, usage) {
|
|
101
|
+
const inputTokens = usage.inputTokens ?? usage.promptTokens ?? 0;
|
|
102
|
+
const outputTokens = usage.outputTokens ?? usage.completionTokens ?? 0;
|
|
103
|
+
const totalTokens = usage.totalTokens ?? inputTokens + outputTokens;
|
|
104
|
+
logger2.debug({
|
|
105
|
+
event: "model:usage",
|
|
106
|
+
modelType,
|
|
133
107
|
provider: "openrouter",
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
108
|
+
prompt: prompt.substring(0, 100),
|
|
109
|
+
usage: {
|
|
110
|
+
promptTokens: inputTokens,
|
|
111
|
+
completionTokens: outputTokens,
|
|
112
|
+
totalTokens
|
|
113
|
+
},
|
|
114
|
+
timestamp: Date.now()
|
|
141
115
|
});
|
|
142
116
|
}
|
|
143
117
|
|
|
144
|
-
//
|
|
145
|
-
import { logger as logger2 } from "@elizaos/core";
|
|
146
|
-
import { JSONParseError } from "ai";
|
|
147
|
-
function getJsonRepairFunction() {
|
|
148
|
-
return async ({ text, error }) => {
|
|
149
|
-
try {
|
|
150
|
-
if (error instanceof JSONParseError) {
|
|
151
|
-
const cleanedText = text.replace(/```json\n|\n```|```/g, "");
|
|
152
|
-
JSON.parse(cleanedText);
|
|
153
|
-
return cleanedText;
|
|
154
|
-
}
|
|
155
|
-
return null;
|
|
156
|
-
} catch (jsonError) {
|
|
157
|
-
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
158
|
-
logger2.warn(`Failed to repair JSON text: ${message}`);
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
function handleEmptyToolResponse(modelType) {
|
|
164
|
-
logger2.warn(`[${modelType}] No text generated after tool execution`);
|
|
165
|
-
const fallbackText = "I executed the requested action. The tool completed successfully.";
|
|
166
|
-
logger2.warn(`[${modelType}] Using fallback response text`);
|
|
167
|
-
return fallbackText;
|
|
168
|
-
}
|
|
169
|
-
function parseImageDescriptionResponse(responseText) {
|
|
170
|
-
try {
|
|
171
|
-
const jsonResponse = JSON.parse(responseText);
|
|
172
|
-
if (jsonResponse.title && jsonResponse.description) {
|
|
173
|
-
return jsonResponse;
|
|
174
|
-
}
|
|
175
|
-
} catch (e) {
|
|
176
|
-
logger2.debug(`Parsing as JSON failed, processing as text: ${e}`);
|
|
177
|
-
}
|
|
178
|
-
const titleMatch = responseText.match(/title[:\s]+(.+?)(?:\n|$)/i);
|
|
179
|
-
const title = titleMatch?.[1]?.trim() || "Image Analysis";
|
|
180
|
-
const description = responseText.replace(/title[:\s]+(.+?)(?:\n|$)/i, "").trim();
|
|
181
|
-
return { title, description };
|
|
182
|
-
}
|
|
183
|
-
async function handleObjectGenerationError(error) {
|
|
184
|
-
if (error instanceof JSONParseError) {
|
|
185
|
-
logger2.error(`[generateObject] Failed to parse JSON: ${error.message}`);
|
|
186
|
-
const repairFunction = getJsonRepairFunction();
|
|
187
|
-
const repairedJsonString = await repairFunction({
|
|
188
|
-
text: error.text,
|
|
189
|
-
error
|
|
190
|
-
});
|
|
191
|
-
if (repairedJsonString) {
|
|
192
|
-
try {
|
|
193
|
-
const repairedObject = JSON.parse(repairedJsonString);
|
|
194
|
-
logger2.log("[generateObject] Successfully repaired JSON.");
|
|
195
|
-
return repairedObject;
|
|
196
|
-
} catch (repairParseError) {
|
|
197
|
-
const message = repairParseError instanceof Error ? repairParseError.message : String(repairParseError);
|
|
198
|
-
logger2.error(`[generateObject] Failed to parse repaired JSON: ${message}`);
|
|
199
|
-
if (repairParseError instanceof Error)
|
|
200
|
-
throw repairParseError;
|
|
201
|
-
throw Object.assign(new Error(message), { cause: repairParseError });
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
logger2.error("[generateObject] JSON repair failed.");
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
|
-
} else {
|
|
208
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
209
|
-
logger2.error(`[generateObject] Unknown error: ${message}`);
|
|
210
|
-
if (error instanceof Error)
|
|
211
|
-
throw error;
|
|
212
|
-
throw Object.assign(new Error(message), { cause: error });
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function isLikelyBase64(key, value) {
|
|
216
|
-
const base64KeyPattern = /^(data|content|body|payload|encoded|b64|base64|document)$/i;
|
|
217
|
-
if (!base64KeyPattern.test(key))
|
|
218
|
-
return false;
|
|
219
|
-
if (value.length < 20 || value.length > 1024 * 1024)
|
|
220
|
-
return false;
|
|
221
|
-
if (value.length % 4 !== 0)
|
|
222
|
-
return false;
|
|
223
|
-
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
224
|
-
return false;
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
function decodeBase64Fields(obj, depth = 0) {
|
|
228
|
-
if (depth > 5)
|
|
229
|
-
return obj;
|
|
230
|
-
if (!obj || typeof obj !== "object")
|
|
231
|
-
return obj;
|
|
232
|
-
if (Array.isArray(obj))
|
|
233
|
-
return obj.map((item) => decodeBase64Fields(item, depth + 1));
|
|
234
|
-
const decoded = {};
|
|
235
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
236
|
-
if (typeof value === "string" && isLikelyBase64(key, value)) {
|
|
237
|
-
try {
|
|
238
|
-
decoded[key] = Buffer.from(value, "base64").toString("utf8");
|
|
239
|
-
logger2.debug(`[decodeBase64] Decoded field '${key}' (${value.length} chars)`);
|
|
240
|
-
} catch (error) {
|
|
241
|
-
logger2.warn(`[decodeBase64] Failed to decode field '${key}': ${error}`);
|
|
242
|
-
decoded[key] = value;
|
|
243
|
-
}
|
|
244
|
-
} else if (value && typeof value === "object") {
|
|
245
|
-
decoded[key] = decodeBase64Fields(value, depth + 1);
|
|
246
|
-
} else {
|
|
247
|
-
decoded[key] = value;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return decoded;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/models/text.ts
|
|
254
|
-
async function generateTextWithModel(runtime, modelType, params) {
|
|
255
|
-
const { prompt, stopSequences = [], tools, toolChoice } = params;
|
|
256
|
-
const temperature = params.temperature ?? 0.7;
|
|
257
|
-
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
258
|
-
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
259
|
-
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
260
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
261
|
-
const modelName = modelType === ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
262
|
-
const modelLabel = modelType === ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
263
|
-
logger3.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
264
|
-
const generateParams = {
|
|
265
|
-
model: openrouter.chat(modelName),
|
|
266
|
-
prompt,
|
|
267
|
-
system: runtime.character.system ?? undefined,
|
|
268
|
-
temperature,
|
|
269
|
-
frequencyPenalty,
|
|
270
|
-
presencePenalty,
|
|
271
|
-
stopSequences
|
|
272
|
-
};
|
|
273
|
-
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
274
|
-
if (tools) {
|
|
275
|
-
generateParams.tools = tools;
|
|
276
|
-
const maxSteps = getToolExecutionMaxSteps(runtime);
|
|
277
|
-
generateParams.stopWhen = stepCountIs(maxSteps);
|
|
278
|
-
logger3.debug(`[OpenRouter] Using maxSteps: ${maxSteps} for tool execution`);
|
|
279
|
-
}
|
|
280
|
-
if (toolChoice) {
|
|
281
|
-
generateParams.toolChoice = toolChoice;
|
|
282
|
-
}
|
|
283
|
-
let capturedToolResults = [];
|
|
284
|
-
let capturedToolCalls = [];
|
|
285
|
-
if (tools) {
|
|
286
|
-
generateParams.onStepFinish = async (stepResult) => {
|
|
287
|
-
if (stepResult.toolCalls && stepResult.toolCalls.length > 0) {
|
|
288
|
-
capturedToolCalls = [
|
|
289
|
-
...capturedToolCalls,
|
|
290
|
-
...stepResult.toolCalls
|
|
291
|
-
];
|
|
292
|
-
}
|
|
293
|
-
if (stepResult.content && Array.isArray(stepResult.content)) {
|
|
294
|
-
const toolResultsFromContent = stepResult.content.filter((content) => content.type === "tool-result" && content.output).map((content) => ({
|
|
295
|
-
toolCallId: content.toolCallId,
|
|
296
|
-
result: decodeBase64Fields(content.output)
|
|
297
|
-
}));
|
|
298
|
-
if (toolResultsFromContent.length > 0) {
|
|
299
|
-
capturedToolResults = [...capturedToolResults, ...toolResultsFromContent];
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
const response = await generateText(generateParams);
|
|
305
|
-
let responseText;
|
|
306
|
-
if (tools && (!response.text || response.text.trim() === "" || response.text === "Tools executed successfully.")) {
|
|
307
|
-
responseText = handleEmptyToolResponse(modelLabel);
|
|
308
|
-
} else {
|
|
309
|
-
responseText = response.text;
|
|
310
|
-
}
|
|
311
|
-
if (response.usage) {
|
|
312
|
-
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
313
|
-
}
|
|
314
|
-
if (tools && response.steps && response.steps.length > 0) {
|
|
315
|
-
return {
|
|
316
|
-
text: responseText,
|
|
317
|
-
toolCalls: capturedToolCalls,
|
|
318
|
-
toolResults: capturedToolResults,
|
|
319
|
-
steps: response.steps,
|
|
320
|
-
usage: response.usage,
|
|
321
|
-
finishReason: response.finishReason
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
return responseText;
|
|
325
|
-
}
|
|
326
|
-
async function handleTextSmall(runtime, params) {
|
|
327
|
-
return generateTextWithModel(runtime, ModelType.TEXT_SMALL, params);
|
|
328
|
-
}
|
|
329
|
-
async function handleTextLarge(runtime, params) {
|
|
330
|
-
return generateTextWithModel(runtime, ModelType.TEXT_LARGE, params);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// src/models/object.ts
|
|
334
|
-
import {
|
|
335
|
-
ModelType as ModelType2,
|
|
336
|
-
logger as logger4
|
|
337
|
-
} from "@elizaos/core";
|
|
338
|
-
import { generateObject } from "ai";
|
|
339
|
-
async function generateObjectWithModel(runtime, modelType, params) {
|
|
340
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
341
|
-
const modelName = modelType === ModelType2.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
342
|
-
const modelLabel = modelType === ModelType2.OBJECT_SMALL ? "OBJECT_SMALL" : "OBJECT_LARGE";
|
|
343
|
-
logger4.log(`[OpenRouter] Using ${modelLabel} model: ${modelName}`);
|
|
344
|
-
const temperature = params.temperature ?? 0.7;
|
|
345
|
-
try {
|
|
346
|
-
const { object, usage } = await generateObject({
|
|
347
|
-
model: openrouter.chat(modelName),
|
|
348
|
-
...params.schema && { schema: params.schema },
|
|
349
|
-
output: params.schema ? "object" : "no-schema",
|
|
350
|
-
prompt: params.prompt,
|
|
351
|
-
temperature,
|
|
352
|
-
experimental_repairText: getJsonRepairFunction()
|
|
353
|
-
});
|
|
354
|
-
if (usage) {
|
|
355
|
-
emitModelUsageEvent(runtime, modelType, params.prompt, usage);
|
|
356
|
-
}
|
|
357
|
-
return object;
|
|
358
|
-
} catch (error) {
|
|
359
|
-
return handleObjectGenerationError(error);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
async function handleObjectSmall(runtime, params) {
|
|
363
|
-
return generateObjectWithModel(runtime, ModelType2.OBJECT_SMALL, params);
|
|
364
|
-
}
|
|
365
|
-
async function handleObjectLarge(runtime, params) {
|
|
366
|
-
return generateObjectWithModel(runtime, ModelType2.OBJECT_LARGE, params);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// src/models/image.ts
|
|
370
|
-
import {
|
|
371
|
-
logger as logger6
|
|
372
|
-
} from "@elizaos/core";
|
|
373
|
-
import { generateText as generateText2 } from "ai";
|
|
374
|
-
|
|
375
|
-
// src/utils/image-storage.ts
|
|
376
|
-
import { logger as logger5, getGeneratedDir } from "@elizaos/core";
|
|
377
|
-
function isBrowser() {
|
|
378
|
-
return typeof globalThis !== "undefined" && globalThis.document;
|
|
379
|
-
}
|
|
380
|
-
function sanitizeId(id) {
|
|
381
|
-
const src = (id ?? "").toString();
|
|
382
|
-
const normalized = src.normalize("NFKC");
|
|
383
|
-
let safe = normalized.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
384
|
-
safe = safe.replace(/_+/g, "_");
|
|
385
|
-
safe = safe.slice(0, 64);
|
|
386
|
-
safe = safe.replace(/^_+|_+$/g, "");
|
|
387
|
-
return safe || "agent";
|
|
388
|
-
}
|
|
389
|
-
function base64ToBytes(base64) {
|
|
390
|
-
const cleaned = base64.replace(/\s+/g, "");
|
|
391
|
-
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
392
|
-
const lookup = new Array(256).fill(-1);
|
|
393
|
-
for (let i = 0;i < chars.length; i++)
|
|
394
|
-
lookup[chars.charCodeAt(i)] = i;
|
|
395
|
-
const len = cleaned.length;
|
|
396
|
-
let pad = 0;
|
|
397
|
-
if (len >= 2 && cleaned[len - 1] === "=")
|
|
398
|
-
pad++;
|
|
399
|
-
if (len >= 2 && cleaned[len - 2] === "=")
|
|
400
|
-
pad++;
|
|
401
|
-
const outLen = (len * 3 >> 2) - pad;
|
|
402
|
-
const out = new Uint8Array(outLen);
|
|
403
|
-
let o = 0;
|
|
404
|
-
for (let i = 0;i < len; i += 4) {
|
|
405
|
-
const c0 = lookup[cleaned.charCodeAt(i)];
|
|
406
|
-
const c1 = lookup[cleaned.charCodeAt(i + 1)];
|
|
407
|
-
const c2 = lookup[cleaned.charCodeAt(i + 2)];
|
|
408
|
-
const c3 = lookup[cleaned.charCodeAt(i + 3)];
|
|
409
|
-
const n = c0 << 18 | c1 << 12 | (c2 & 63) << 6 | c3 & 63;
|
|
410
|
-
if (o < outLen)
|
|
411
|
-
out[o++] = n >> 16 & 255;
|
|
412
|
-
if (o < outLen)
|
|
413
|
-
out[o++] = n >> 8 & 255;
|
|
414
|
-
if (o < outLen)
|
|
415
|
-
out[o++] = n & 255;
|
|
416
|
-
}
|
|
417
|
-
return out;
|
|
418
|
-
}
|
|
419
|
-
async function saveBase64Image(base64Url, agentId, index = 0) {
|
|
420
|
-
if (isBrowser()) {
|
|
421
|
-
return null;
|
|
422
|
-
}
|
|
423
|
-
const m = base64Url.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=]+)$/);
|
|
424
|
-
if (!m)
|
|
425
|
-
return null;
|
|
426
|
-
const mime = m[1];
|
|
427
|
-
const base64Data = m[2];
|
|
428
|
-
const extMap = {
|
|
429
|
-
"image/png": "png",
|
|
430
|
-
"image/jpeg": "jpg",
|
|
431
|
-
"image/jpg": "jpg",
|
|
432
|
-
"image/webp": "webp",
|
|
433
|
-
"image/gif": "gif",
|
|
434
|
-
"image/bmp": "bmp",
|
|
435
|
-
"image/tiff": "tiff"
|
|
436
|
-
};
|
|
437
|
-
const extension = extMap[mime];
|
|
438
|
-
if (!extension)
|
|
439
|
-
return null;
|
|
440
|
-
const { join } = await import("node:path");
|
|
441
|
-
const safeAgentId = sanitizeId(agentId);
|
|
442
|
-
const baseDir = join(getGeneratedDir(), safeAgentId);
|
|
443
|
-
const { existsSync } = await import("node:fs");
|
|
444
|
-
if (!existsSync(baseDir)) {
|
|
445
|
-
const { mkdir } = await import("node:fs/promises");
|
|
446
|
-
await mkdir(baseDir, { recursive: true });
|
|
447
|
-
}
|
|
448
|
-
const timestamp = Date.now();
|
|
449
|
-
const filename = `image_${timestamp}_${index}.${extension}`;
|
|
450
|
-
const filepath = join(baseDir, filename);
|
|
451
|
-
const buffer = base64ToBytes(base64Data);
|
|
452
|
-
const { writeFile } = await import("node:fs/promises");
|
|
453
|
-
await writeFile(filepath, buffer);
|
|
454
|
-
logger5.info(`[OpenRouter] Saved generated image to ${filepath}`);
|
|
455
|
-
return filepath;
|
|
456
|
-
}
|
|
457
|
-
function deleteImage(filepath) {
|
|
458
|
-
if (isBrowser()) {
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
try {
|
|
462
|
-
(async () => {
|
|
463
|
-
const { existsSync, unlinkSync } = await import("node:fs");
|
|
464
|
-
if (existsSync(filepath)) {
|
|
465
|
-
unlinkSync(filepath);
|
|
466
|
-
logger5.debug(`[OpenRouter] Deleted image: ${filepath}`);
|
|
467
|
-
}
|
|
468
|
-
})().catch((error) => {
|
|
469
|
-
logger5.warn(`[OpenRouter] Failed to delete image ${filepath}:`, String(error));
|
|
470
|
-
});
|
|
471
|
-
} catch (error) {
|
|
472
|
-
logger5.warn(`[OpenRouter] Failed to delete image ${filepath}:`, String(error));
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/models/image.ts
|
|
477
|
-
async function handleImageDescription(runtime, params) {
|
|
478
|
-
let imageUrl;
|
|
479
|
-
let promptText;
|
|
480
|
-
const modelName = getImageModel(runtime);
|
|
481
|
-
logger6.log(`[OpenRouter] Using IMAGE_DESCRIPTION model: ${modelName}`);
|
|
482
|
-
const maxOutputTokens = 300;
|
|
483
|
-
if (typeof params === "string") {
|
|
484
|
-
imageUrl = params;
|
|
485
|
-
promptText = "Please analyze this image and provide a title and detailed description.";
|
|
486
|
-
} else {
|
|
487
|
-
imageUrl = params.imageUrl;
|
|
488
|
-
promptText = params.prompt || "Please analyze this image and provide a title and detailed description.";
|
|
489
|
-
}
|
|
490
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
491
|
-
const messages = [
|
|
492
|
-
{
|
|
493
|
-
role: "user",
|
|
494
|
-
content: [
|
|
495
|
-
{ type: "text", text: promptText },
|
|
496
|
-
{ type: "image", image: imageUrl }
|
|
497
|
-
]
|
|
498
|
-
}
|
|
499
|
-
];
|
|
500
|
-
try {
|
|
501
|
-
const model = openrouter.chat(modelName);
|
|
502
|
-
const { text: responseText } = await generateText2({
|
|
503
|
-
model,
|
|
504
|
-
messages,
|
|
505
|
-
maxOutputTokens
|
|
506
|
-
});
|
|
507
|
-
return parseImageDescriptionResponse(responseText);
|
|
508
|
-
} catch (error) {
|
|
509
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
510
|
-
logger6.error(`Error analyzing image: ${message}`);
|
|
511
|
-
return {
|
|
512
|
-
title: "Failed to analyze image",
|
|
513
|
-
description: `Error: ${message}`
|
|
514
|
-
};
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
async function handleImageGeneration(runtime, params) {
|
|
518
|
-
const modelName = getImageGenerationModel(runtime);
|
|
519
|
-
logger6.log(`[OpenRouter] Using IMAGE_GENERATION model: ${modelName}`);
|
|
520
|
-
const apiKey = getApiKey(runtime);
|
|
521
|
-
try {
|
|
522
|
-
const baseUrl = getBaseURL(runtime);
|
|
523
|
-
const isBrowser2 = typeof globalThis !== "undefined" && globalThis.document;
|
|
524
|
-
const response = await fetch(`${baseUrl}/chat/completions`, {
|
|
525
|
-
method: "POST",
|
|
526
|
-
headers: {
|
|
527
|
-
...isBrowser2 ? {} : { Authorization: `Bearer ${apiKey}` },
|
|
528
|
-
"Content-Type": "application/json"
|
|
529
|
-
},
|
|
530
|
-
body: JSON.stringify({
|
|
531
|
-
model: modelName,
|
|
532
|
-
messages: [
|
|
533
|
-
{
|
|
534
|
-
role: "user",
|
|
535
|
-
content: params.prompt
|
|
536
|
-
}
|
|
537
|
-
],
|
|
538
|
-
modalities: ["image", "text"]
|
|
539
|
-
}),
|
|
540
|
-
signal: AbortSignal.timeout ? AbortSignal.timeout(60000) : undefined
|
|
541
|
-
});
|
|
542
|
-
if (!response.ok) {
|
|
543
|
-
const errorText = await response.text().catch(() => "");
|
|
544
|
-
throw new Error(`HTTP ${response.status} ${response.statusText} ${errorText}`);
|
|
545
|
-
}
|
|
546
|
-
const result = await response.json();
|
|
547
|
-
const images = [];
|
|
548
|
-
const savedPaths = [];
|
|
549
|
-
if (result.choices?.[0]?.message?.images) {
|
|
550
|
-
for (const [index, image] of result.choices[0].message.images.entries()) {
|
|
551
|
-
const base64Url = image.image_url.url;
|
|
552
|
-
const filepath = await saveBase64Image(base64Url, runtime.agentId, index);
|
|
553
|
-
if (filepath) {
|
|
554
|
-
logger6.log(`[OpenRouter] Returning image with filepath: ${filepath}`);
|
|
555
|
-
images.push({
|
|
556
|
-
url: filepath
|
|
557
|
-
});
|
|
558
|
-
savedPaths.push(filepath);
|
|
559
|
-
} else if (!base64Url.startsWith("data:")) {
|
|
560
|
-
images.push({ url: base64Url });
|
|
561
|
-
} else {
|
|
562
|
-
logger6.warn(`[OpenRouter] Failed to save image ${index + 1}, skipping`);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
if (savedPaths.length > 0 && shouldAutoCleanupImages(runtime)) {
|
|
567
|
-
setTimeout(() => {
|
|
568
|
-
savedPaths.forEach((path) => {
|
|
569
|
-
deleteImage(path);
|
|
570
|
-
});
|
|
571
|
-
}, 30000);
|
|
572
|
-
}
|
|
573
|
-
if (images.length === 0) {
|
|
574
|
-
throw new Error("No images generated in response");
|
|
575
|
-
}
|
|
576
|
-
logger6.log(`[OpenRouter] Generated ${images.length} image(s)`);
|
|
577
|
-
return images;
|
|
578
|
-
} catch (error) {
|
|
579
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
580
|
-
logger6.error(`[OpenRouter] Error generating image: ${message}`);
|
|
581
|
-
return [];
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
// src/models/embedding.ts
|
|
586
|
-
import { logger as logger7, ModelType as ModelType3, VECTOR_DIMS } from "@elizaos/core";
|
|
118
|
+
// models/embedding.ts
|
|
587
119
|
async function handleTextEmbedding(runtime, params) {
|
|
588
120
|
const embeddingModelName = getEmbeddingModel(runtime);
|
|
589
121
|
const embeddingDimension = Number.parseInt(getSetting(runtime, "OPENROUTER_EMBEDDING_DIMENSIONS") ?? getSetting(runtime, "EMBEDDING_DIMENSIONS") ?? "1536", 10);
|
|
590
122
|
if (!Object.values(VECTOR_DIMS).includes(embeddingDimension)) {
|
|
591
123
|
const errorMsg = `Invalid embedding dimension: ${embeddingDimension}. Must be one of: ${Object.values(VECTOR_DIMS).join(", ")}`;
|
|
592
|
-
|
|
124
|
+
logger3.error(errorMsg);
|
|
593
125
|
throw new Error(errorMsg);
|
|
594
126
|
}
|
|
595
127
|
if (params === null) {
|
|
596
|
-
logger7.debug("Creating test embedding for initialization");
|
|
597
128
|
const testVector = Array(embeddingDimension).fill(0);
|
|
598
129
|
testVector[0] = 0.1;
|
|
599
130
|
return testVector;
|
|
@@ -605,14 +136,14 @@ async function handleTextEmbedding(runtime, params) {
|
|
|
605
136
|
text = params.text;
|
|
606
137
|
} else {
|
|
607
138
|
const errorMsg = "Invalid input format for embedding";
|
|
608
|
-
|
|
139
|
+
logger3.warn(errorMsg);
|
|
609
140
|
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
610
141
|
fallbackVector[0] = 0.2;
|
|
611
142
|
return fallbackVector;
|
|
612
143
|
}
|
|
613
144
|
if (!text.trim()) {
|
|
614
145
|
const errorMsg = "Empty text for embedding";
|
|
615
|
-
|
|
146
|
+
logger3.warn(errorMsg);
|
|
616
147
|
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
617
148
|
fallbackVector[0] = 0.3;
|
|
618
149
|
return fallbackVector;
|
|
@@ -620,7 +151,7 @@ async function handleTextEmbedding(runtime, params) {
|
|
|
620
151
|
const apiKey = getApiKey(runtime);
|
|
621
152
|
if (!apiKey) {
|
|
622
153
|
const errorMsg = "OPENROUTER_API_KEY is not set";
|
|
623
|
-
|
|
154
|
+
logger3.error(errorMsg);
|
|
624
155
|
throw new Error(errorMsg);
|
|
625
156
|
}
|
|
626
157
|
const baseURL = getBaseURL(runtime);
|
|
@@ -639,18 +170,18 @@ async function handleTextEmbedding(runtime, params) {
|
|
|
639
170
|
})
|
|
640
171
|
});
|
|
641
172
|
if (!response.ok) {
|
|
642
|
-
|
|
173
|
+
logger3.error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
643
174
|
throw new Error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
644
175
|
}
|
|
645
176
|
const data = await response.json();
|
|
646
177
|
if (!data?.data?.[0]?.embedding) {
|
|
647
|
-
|
|
178
|
+
logger3.error("API returned invalid structure");
|
|
648
179
|
throw new Error("API returned invalid structure");
|
|
649
180
|
}
|
|
650
181
|
const embedding = data.data[0].embedding;
|
|
651
182
|
if (!Array.isArray(embedding) || embedding.length !== embeddingDimension) {
|
|
652
183
|
const errorMsg = `Embedding length ${embedding?.length ?? 0} does not match configured dimension ${embeddingDimension}`;
|
|
653
|
-
|
|
184
|
+
logger3.error(errorMsg);
|
|
654
185
|
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
655
186
|
fallbackVector[0] = 0.4;
|
|
656
187
|
return fallbackVector;
|
|
@@ -661,69 +192,353 @@ async function handleTextEmbedding(runtime, params) {
|
|
|
661
192
|
outputTokens: 0,
|
|
662
193
|
totalTokens: data.usage.total_tokens
|
|
663
194
|
};
|
|
664
|
-
emitModelUsageEvent(runtime,
|
|
195
|
+
emitModelUsageEvent(runtime, ModelType.TEXT_EMBEDDING, text, usage);
|
|
665
196
|
}
|
|
666
|
-
logger7.log(`Got valid embedding with length ${embedding.length}`);
|
|
667
197
|
return embedding;
|
|
668
198
|
} catch (error) {
|
|
669
199
|
const message = error instanceof Error ? error.message : String(error);
|
|
670
|
-
|
|
200
|
+
logger3.error(`Error generating embedding: ${message}`);
|
|
201
|
+
throw error instanceof Error ? error : new Error(message);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// models/image.ts
|
|
206
|
+
import { logger as logger4, ModelType as ModelType2 } from "@elizaos/core";
|
|
207
|
+
import { generateText } from "ai";
|
|
208
|
+
|
|
209
|
+
// providers/openrouter.ts
|
|
210
|
+
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
211
|
+
function createOpenRouterProvider(runtime) {
|
|
212
|
+
const apiKey = getApiKey(runtime);
|
|
213
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.document;
|
|
214
|
+
const baseURL = getBaseURL(runtime);
|
|
215
|
+
return createOpenRouter({
|
|
216
|
+
apiKey: isBrowser ? undefined : apiKey,
|
|
217
|
+
baseURL
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
// models/image.ts
|
|
221
|
+
async function handleImageDescription(runtime, params) {
|
|
222
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
223
|
+
const modelName = getImageModel(runtime);
|
|
224
|
+
const imageUrl = typeof params === "string" ? params : params.imageUrl;
|
|
225
|
+
const prompt = typeof params === "string" ? "Describe this image" : params.prompt || "Describe this image";
|
|
226
|
+
try {
|
|
227
|
+
const generateParams = {
|
|
228
|
+
model: openrouter.chat(modelName),
|
|
229
|
+
messages: [
|
|
230
|
+
{
|
|
231
|
+
role: "user",
|
|
232
|
+
content: [
|
|
233
|
+
{ type: "text", text: prompt },
|
|
234
|
+
{ type: "image", image: imageUrl }
|
|
235
|
+
]
|
|
236
|
+
}
|
|
237
|
+
]
|
|
238
|
+
};
|
|
239
|
+
const response = await generateText(generateParams);
|
|
240
|
+
if (response.usage) {
|
|
241
|
+
emitModelUsageEvent(runtime, ModelType2.IMAGE_DESCRIPTION, prompt, response.usage);
|
|
242
|
+
}
|
|
243
|
+
return response.text;
|
|
244
|
+
} catch (error) {
|
|
245
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
246
|
+
logger4.error(`Error describing image: ${message}`);
|
|
247
|
+
throw error instanceof Error ? error : new Error(message);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function handleImageGeneration(runtime, params) {
|
|
251
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
252
|
+
const modelName = getImageGenerationModel(runtime);
|
|
253
|
+
try {
|
|
254
|
+
const generateParams = {
|
|
255
|
+
model: openrouter.chat(modelName),
|
|
256
|
+
prompt: `Generate an image: ${params.prompt}`
|
|
257
|
+
};
|
|
258
|
+
const response = await generateText(generateParams);
|
|
259
|
+
if (response.usage) {
|
|
260
|
+
emitModelUsageEvent(runtime, ModelType2.IMAGE, params.prompt, response.usage);
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
imageUrl: response.text,
|
|
264
|
+
caption: params.prompt
|
|
265
|
+
};
|
|
266
|
+
} catch (error) {
|
|
267
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
268
|
+
logger4.error(`Error generating image: ${message}`);
|
|
671
269
|
throw error instanceof Error ? error : new Error(message);
|
|
672
270
|
}
|
|
673
271
|
}
|
|
674
272
|
|
|
675
|
-
//
|
|
273
|
+
// models/object.ts
|
|
274
|
+
import {
|
|
275
|
+
ModelType as ModelType3
|
|
276
|
+
} from "@elizaos/core";
|
|
277
|
+
import { generateObject, jsonSchema } from "ai";
|
|
278
|
+
|
|
279
|
+
// utils/helpers.ts
|
|
280
|
+
import { logger as logger5 } from "@elizaos/core";
|
|
281
|
+
function handleObjectGenerationError(error) {
|
|
282
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
283
|
+
logger5.error(`Error generating object: ${message}`);
|
|
284
|
+
return { error: message };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// models/object.ts
|
|
288
|
+
async function generateObjectWithModel(runtime, modelType, params) {
|
|
289
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
290
|
+
const modelName = modelType === ModelType3.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
291
|
+
const temperature = params.temperature ?? 0.7;
|
|
292
|
+
try {
|
|
293
|
+
const generateParams = {
|
|
294
|
+
model: openrouter.chat(modelName),
|
|
295
|
+
...params.schema && {
|
|
296
|
+
schema: jsonSchema(params.schema)
|
|
297
|
+
},
|
|
298
|
+
output: params.schema ? "object" : "no-schema",
|
|
299
|
+
prompt: params.prompt,
|
|
300
|
+
temperature
|
|
301
|
+
};
|
|
302
|
+
const { object, usage } = await generateObject(generateParams);
|
|
303
|
+
if (usage) {
|
|
304
|
+
emitModelUsageEvent(runtime, modelType, params.prompt, usage);
|
|
305
|
+
}
|
|
306
|
+
return object;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
return handleObjectGenerationError(error);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async function handleObjectSmall(runtime, params) {
|
|
312
|
+
return generateObjectWithModel(runtime, ModelType3.OBJECT_SMALL, params);
|
|
313
|
+
}
|
|
314
|
+
async function handleObjectLarge(runtime, params) {
|
|
315
|
+
return generateObjectWithModel(runtime, ModelType3.OBJECT_LARGE, params);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// models/text.ts
|
|
319
|
+
import { ModelType as ModelType4 } from "@elizaos/core";
|
|
320
|
+
import { generateText as generateText2, streamText } from "ai";
|
|
321
|
+
function buildGenerateParams(runtime, modelType, params) {
|
|
322
|
+
const { prompt, stopSequences = [] } = params;
|
|
323
|
+
const temperature = params.temperature ?? 0.7;
|
|
324
|
+
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
325
|
+
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
326
|
+
const paramsWithMax = params;
|
|
327
|
+
const resolvedMaxOutput = paramsWithMax.maxOutputTokens ?? paramsWithMax.maxTokens ?? 8192;
|
|
328
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
329
|
+
const modelName = modelType === ModelType4.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
330
|
+
const modelLabel = modelType === ModelType4.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
331
|
+
const generateParams = {
|
|
332
|
+
model: openrouter.chat(modelName),
|
|
333
|
+
prompt,
|
|
334
|
+
system: runtime.character?.system ?? undefined,
|
|
335
|
+
temperature,
|
|
336
|
+
frequencyPenalty,
|
|
337
|
+
presencePenalty,
|
|
338
|
+
stopSequences,
|
|
339
|
+
maxOutputTokens: resolvedMaxOutput
|
|
340
|
+
};
|
|
341
|
+
return { generateParams, modelName, modelLabel, prompt };
|
|
342
|
+
}
|
|
343
|
+
function handleStreamingGeneration(runtime, modelType, generateParams, prompt, _modelLabel) {
|
|
344
|
+
const streamResult = streamText(generateParams);
|
|
345
|
+
return {
|
|
346
|
+
textStream: streamResult.textStream,
|
|
347
|
+
text: Promise.resolve(streamResult.text),
|
|
348
|
+
usage: Promise.resolve(streamResult.usage).then((usage) => {
|
|
349
|
+
if (usage) {
|
|
350
|
+
emitModelUsageEvent(runtime, modelType, prompt, usage);
|
|
351
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
352
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
353
|
+
return {
|
|
354
|
+
promptTokens: inputTokens,
|
|
355
|
+
completionTokens: outputTokens,
|
|
356
|
+
totalTokens: inputTokens + outputTokens
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return;
|
|
360
|
+
}),
|
|
361
|
+
finishReason: Promise.resolve(streamResult.finishReason)
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
async function generateTextWithModel(runtime, modelType, params) {
|
|
365
|
+
const {
|
|
366
|
+
generateParams,
|
|
367
|
+
modelName: _modelName,
|
|
368
|
+
modelLabel,
|
|
369
|
+
prompt
|
|
370
|
+
} = buildGenerateParams(runtime, modelType, params);
|
|
371
|
+
if (params.stream) {
|
|
372
|
+
return handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelLabel);
|
|
373
|
+
}
|
|
374
|
+
const response = await generateText2(generateParams);
|
|
375
|
+
if (response.usage) {
|
|
376
|
+
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
377
|
+
}
|
|
378
|
+
return response.text;
|
|
379
|
+
}
|
|
380
|
+
async function handleTextSmall(runtime, params) {
|
|
381
|
+
return generateTextWithModel(runtime, ModelType4.TEXT_SMALL, params);
|
|
382
|
+
}
|
|
383
|
+
async function handleTextLarge(runtime, params) {
|
|
384
|
+
return generateTextWithModel(runtime, ModelType4.TEXT_LARGE, params);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// plugin.ts
|
|
388
|
+
function getProcessEnv() {
|
|
389
|
+
if (typeof process === "undefined" || !process.env) {
|
|
390
|
+
return {};
|
|
391
|
+
}
|
|
392
|
+
return process.env;
|
|
393
|
+
}
|
|
394
|
+
var env = getProcessEnv();
|
|
676
395
|
var openrouterPlugin = {
|
|
677
396
|
name: "openrouter",
|
|
678
|
-
description: "OpenRouter plugin",
|
|
397
|
+
description: "OpenRouter multi-model AI gateway plugin",
|
|
679
398
|
config: {
|
|
680
|
-
OPENROUTER_API_KEY:
|
|
681
|
-
OPENROUTER_BASE_URL:
|
|
682
|
-
OPENROUTER_SMALL_MODEL:
|
|
683
|
-
OPENROUTER_LARGE_MODEL:
|
|
684
|
-
OPENROUTER_IMAGE_MODEL:
|
|
685
|
-
OPENROUTER_IMAGE_GENERATION_MODEL:
|
|
686
|
-
OPENROUTER_EMBEDDING_MODEL:
|
|
687
|
-
OPENROUTER_EMBEDDING_DIMENSIONS:
|
|
688
|
-
OPENROUTER_AUTO_CLEANUP_IMAGES:
|
|
689
|
-
SMALL_MODEL:
|
|
690
|
-
LARGE_MODEL:
|
|
691
|
-
IMAGE_MODEL:
|
|
692
|
-
IMAGE_GENERATION_MODEL:
|
|
693
|
-
EMBEDDING_MODEL:
|
|
694
|
-
EMBEDDING_DIMENSIONS:
|
|
399
|
+
OPENROUTER_API_KEY: env.OPENROUTER_API_KEY ?? null,
|
|
400
|
+
OPENROUTER_BASE_URL: env.OPENROUTER_BASE_URL ?? null,
|
|
401
|
+
OPENROUTER_SMALL_MODEL: env.OPENROUTER_SMALL_MODEL ?? null,
|
|
402
|
+
OPENROUTER_LARGE_MODEL: env.OPENROUTER_LARGE_MODEL ?? null,
|
|
403
|
+
OPENROUTER_IMAGE_MODEL: env.OPENROUTER_IMAGE_MODEL ?? null,
|
|
404
|
+
OPENROUTER_IMAGE_GENERATION_MODEL: env.OPENROUTER_IMAGE_GENERATION_MODEL ?? null,
|
|
405
|
+
OPENROUTER_EMBEDDING_MODEL: env.OPENROUTER_EMBEDDING_MODEL ?? null,
|
|
406
|
+
OPENROUTER_EMBEDDING_DIMENSIONS: env.OPENROUTER_EMBEDDING_DIMENSIONS ?? null,
|
|
407
|
+
OPENROUTER_AUTO_CLEANUP_IMAGES: env.OPENROUTER_AUTO_CLEANUP_IMAGES ?? null,
|
|
408
|
+
SMALL_MODEL: env.SMALL_MODEL ?? null,
|
|
409
|
+
LARGE_MODEL: env.LARGE_MODEL ?? null,
|
|
410
|
+
IMAGE_MODEL: env.IMAGE_MODEL ?? null,
|
|
411
|
+
IMAGE_GENERATION_MODEL: env.IMAGE_GENERATION_MODEL ?? null,
|
|
412
|
+
EMBEDDING_MODEL: env.EMBEDDING_MODEL ?? null,
|
|
413
|
+
EMBEDDING_DIMENSIONS: env.EMBEDDING_DIMENSIONS ?? null
|
|
695
414
|
},
|
|
696
415
|
async init(config, runtime) {
|
|
697
416
|
initializeOpenRouter(config, runtime);
|
|
698
417
|
},
|
|
699
418
|
models: {
|
|
700
|
-
[
|
|
419
|
+
[ModelType5.TEXT_SMALL]: async (runtime, params) => {
|
|
701
420
|
return handleTextSmall(runtime, params);
|
|
702
421
|
},
|
|
703
|
-
[
|
|
422
|
+
[ModelType5.TEXT_LARGE]: async (runtime, params) => {
|
|
704
423
|
return handleTextLarge(runtime, params);
|
|
705
424
|
},
|
|
706
|
-
[
|
|
425
|
+
[ModelType5.OBJECT_SMALL]: async (runtime, params) => {
|
|
707
426
|
return handleObjectSmall(runtime, params);
|
|
708
427
|
},
|
|
709
|
-
[
|
|
428
|
+
[ModelType5.OBJECT_LARGE]: async (runtime, params) => {
|
|
710
429
|
return handleObjectLarge(runtime, params);
|
|
711
430
|
},
|
|
712
|
-
[
|
|
713
|
-
|
|
431
|
+
[ModelType5.IMAGE_DESCRIPTION]: async (runtime, params) => {
|
|
432
|
+
const description = await handleImageDescription(runtime, params);
|
|
433
|
+
return { title: "", description };
|
|
714
434
|
},
|
|
715
|
-
[
|
|
716
|
-
|
|
435
|
+
[ModelType5.IMAGE]: async (runtime, params) => {
|
|
436
|
+
const result = await handleImageGeneration(runtime, params);
|
|
437
|
+
return [{ url: result.imageUrl }];
|
|
717
438
|
},
|
|
718
|
-
[
|
|
439
|
+
[ModelType5.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
719
440
|
return handleTextEmbedding(runtime, params);
|
|
720
441
|
}
|
|
721
|
-
}
|
|
442
|
+
},
|
|
443
|
+
tests: [
|
|
444
|
+
{
|
|
445
|
+
name: "openrouter_plugin_tests",
|
|
446
|
+
tests: [
|
|
447
|
+
{
|
|
448
|
+
name: "openrouter_test_text_small",
|
|
449
|
+
fn: async (runtime) => {
|
|
450
|
+
try {
|
|
451
|
+
const text = await runtime.useModel(ModelType5.TEXT_SMALL, {
|
|
452
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
453
|
+
});
|
|
454
|
+
if (text.length === 0) {
|
|
455
|
+
throw new Error("Failed to generate text");
|
|
456
|
+
}
|
|
457
|
+
logger6.log({ text }, "generated with test_text_small");
|
|
458
|
+
} catch (error) {
|
|
459
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
460
|
+
logger6.error(`Error in test_text_small: ${message}`);
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
name: "openrouter_test_text_large",
|
|
467
|
+
fn: async (runtime) => {
|
|
468
|
+
try {
|
|
469
|
+
const text = await runtime.useModel(ModelType5.TEXT_LARGE, {
|
|
470
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
471
|
+
});
|
|
472
|
+
if (text.length === 0) {
|
|
473
|
+
throw new Error("Failed to generate text");
|
|
474
|
+
}
|
|
475
|
+
logger6.log({ text }, "generated with test_text_large");
|
|
476
|
+
} catch (error) {
|
|
477
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
478
|
+
logger6.error(`Error in test_text_large: ${message}`);
|
|
479
|
+
throw error;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
name: "openrouter_test_object_small",
|
|
485
|
+
fn: async (runtime) => {
|
|
486
|
+
try {
|
|
487
|
+
const result = await runtime.useModel(ModelType5.OBJECT_SMALL, {
|
|
488
|
+
prompt: "Create a simple JSON object with a message field saying hello",
|
|
489
|
+
schema: { type: "object" }
|
|
490
|
+
});
|
|
491
|
+
logger6.log({ result }, "Generated object with test_object_small");
|
|
492
|
+
if (!result || typeof result === "object" && "error" in result) {
|
|
493
|
+
throw new Error("Failed to generate object");
|
|
494
|
+
}
|
|
495
|
+
} catch (error) {
|
|
496
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
497
|
+
logger6.error(`Error in test_object_small: ${message}`);
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
name: "openrouter_test_text_embedding",
|
|
504
|
+
fn: async (runtime) => {
|
|
505
|
+
try {
|
|
506
|
+
const embedding = await runtime.useModel(ModelType5.TEXT_EMBEDDING, {
|
|
507
|
+
text: "Hello, world!"
|
|
508
|
+
});
|
|
509
|
+
logger6.log({ embedding }, "embedding");
|
|
510
|
+
} catch (error) {
|
|
511
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
512
|
+
logger6.error(`Error in test_text_embedding: ${message}`);
|
|
513
|
+
throw error;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
]
|
|
518
|
+
}
|
|
519
|
+
]
|
|
722
520
|
};
|
|
723
|
-
var src_default = openrouterPlugin;
|
|
724
521
|
export {
|
|
522
|
+
shouldAutoCleanupImages,
|
|
725
523
|
openrouterPlugin,
|
|
726
|
-
|
|
524
|
+
getSmallModel,
|
|
525
|
+
getSetting,
|
|
526
|
+
getLargeModel,
|
|
527
|
+
getImageModel,
|
|
528
|
+
getImageGenerationModel,
|
|
529
|
+
getEmbeddingModel,
|
|
530
|
+
getEmbeddingDimensions,
|
|
531
|
+
getBaseURL,
|
|
532
|
+
getApiKey,
|
|
533
|
+
openrouterPlugin as default,
|
|
534
|
+
DEFAULT_SMALL_MODEL,
|
|
535
|
+
DEFAULT_LARGE_MODEL,
|
|
536
|
+
DEFAULT_IMAGE_MODEL,
|
|
537
|
+
DEFAULT_IMAGE_GENERATION_MODEL,
|
|
538
|
+
DEFAULT_EMBEDDING_MODEL,
|
|
539
|
+
DEFAULT_EMBEDDING_DIMENSIONS,
|
|
540
|
+
DEFAULT_BASE_URL
|
|
727
541
|
};
|
|
728
542
|
|
|
729
|
-
//# debugId=
|
|
543
|
+
//# debugId=22E4EFC6A1BE14DB64756E2164756E21
|
|
544
|
+
//# sourceMappingURL=index.node.js.map
|