@elizaos/plugin-openrouter 1.5.14 → 1.5.17
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 +16 -3
- package/dist/browser/index.browser.js +322 -162
- package/dist/browser/index.browser.js.map +12 -11
- package/dist/cjs/index.node.cjs +318 -159
- package/dist/cjs/index.node.js.map +11 -10
- package/dist/index.d.ts +1 -1
- package/dist/models/embedding.d.ts +5 -0
- package/dist/models/index.d.ts +4 -3
- package/dist/models/object.d.ts +2 -2
- package/dist/models/text.d.ts +11 -11
- package/dist/node/index.node.js +319 -159
- package/dist/node/index.node.js.map +11 -10
- package/dist/types/index.d.ts +0 -46
- package/dist/utils/config.d.ts +7 -7
- package/dist/utils/helpers.d.ts +1 -9
- package/package.json +31 -2
package/dist/cjs/index.node.cjs
CHANGED
|
@@ -48,7 +48,7 @@ __export(exports_index_node, {
|
|
|
48
48
|
module.exports = __toCommonJS(exports_index_node);
|
|
49
49
|
|
|
50
50
|
// src/index.ts
|
|
51
|
-
var
|
|
51
|
+
var import_core9 = require("@elizaos/core");
|
|
52
52
|
|
|
53
53
|
// src/init.ts
|
|
54
54
|
var import_core = require("@elizaos/core");
|
|
@@ -56,7 +56,11 @@ var import_undici = require("undici");
|
|
|
56
56
|
|
|
57
57
|
// src/utils/config.ts
|
|
58
58
|
function getSetting(runtime, key, defaultValue) {
|
|
59
|
-
|
|
59
|
+
const value = runtime.getSetting(key);
|
|
60
|
+
if (value !== undefined && value !== null) {
|
|
61
|
+
return String(value);
|
|
62
|
+
}
|
|
63
|
+
return process.env[key] ?? defaultValue;
|
|
60
64
|
}
|
|
61
65
|
function getBaseURL(runtime) {
|
|
62
66
|
const browserURL = getSetting(runtime, "OPENROUTER_BROWSER_BASE_URL");
|
|
@@ -80,19 +84,13 @@ function getImageModel(runtime) {
|
|
|
80
84
|
function getImageGenerationModel(runtime) {
|
|
81
85
|
return getSetting(runtime, "OPENROUTER_IMAGE_GENERATION_MODEL") ?? getSetting(runtime, "IMAGE_GENERATION_MODEL", "google/gemini-2.5-flash-image-preview") ?? "google/gemini-2.5-flash-image-preview";
|
|
82
86
|
}
|
|
87
|
+
function getEmbeddingModel(runtime) {
|
|
88
|
+
return getSetting(runtime, "OPENROUTER_EMBEDDING_MODEL") ?? getSetting(runtime, "EMBEDDING_MODEL", "openai/text-embedding-3-small") ?? "openai/text-embedding-3-small";
|
|
89
|
+
}
|
|
83
90
|
function shouldAutoCleanupImages(runtime) {
|
|
84
91
|
const setting = getSetting(runtime, "OPENROUTER_AUTO_CLEANUP_IMAGES", "false");
|
|
85
92
|
return setting?.toLowerCase() === "true";
|
|
86
93
|
}
|
|
87
|
-
function getToolExecutionMaxSteps(runtime) {
|
|
88
|
-
const setting = getSetting(runtime, "OPENROUTER_TOOL_EXECUTION_MAX_STEPS", "15");
|
|
89
|
-
const value = parseInt(setting || "15", 10);
|
|
90
|
-
if (Number.isNaN(value) || value < 1)
|
|
91
|
-
return 15;
|
|
92
|
-
if (value > 100)
|
|
93
|
-
return 100;
|
|
94
|
-
return value;
|
|
95
|
-
}
|
|
96
94
|
|
|
97
95
|
// src/init.ts
|
|
98
96
|
function initializeOpenRouter(_config, runtime) {
|
|
@@ -131,8 +129,8 @@ function initializeOpenRouter(_config, runtime) {
|
|
|
131
129
|
}
|
|
132
130
|
|
|
133
131
|
// src/models/text.ts
|
|
134
|
-
var
|
|
135
|
-
var
|
|
132
|
+
var import_core3 = require("@elizaos/core");
|
|
133
|
+
var import_ai = require("ai");
|
|
136
134
|
|
|
137
135
|
// src/providers/openrouter.ts
|
|
138
136
|
var import_ai_sdk_provider = require("@openrouter/ai-sdk-provider");
|
|
@@ -153,6 +151,8 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
153
151
|
const outputTokens = Number(usage.outputTokens || 0);
|
|
154
152
|
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
155
153
|
runtime.emitEvent(import_core2.EventType.MODEL_USED, {
|
|
154
|
+
runtime,
|
|
155
|
+
source: "openrouter",
|
|
156
156
|
provider: "openrouter",
|
|
157
157
|
type,
|
|
158
158
|
prompt: truncatedPrompt,
|
|
@@ -164,13 +164,80 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
// src/models/text.ts
|
|
168
|
+
function buildGenerateParams(runtime, modelType, params) {
|
|
169
|
+
const { prompt, stopSequences = [] } = params;
|
|
170
|
+
const temperature = params.temperature ?? 0.7;
|
|
171
|
+
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
172
|
+
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
173
|
+
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
174
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
175
|
+
const modelName = modelType === import_core3.ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
176
|
+
const modelLabel = modelType === import_core3.ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
177
|
+
const generateParams = {
|
|
178
|
+
model: openrouter.chat(modelName),
|
|
179
|
+
prompt,
|
|
180
|
+
system: runtime.character.system ?? undefined,
|
|
181
|
+
temperature,
|
|
182
|
+
frequencyPenalty,
|
|
183
|
+
presencePenalty,
|
|
184
|
+
stopSequences
|
|
185
|
+
};
|
|
186
|
+
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
187
|
+
return { generateParams, modelName, modelLabel, prompt };
|
|
188
|
+
}
|
|
189
|
+
function handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelLabel) {
|
|
190
|
+
import_core3.logger.debug(`[OpenRouter] Streaming text with ${modelLabel} model`);
|
|
191
|
+
const streamResult = import_ai.streamText(generateParams);
|
|
192
|
+
return {
|
|
193
|
+
textStream: streamResult.textStream,
|
|
194
|
+
text: streamResult.text,
|
|
195
|
+
usage: streamResult.usage.then((usage) => {
|
|
196
|
+
if (usage) {
|
|
197
|
+
emitModelUsageEvent(runtime, modelType, prompt, usage);
|
|
198
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
199
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
200
|
+
return {
|
|
201
|
+
promptTokens: inputTokens,
|
|
202
|
+
completionTokens: outputTokens,
|
|
203
|
+
totalTokens: inputTokens + outputTokens
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}),
|
|
208
|
+
finishReason: streamResult.finishReason
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async function generateTextWithModel(runtime, modelType, params) {
|
|
212
|
+
const { generateParams, modelName, modelLabel, prompt } = buildGenerateParams(runtime, modelType, params);
|
|
213
|
+
import_core3.logger.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
214
|
+
if (params.stream) {
|
|
215
|
+
return handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelLabel);
|
|
216
|
+
}
|
|
217
|
+
const response = await import_ai.generateText(generateParams);
|
|
218
|
+
if (response.usage) {
|
|
219
|
+
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
220
|
+
}
|
|
221
|
+
return response.text;
|
|
222
|
+
}
|
|
223
|
+
async function handleTextSmall(runtime, params) {
|
|
224
|
+
return generateTextWithModel(runtime, import_core3.ModelType.TEXT_SMALL, params);
|
|
225
|
+
}
|
|
226
|
+
async function handleTextLarge(runtime, params) {
|
|
227
|
+
return generateTextWithModel(runtime, import_core3.ModelType.TEXT_LARGE, params);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// src/models/object.ts
|
|
231
|
+
var import_core5 = require("@elizaos/core");
|
|
232
|
+
var import_ai3 = require("ai");
|
|
233
|
+
|
|
167
234
|
// src/utils/helpers.ts
|
|
168
|
-
var
|
|
169
|
-
var
|
|
235
|
+
var import_core4 = require("@elizaos/core");
|
|
236
|
+
var import_ai2 = require("ai");
|
|
170
237
|
function getJsonRepairFunction() {
|
|
171
238
|
return async ({ text, error }) => {
|
|
172
239
|
try {
|
|
173
|
-
if (error instanceof
|
|
240
|
+
if (error instanceof import_ai2.JSONParseError) {
|
|
174
241
|
const cleanedText = text.replace(/```json\n|\n```|```/g, "");
|
|
175
242
|
JSON.parse(cleanedText);
|
|
176
243
|
return cleanedText;
|
|
@@ -178,17 +245,11 @@ function getJsonRepairFunction() {
|
|
|
178
245
|
return null;
|
|
179
246
|
} catch (jsonError) {
|
|
180
247
|
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
181
|
-
|
|
248
|
+
import_core4.logger.warn(`Failed to repair JSON text: ${message}`);
|
|
182
249
|
return null;
|
|
183
250
|
}
|
|
184
251
|
};
|
|
185
252
|
}
|
|
186
|
-
function handleEmptyToolResponse(modelType) {
|
|
187
|
-
import_core3.logger.warn(`[${modelType}] No text generated after tool execution`);
|
|
188
|
-
const fallbackText = "I executed the requested action. The tool completed successfully.";
|
|
189
|
-
import_core3.logger.warn(`[${modelType}] Using fallback response text`);
|
|
190
|
-
return fallbackText;
|
|
191
|
-
}
|
|
192
253
|
function parseImageDescriptionResponse(responseText) {
|
|
193
254
|
try {
|
|
194
255
|
const jsonResponse = JSON.parse(responseText);
|
|
@@ -196,7 +257,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
196
257
|
return jsonResponse;
|
|
197
258
|
}
|
|
198
259
|
} catch (e) {
|
|
199
|
-
|
|
260
|
+
import_core4.logger.debug(`Parsing as JSON failed, processing as text: ${e}`);
|
|
200
261
|
}
|
|
201
262
|
const titleMatch = responseText.match(/title[:\s]+(.+?)(?:\n|$)/i);
|
|
202
263
|
const title = titleMatch?.[1]?.trim() || "Image Analysis";
|
|
@@ -204,8 +265,8 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
204
265
|
return { title, description };
|
|
205
266
|
}
|
|
206
267
|
async function handleObjectGenerationError(error) {
|
|
207
|
-
if (error instanceof
|
|
208
|
-
|
|
268
|
+
if (error instanceof import_ai2.JSONParseError) {
|
|
269
|
+
import_core4.logger.error(`[generateObject] Failed to parse JSON: ${error.message}`);
|
|
209
270
|
const repairFunction = getJsonRepairFunction();
|
|
210
271
|
const repairedJsonString = await repairFunction({
|
|
211
272
|
text: error.text,
|
|
@@ -214,148 +275,29 @@ async function handleObjectGenerationError(error) {
|
|
|
214
275
|
if (repairedJsonString) {
|
|
215
276
|
try {
|
|
216
277
|
const repairedObject = JSON.parse(repairedJsonString);
|
|
217
|
-
|
|
278
|
+
import_core4.logger.log("[generateObject] Successfully repaired JSON.");
|
|
218
279
|
return repairedObject;
|
|
219
280
|
} catch (repairParseError) {
|
|
220
281
|
const message = repairParseError instanceof Error ? repairParseError.message : String(repairParseError);
|
|
221
|
-
|
|
282
|
+
import_core4.logger.error(`[generateObject] Failed to parse repaired JSON: ${message}`);
|
|
222
283
|
if (repairParseError instanceof Error)
|
|
223
284
|
throw repairParseError;
|
|
224
285
|
throw Object.assign(new Error(message), { cause: repairParseError });
|
|
225
286
|
}
|
|
226
287
|
} else {
|
|
227
|
-
|
|
288
|
+
import_core4.logger.error("[generateObject] JSON repair failed.");
|
|
228
289
|
throw error;
|
|
229
290
|
}
|
|
230
291
|
} else {
|
|
231
292
|
const message = error instanceof Error ? error.message : String(error);
|
|
232
|
-
|
|
293
|
+
import_core4.logger.error(`[generateObject] Unknown error: ${message}`);
|
|
233
294
|
if (error instanceof Error)
|
|
234
295
|
throw error;
|
|
235
296
|
throw Object.assign(new Error(message), { cause: error });
|
|
236
297
|
}
|
|
237
298
|
}
|
|
238
|
-
function isLikelyBase64(key, value) {
|
|
239
|
-
const base64KeyPattern = /^(data|content|body|payload|encoded|b64|base64|document)$/i;
|
|
240
|
-
if (!base64KeyPattern.test(key))
|
|
241
|
-
return false;
|
|
242
|
-
if (value.length < 20 || value.length > 1024 * 1024)
|
|
243
|
-
return false;
|
|
244
|
-
if (value.length % 4 !== 0)
|
|
245
|
-
return false;
|
|
246
|
-
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
247
|
-
return false;
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
|
-
function decodeBase64Fields(obj, depth = 0) {
|
|
251
|
-
if (depth > 5)
|
|
252
|
-
return obj;
|
|
253
|
-
if (!obj || typeof obj !== "object")
|
|
254
|
-
return obj;
|
|
255
|
-
if (Array.isArray(obj))
|
|
256
|
-
return obj.map((item) => decodeBase64Fields(item, depth + 1));
|
|
257
|
-
const decoded = {};
|
|
258
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
259
|
-
if (typeof value === "string" && isLikelyBase64(key, value)) {
|
|
260
|
-
try {
|
|
261
|
-
decoded[key] = Buffer.from(value, "base64").toString("utf8");
|
|
262
|
-
import_core3.logger.debug(`[decodeBase64] Decoded field '${key}' (${value.length} chars)`);
|
|
263
|
-
} catch (error) {
|
|
264
|
-
import_core3.logger.warn(`[decodeBase64] Failed to decode field '${key}': ${error}`);
|
|
265
|
-
decoded[key] = value;
|
|
266
|
-
}
|
|
267
|
-
} else if (value && typeof value === "object") {
|
|
268
|
-
decoded[key] = decodeBase64Fields(value, depth + 1);
|
|
269
|
-
} else {
|
|
270
|
-
decoded[key] = value;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return decoded;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// src/models/text.ts
|
|
277
|
-
async function generateTextWithModel(runtime, modelType, params) {
|
|
278
|
-
const { prompt, stopSequences = [], tools, toolChoice } = params;
|
|
279
|
-
const temperature = params.temperature ?? 0.7;
|
|
280
|
-
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
281
|
-
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
282
|
-
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
283
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
284
|
-
const modelName = modelType === import_core4.ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
285
|
-
const modelLabel = modelType === import_core4.ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
286
|
-
import_core4.logger.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
287
|
-
const generateParams = {
|
|
288
|
-
model: openrouter.chat(modelName),
|
|
289
|
-
prompt,
|
|
290
|
-
system: runtime.character.system ?? undefined,
|
|
291
|
-
temperature,
|
|
292
|
-
frequencyPenalty,
|
|
293
|
-
presencePenalty,
|
|
294
|
-
stopSequences
|
|
295
|
-
};
|
|
296
|
-
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
297
|
-
if (tools) {
|
|
298
|
-
generateParams.tools = tools;
|
|
299
|
-
const maxSteps = getToolExecutionMaxSteps(runtime);
|
|
300
|
-
generateParams.stopWhen = import_ai2.stepCountIs(maxSteps);
|
|
301
|
-
import_core4.logger.debug(`[OpenRouter] Using maxSteps: ${maxSteps} for tool execution`);
|
|
302
|
-
}
|
|
303
|
-
if (toolChoice) {
|
|
304
|
-
generateParams.toolChoice = toolChoice;
|
|
305
|
-
}
|
|
306
|
-
let capturedToolResults = [];
|
|
307
|
-
let capturedToolCalls = [];
|
|
308
|
-
if (tools) {
|
|
309
|
-
generateParams.onStepFinish = async (stepResult) => {
|
|
310
|
-
if (stepResult.toolCalls && stepResult.toolCalls.length > 0) {
|
|
311
|
-
capturedToolCalls = [
|
|
312
|
-
...capturedToolCalls,
|
|
313
|
-
...stepResult.toolCalls
|
|
314
|
-
];
|
|
315
|
-
}
|
|
316
|
-
if (stepResult.content && Array.isArray(stepResult.content)) {
|
|
317
|
-
const toolResultsFromContent = stepResult.content.filter((content) => content.type === "tool-result" && content.output).map((content) => ({
|
|
318
|
-
toolCallId: content.toolCallId,
|
|
319
|
-
result: decodeBase64Fields(content.output)
|
|
320
|
-
}));
|
|
321
|
-
if (toolResultsFromContent.length > 0) {
|
|
322
|
-
capturedToolResults = [...capturedToolResults, ...toolResultsFromContent];
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
const response = await import_ai2.generateText(generateParams);
|
|
328
|
-
let responseText;
|
|
329
|
-
if (tools && (!response.text || response.text.trim() === "" || response.text === "Tools executed successfully.")) {
|
|
330
|
-
responseText = handleEmptyToolResponse(modelLabel);
|
|
331
|
-
} else {
|
|
332
|
-
responseText = response.text;
|
|
333
|
-
}
|
|
334
|
-
if (response.usage) {
|
|
335
|
-
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
336
|
-
}
|
|
337
|
-
if (tools && response.steps && response.steps.length > 0) {
|
|
338
|
-
return {
|
|
339
|
-
text: responseText,
|
|
340
|
-
toolCalls: capturedToolCalls,
|
|
341
|
-
toolResults: capturedToolResults,
|
|
342
|
-
steps: response.steps,
|
|
343
|
-
usage: response.usage,
|
|
344
|
-
finishReason: response.finishReason
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
return responseText;
|
|
348
|
-
}
|
|
349
|
-
async function handleTextSmall(runtime, params) {
|
|
350
|
-
return generateTextWithModel(runtime, import_core4.ModelType.TEXT_SMALL, params);
|
|
351
|
-
}
|
|
352
|
-
async function handleTextLarge(runtime, params) {
|
|
353
|
-
return generateTextWithModel(runtime, import_core4.ModelType.TEXT_LARGE, params);
|
|
354
|
-
}
|
|
355
299
|
|
|
356
300
|
// src/models/object.ts
|
|
357
|
-
var import_core5 = require("@elizaos/core");
|
|
358
|
-
var import_ai3 = require("ai");
|
|
359
301
|
async function generateObjectWithModel(runtime, modelType, params) {
|
|
360
302
|
const openrouter = createOpenRouterProvider(runtime);
|
|
361
303
|
const modelName = modelType === import_core5.ModelType.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
@@ -365,7 +307,7 @@ async function generateObjectWithModel(runtime, modelType, params) {
|
|
|
365
307
|
try {
|
|
366
308
|
const { object, usage } = await import_ai3.generateObject({
|
|
367
309
|
model: openrouter.chat(modelName),
|
|
368
|
-
...params.schema && { schema: params.schema },
|
|
310
|
+
...params.schema && { schema: import_ai3.jsonSchema(params.schema) },
|
|
369
311
|
output: params.schema ? "object" : "no-schema",
|
|
370
312
|
prompt: params.prompt,
|
|
371
313
|
temperature,
|
|
@@ -600,6 +542,96 @@ async function handleImageGeneration(runtime, params) {
|
|
|
600
542
|
}
|
|
601
543
|
}
|
|
602
544
|
|
|
545
|
+
// src/models/embedding.ts
|
|
546
|
+
var import_core8 = require("@elizaos/core");
|
|
547
|
+
async function handleTextEmbedding(runtime, params) {
|
|
548
|
+
const embeddingModelName = getEmbeddingModel(runtime);
|
|
549
|
+
const embeddingDimension = Number.parseInt(getSetting(runtime, "OPENROUTER_EMBEDDING_DIMENSIONS") ?? getSetting(runtime, "EMBEDDING_DIMENSIONS") ?? "1536", 10);
|
|
550
|
+
if (!Object.values(import_core8.VECTOR_DIMS).includes(embeddingDimension)) {
|
|
551
|
+
const errorMsg = `Invalid embedding dimension: ${embeddingDimension}. Must be one of: ${Object.values(import_core8.VECTOR_DIMS).join(", ")}`;
|
|
552
|
+
import_core8.logger.error(errorMsg);
|
|
553
|
+
throw new Error(errorMsg);
|
|
554
|
+
}
|
|
555
|
+
if (params === null) {
|
|
556
|
+
import_core8.logger.debug("Creating test embedding for initialization");
|
|
557
|
+
const testVector = Array(embeddingDimension).fill(0);
|
|
558
|
+
testVector[0] = 0.1;
|
|
559
|
+
return testVector;
|
|
560
|
+
}
|
|
561
|
+
let text;
|
|
562
|
+
if (typeof params === "string") {
|
|
563
|
+
text = params;
|
|
564
|
+
} else if (typeof params === "object" && params.text) {
|
|
565
|
+
text = params.text;
|
|
566
|
+
} else {
|
|
567
|
+
const errorMsg = "Invalid input format for embedding";
|
|
568
|
+
import_core8.logger.warn(errorMsg);
|
|
569
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
570
|
+
fallbackVector[0] = 0.2;
|
|
571
|
+
return fallbackVector;
|
|
572
|
+
}
|
|
573
|
+
if (!text.trim()) {
|
|
574
|
+
const errorMsg = "Empty text for embedding";
|
|
575
|
+
import_core8.logger.warn(errorMsg);
|
|
576
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
577
|
+
fallbackVector[0] = 0.3;
|
|
578
|
+
return fallbackVector;
|
|
579
|
+
}
|
|
580
|
+
const apiKey = getApiKey(runtime);
|
|
581
|
+
if (!apiKey) {
|
|
582
|
+
const errorMsg = "OPENROUTER_API_KEY is not set";
|
|
583
|
+
import_core8.logger.error(errorMsg);
|
|
584
|
+
throw new Error(errorMsg);
|
|
585
|
+
}
|
|
586
|
+
const baseURL = getBaseURL(runtime);
|
|
587
|
+
try {
|
|
588
|
+
const response = await fetch(`${baseURL}/embeddings`, {
|
|
589
|
+
method: "POST",
|
|
590
|
+
headers: {
|
|
591
|
+
Authorization: `Bearer ${apiKey}`,
|
|
592
|
+
"Content-Type": "application/json",
|
|
593
|
+
"HTTP-Referer": getSetting(runtime, "OPENROUTER_HTTP_REFERER") || "",
|
|
594
|
+
"X-Title": getSetting(runtime, "OPENROUTER_X_TITLE") || "ElizaOS"
|
|
595
|
+
},
|
|
596
|
+
body: JSON.stringify({
|
|
597
|
+
model: embeddingModelName,
|
|
598
|
+
input: text
|
|
599
|
+
})
|
|
600
|
+
});
|
|
601
|
+
if (!response.ok) {
|
|
602
|
+
import_core8.logger.error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
603
|
+
throw new Error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
604
|
+
}
|
|
605
|
+
const data = await response.json();
|
|
606
|
+
if (!data?.data?.[0]?.embedding) {
|
|
607
|
+
import_core8.logger.error("API returned invalid structure");
|
|
608
|
+
throw new Error("API returned invalid structure");
|
|
609
|
+
}
|
|
610
|
+
const embedding = data.data[0].embedding;
|
|
611
|
+
if (!Array.isArray(embedding) || embedding.length !== embeddingDimension) {
|
|
612
|
+
const errorMsg = `Embedding length ${embedding?.length ?? 0} does not match configured dimension ${embeddingDimension}`;
|
|
613
|
+
import_core8.logger.error(errorMsg);
|
|
614
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
615
|
+
fallbackVector[0] = 0.4;
|
|
616
|
+
return fallbackVector;
|
|
617
|
+
}
|
|
618
|
+
if (data.usage) {
|
|
619
|
+
const usage = {
|
|
620
|
+
inputTokens: data.usage.prompt_tokens,
|
|
621
|
+
outputTokens: 0,
|
|
622
|
+
totalTokens: data.usage.total_tokens
|
|
623
|
+
};
|
|
624
|
+
emitModelUsageEvent(runtime, import_core8.ModelType.TEXT_EMBEDDING, text, usage);
|
|
625
|
+
}
|
|
626
|
+
import_core8.logger.log(`Got valid embedding with length ${embedding.length}`);
|
|
627
|
+
return embedding;
|
|
628
|
+
} catch (error) {
|
|
629
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
630
|
+
import_core8.logger.error(`Error generating embedding: ${message}`);
|
|
631
|
+
throw error instanceof Error ? error : new Error(message);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
603
635
|
// src/index.ts
|
|
604
636
|
var openrouterPlugin = {
|
|
605
637
|
name: "openrouter",
|
|
@@ -611,36 +643,163 @@ var openrouterPlugin = {
|
|
|
611
643
|
OPENROUTER_LARGE_MODEL: process.env.OPENROUTER_LARGE_MODEL,
|
|
612
644
|
OPENROUTER_IMAGE_MODEL: process.env.OPENROUTER_IMAGE_MODEL,
|
|
613
645
|
OPENROUTER_IMAGE_GENERATION_MODEL: process.env.OPENROUTER_IMAGE_GENERATION_MODEL,
|
|
646
|
+
OPENROUTER_EMBEDDING_MODEL: process.env.OPENROUTER_EMBEDDING_MODEL,
|
|
647
|
+
OPENROUTER_EMBEDDING_DIMENSIONS: process.env.OPENROUTER_EMBEDDING_DIMENSIONS,
|
|
614
648
|
OPENROUTER_AUTO_CLEANUP_IMAGES: process.env.OPENROUTER_AUTO_CLEANUP_IMAGES,
|
|
615
649
|
SMALL_MODEL: process.env.SMALL_MODEL,
|
|
616
650
|
LARGE_MODEL: process.env.LARGE_MODEL,
|
|
617
651
|
IMAGE_MODEL: process.env.IMAGE_MODEL,
|
|
618
|
-
IMAGE_GENERATION_MODEL: process.env.IMAGE_GENERATION_MODEL
|
|
652
|
+
IMAGE_GENERATION_MODEL: process.env.IMAGE_GENERATION_MODEL,
|
|
653
|
+
EMBEDDING_MODEL: process.env.EMBEDDING_MODEL,
|
|
654
|
+
EMBEDDING_DIMENSIONS: process.env.EMBEDDING_DIMENSIONS
|
|
619
655
|
},
|
|
620
656
|
async init(config, runtime) {
|
|
621
657
|
initializeOpenRouter(config, runtime);
|
|
622
658
|
},
|
|
623
659
|
models: {
|
|
624
|
-
[
|
|
660
|
+
[import_core9.ModelType.TEXT_SMALL]: async (runtime, params) => {
|
|
625
661
|
return handleTextSmall(runtime, params);
|
|
626
662
|
},
|
|
627
|
-
[
|
|
663
|
+
[import_core9.ModelType.TEXT_LARGE]: async (runtime, params) => {
|
|
628
664
|
return handleTextLarge(runtime, params);
|
|
629
665
|
},
|
|
630
|
-
[
|
|
666
|
+
[import_core9.ModelType.OBJECT_SMALL]: async (runtime, params) => {
|
|
631
667
|
return handleObjectSmall(runtime, params);
|
|
632
668
|
},
|
|
633
|
-
[
|
|
669
|
+
[import_core9.ModelType.OBJECT_LARGE]: async (runtime, params) => {
|
|
634
670
|
return handleObjectLarge(runtime, params);
|
|
635
671
|
},
|
|
636
|
-
[
|
|
672
|
+
[import_core9.ModelType.IMAGE_DESCRIPTION]: async (runtime, params) => {
|
|
637
673
|
return handleImageDescription(runtime, params);
|
|
638
674
|
},
|
|
639
|
-
[
|
|
675
|
+
[import_core9.ModelType.IMAGE]: async (runtime, params) => {
|
|
640
676
|
return handleImageGeneration(runtime, params);
|
|
677
|
+
},
|
|
678
|
+
[import_core9.ModelType.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
679
|
+
return handleTextEmbedding(runtime, params);
|
|
641
680
|
}
|
|
642
|
-
}
|
|
681
|
+
},
|
|
682
|
+
tests: [
|
|
683
|
+
{
|
|
684
|
+
name: "openrouter_plugin_tests",
|
|
685
|
+
tests: [
|
|
686
|
+
{
|
|
687
|
+
name: "openrouter_test_text_small",
|
|
688
|
+
fn: async (runtime) => {
|
|
689
|
+
try {
|
|
690
|
+
const text = await runtime.useModel(import_core9.ModelType.TEXT_SMALL, {
|
|
691
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
692
|
+
});
|
|
693
|
+
if (text.length === 0) {
|
|
694
|
+
throw new Error("Failed to generate text");
|
|
695
|
+
}
|
|
696
|
+
import_core9.logger.log({ text }, "generated with test_text_small");
|
|
697
|
+
} catch (error) {
|
|
698
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
699
|
+
import_core9.logger.error(`Error in test_text_small: ${message}`);
|
|
700
|
+
throw error;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
name: "openrouter_test_text_large",
|
|
706
|
+
fn: async (runtime) => {
|
|
707
|
+
try {
|
|
708
|
+
const text = await runtime.useModel(import_core9.ModelType.TEXT_LARGE, {
|
|
709
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
710
|
+
});
|
|
711
|
+
if (text.length === 0) {
|
|
712
|
+
throw new Error("Failed to generate text");
|
|
713
|
+
}
|
|
714
|
+
import_core9.logger.log({ text }, "generated with test_text_large");
|
|
715
|
+
} catch (error) {
|
|
716
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
717
|
+
import_core9.logger.error(`Error in test_text_large: ${message}`);
|
|
718
|
+
throw error;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: "openrouter_test_text_generation_large",
|
|
724
|
+
fn: async (runtime) => {
|
|
725
|
+
try {
|
|
726
|
+
const result = await runtime.useModel(import_core9.ModelType.TEXT_LARGE, {
|
|
727
|
+
prompt: "Say hello in 5 words."
|
|
728
|
+
});
|
|
729
|
+
if (!result || result.length === 0) {
|
|
730
|
+
throw new Error("Text generation returned empty result");
|
|
731
|
+
}
|
|
732
|
+
import_core9.logger.log({ result }, "Text generation test completed");
|
|
733
|
+
} catch (error) {
|
|
734
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
735
|
+
import_core9.logger.error(`Error in openrouter_test_text_generation_large: ${message}`);
|
|
736
|
+
throw error;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
name: "openrouter_test_streaming",
|
|
742
|
+
fn: async (runtime) => {
|
|
743
|
+
try {
|
|
744
|
+
const chunks = [];
|
|
745
|
+
const result = await runtime.useModel(import_core9.ModelType.TEXT_LARGE, {
|
|
746
|
+
prompt: "Count from 1 to 5.",
|
|
747
|
+
onStreamChunk: (chunk) => {
|
|
748
|
+
chunks.push(chunk);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
if (!result || result.length === 0) {
|
|
752
|
+
throw new Error("Streaming returned empty result");
|
|
753
|
+
}
|
|
754
|
+
if (chunks.length === 0) {
|
|
755
|
+
throw new Error("No streaming chunks received");
|
|
756
|
+
}
|
|
757
|
+
import_core9.logger.log({ chunks: chunks.length, result: result.substring(0, 50) }, "Streaming test completed");
|
|
758
|
+
} catch (error) {
|
|
759
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
760
|
+
import_core9.logger.error(`Error in openrouter_test_streaming: ${message}`);
|
|
761
|
+
throw error;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
name: "openrouter_test_object_small",
|
|
767
|
+
fn: async (runtime) => {
|
|
768
|
+
try {
|
|
769
|
+
const result = await runtime.useModel(import_core9.ModelType.OBJECT_SMALL, {
|
|
770
|
+
prompt: "Create a simple JSON object with a message field saying hello",
|
|
771
|
+
schema: { type: "object" }
|
|
772
|
+
});
|
|
773
|
+
import_core9.logger.log({ result }, "Generated object with test_object_small");
|
|
774
|
+
if (!result || typeof result === "object" && "error" in result) {
|
|
775
|
+
throw new Error("Failed to generate object");
|
|
776
|
+
}
|
|
777
|
+
} catch (error) {
|
|
778
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
779
|
+
import_core9.logger.error(`Error in test_object_small: ${message}`);
|
|
780
|
+
throw error;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
name: "openrouter_test_text_embedding",
|
|
786
|
+
fn: async (runtime) => {
|
|
787
|
+
try {
|
|
788
|
+
const embedding = await runtime.useModel(import_core9.ModelType.TEXT_EMBEDDING, {
|
|
789
|
+
text: "Hello, world!"
|
|
790
|
+
});
|
|
791
|
+
import_core9.logger.log({ embedding }, "embedding");
|
|
792
|
+
} catch (error) {
|
|
793
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
794
|
+
import_core9.logger.error(`Error in test_text_embedding: ${message}`);
|
|
795
|
+
throw error;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
]
|
|
800
|
+
}
|
|
801
|
+
]
|
|
643
802
|
};
|
|
644
803
|
var src_default = openrouterPlugin;
|
|
645
804
|
|
|
646
|
-
//# debugId=
|
|
805
|
+
//# debugId=65CA0E3B77F6CA5664756E2164756E21
|