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