@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
|
@@ -53,7 +53,7 @@ __export(exports_path, {
|
|
|
53
53
|
});
|
|
54
54
|
function assertPath(path) {
|
|
55
55
|
if (typeof path !== "string")
|
|
56
|
-
throw
|
|
56
|
+
throw TypeError("Path must be a string. Received " + JSON.stringify(path));
|
|
57
57
|
}
|
|
58
58
|
function normalizeStringPosix(path, allowAboveRoot) {
|
|
59
59
|
var res = "", lastSegmentLength = 0, lastSlash = -1, dots = 0, code;
|
|
@@ -243,7 +243,7 @@ function dirname(path) {
|
|
|
243
243
|
}
|
|
244
244
|
function basename(path, ext) {
|
|
245
245
|
if (ext !== undefined && typeof ext !== "string")
|
|
246
|
-
throw
|
|
246
|
+
throw TypeError('"ext" argument must be a string');
|
|
247
247
|
assertPath(path);
|
|
248
248
|
var start = 0, end = -1, matchedSlash = true, i;
|
|
249
249
|
if (ext !== undefined && ext.length > 0 && ext.length <= path.length) {
|
|
@@ -315,7 +315,7 @@ function extname(path) {
|
|
|
315
315
|
}
|
|
316
316
|
function format(pathObject) {
|
|
317
317
|
if (pathObject === null || typeof pathObject !== "object")
|
|
318
|
-
throw
|
|
318
|
+
throw TypeError('The "pathObject" argument must be of type Object. Received type ' + typeof pathObject);
|
|
319
319
|
return _format("/", pathObject);
|
|
320
320
|
}
|
|
321
321
|
function parse(path) {
|
|
@@ -374,7 +374,8 @@ var init_path = __esm(() => {
|
|
|
374
374
|
|
|
375
375
|
// src/index.ts
|
|
376
376
|
import {
|
|
377
|
-
ModelType as
|
|
377
|
+
ModelType as ModelType4,
|
|
378
|
+
logger as logger8
|
|
378
379
|
} from "@elizaos/core";
|
|
379
380
|
|
|
380
381
|
// src/init.ts
|
|
@@ -383,7 +384,11 @@ import { fetch as fetch2 } from "undici";
|
|
|
383
384
|
|
|
384
385
|
// src/utils/config.ts
|
|
385
386
|
function getSetting(runtime, key, defaultValue) {
|
|
386
|
-
|
|
387
|
+
const value = runtime.getSetting(key);
|
|
388
|
+
if (value !== undefined && value !== null) {
|
|
389
|
+
return String(value);
|
|
390
|
+
}
|
|
391
|
+
return process.env[key] ?? defaultValue;
|
|
387
392
|
}
|
|
388
393
|
function getBaseURL(runtime) {
|
|
389
394
|
const browserURL = getSetting(runtime, "OPENROUTER_BROWSER_BASE_URL");
|
|
@@ -407,19 +412,13 @@ function getImageModel(runtime) {
|
|
|
407
412
|
function getImageGenerationModel(runtime) {
|
|
408
413
|
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";
|
|
409
414
|
}
|
|
415
|
+
function getEmbeddingModel(runtime) {
|
|
416
|
+
return getSetting(runtime, "OPENROUTER_EMBEDDING_MODEL") ?? getSetting(runtime, "EMBEDDING_MODEL", "openai/text-embedding-3-small") ?? "openai/text-embedding-3-small";
|
|
417
|
+
}
|
|
410
418
|
function shouldAutoCleanupImages(runtime) {
|
|
411
419
|
const setting = getSetting(runtime, "OPENROUTER_AUTO_CLEANUP_IMAGES", "false");
|
|
412
420
|
return setting?.toLowerCase() === "true";
|
|
413
421
|
}
|
|
414
|
-
function getToolExecutionMaxSteps(runtime) {
|
|
415
|
-
const setting = getSetting(runtime, "OPENROUTER_TOOL_EXECUTION_MAX_STEPS", "15");
|
|
416
|
-
const value = parseInt(setting || "15", 10);
|
|
417
|
-
if (Number.isNaN(value) || value < 1)
|
|
418
|
-
return 15;
|
|
419
|
-
if (value > 100)
|
|
420
|
-
return 100;
|
|
421
|
-
return value;
|
|
422
|
-
}
|
|
423
422
|
|
|
424
423
|
// src/init.ts
|
|
425
424
|
function initializeOpenRouter(_config, runtime) {
|
|
@@ -458,8 +457,8 @@ function initializeOpenRouter(_config, runtime) {
|
|
|
458
457
|
}
|
|
459
458
|
|
|
460
459
|
// src/models/text.ts
|
|
461
|
-
import { logger as
|
|
462
|
-
import { generateText,
|
|
460
|
+
import { logger as logger2, ModelType } from "@elizaos/core";
|
|
461
|
+
import { generateText, streamText } from "ai";
|
|
463
462
|
|
|
464
463
|
// src/providers/openrouter.ts
|
|
465
464
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
@@ -482,6 +481,8 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
482
481
|
const outputTokens = Number(usage.outputTokens || 0);
|
|
483
482
|
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
484
483
|
runtime.emitEvent(EventType.MODEL_USED, {
|
|
484
|
+
runtime,
|
|
485
|
+
source: "openrouter",
|
|
485
486
|
provider: "openrouter",
|
|
486
487
|
type,
|
|
487
488
|
prompt: truncatedPrompt,
|
|
@@ -493,8 +494,78 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
493
494
|
});
|
|
494
495
|
}
|
|
495
496
|
|
|
497
|
+
// src/models/text.ts
|
|
498
|
+
function buildGenerateParams(runtime, modelType, params) {
|
|
499
|
+
const { prompt, stopSequences = [] } = params;
|
|
500
|
+
const temperature = params.temperature ?? 0.7;
|
|
501
|
+
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
502
|
+
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
503
|
+
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
504
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
505
|
+
const modelName = modelType === ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
506
|
+
const modelLabel = modelType === ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
507
|
+
const generateParams = {
|
|
508
|
+
model: openrouter.chat(modelName),
|
|
509
|
+
prompt,
|
|
510
|
+
system: runtime.character.system ?? undefined,
|
|
511
|
+
temperature,
|
|
512
|
+
frequencyPenalty,
|
|
513
|
+
presencePenalty,
|
|
514
|
+
stopSequences
|
|
515
|
+
};
|
|
516
|
+
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
517
|
+
return { generateParams, modelName, modelLabel, prompt };
|
|
518
|
+
}
|
|
519
|
+
function handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelLabel) {
|
|
520
|
+
logger2.debug(`[OpenRouter] Streaming text with ${modelLabel} model`);
|
|
521
|
+
const streamResult = streamText(generateParams);
|
|
522
|
+
return {
|
|
523
|
+
textStream: streamResult.textStream,
|
|
524
|
+
text: streamResult.text,
|
|
525
|
+
usage: streamResult.usage.then((usage) => {
|
|
526
|
+
if (usage) {
|
|
527
|
+
emitModelUsageEvent(runtime, modelType, prompt, usage);
|
|
528
|
+
const inputTokens = usage.inputTokens ?? 0;
|
|
529
|
+
const outputTokens = usage.outputTokens ?? 0;
|
|
530
|
+
return {
|
|
531
|
+
promptTokens: inputTokens,
|
|
532
|
+
completionTokens: outputTokens,
|
|
533
|
+
totalTokens: inputTokens + outputTokens
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
return;
|
|
537
|
+
}),
|
|
538
|
+
finishReason: streamResult.finishReason
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
async function generateTextWithModel(runtime, modelType, params) {
|
|
542
|
+
const { generateParams, modelName, modelLabel, prompt } = buildGenerateParams(runtime, modelType, params);
|
|
543
|
+
logger2.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
544
|
+
if (params.stream) {
|
|
545
|
+
return handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelLabel);
|
|
546
|
+
}
|
|
547
|
+
const response = await generateText(generateParams);
|
|
548
|
+
if (response.usage) {
|
|
549
|
+
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
550
|
+
}
|
|
551
|
+
return response.text;
|
|
552
|
+
}
|
|
553
|
+
async function handleTextSmall(runtime, params) {
|
|
554
|
+
return generateTextWithModel(runtime, ModelType.TEXT_SMALL, params);
|
|
555
|
+
}
|
|
556
|
+
async function handleTextLarge(runtime, params) {
|
|
557
|
+
return generateTextWithModel(runtime, ModelType.TEXT_LARGE, params);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// src/models/object.ts
|
|
561
|
+
import {
|
|
562
|
+
ModelType as ModelType2,
|
|
563
|
+
logger as logger4
|
|
564
|
+
} from "@elizaos/core";
|
|
565
|
+
import { generateObject, jsonSchema } from "ai";
|
|
566
|
+
|
|
496
567
|
// src/utils/helpers.ts
|
|
497
|
-
import { logger as
|
|
568
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
498
569
|
import { JSONParseError } from "ai";
|
|
499
570
|
function getJsonRepairFunction() {
|
|
500
571
|
return async ({ text, error }) => {
|
|
@@ -507,17 +578,11 @@ function getJsonRepairFunction() {
|
|
|
507
578
|
return null;
|
|
508
579
|
} catch (jsonError) {
|
|
509
580
|
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
510
|
-
|
|
581
|
+
logger3.warn(`Failed to repair JSON text: ${message}`);
|
|
511
582
|
return null;
|
|
512
583
|
}
|
|
513
584
|
};
|
|
514
585
|
}
|
|
515
|
-
function handleEmptyToolResponse(modelType) {
|
|
516
|
-
logger2.warn(`[${modelType}] No text generated after tool execution`);
|
|
517
|
-
const fallbackText = "I executed the requested action. The tool completed successfully.";
|
|
518
|
-
logger2.warn(`[${modelType}] Using fallback response text`);
|
|
519
|
-
return fallbackText;
|
|
520
|
-
}
|
|
521
586
|
function parseImageDescriptionResponse(responseText) {
|
|
522
587
|
try {
|
|
523
588
|
const jsonResponse = JSON.parse(responseText);
|
|
@@ -525,7 +590,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
525
590
|
return jsonResponse;
|
|
526
591
|
}
|
|
527
592
|
} catch (e) {
|
|
528
|
-
|
|
593
|
+
logger3.debug(`Parsing as JSON failed, processing as text: ${e}`);
|
|
529
594
|
}
|
|
530
595
|
const titleMatch = responseText.match(/title[:\s]+(.+?)(?:\n|$)/i);
|
|
531
596
|
const title = titleMatch?.[1]?.trim() || "Image Analysis";
|
|
@@ -534,7 +599,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
534
599
|
}
|
|
535
600
|
async function handleObjectGenerationError(error) {
|
|
536
601
|
if (error instanceof JSONParseError) {
|
|
537
|
-
|
|
602
|
+
logger3.error(`[generateObject] Failed to parse JSON: ${error.message}`);
|
|
538
603
|
const repairFunction = getJsonRepairFunction();
|
|
539
604
|
const repairedJsonString = await repairFunction({
|
|
540
605
|
text: error.text,
|
|
@@ -543,151 +608,29 @@ async function handleObjectGenerationError(error) {
|
|
|
543
608
|
if (repairedJsonString) {
|
|
544
609
|
try {
|
|
545
610
|
const repairedObject = JSON.parse(repairedJsonString);
|
|
546
|
-
|
|
611
|
+
logger3.log("[generateObject] Successfully repaired JSON.");
|
|
547
612
|
return repairedObject;
|
|
548
613
|
} catch (repairParseError) {
|
|
549
614
|
const message = repairParseError instanceof Error ? repairParseError.message : String(repairParseError);
|
|
550
|
-
|
|
615
|
+
logger3.error(`[generateObject] Failed to parse repaired JSON: ${message}`);
|
|
551
616
|
if (repairParseError instanceof Error)
|
|
552
617
|
throw repairParseError;
|
|
553
618
|
throw Object.assign(new Error(message), { cause: repairParseError });
|
|
554
619
|
}
|
|
555
620
|
} else {
|
|
556
|
-
|
|
621
|
+
logger3.error("[generateObject] JSON repair failed.");
|
|
557
622
|
throw error;
|
|
558
623
|
}
|
|
559
624
|
} else {
|
|
560
625
|
const message = error instanceof Error ? error.message : String(error);
|
|
561
|
-
|
|
626
|
+
logger3.error(`[generateObject] Unknown error: ${message}`);
|
|
562
627
|
if (error instanceof Error)
|
|
563
628
|
throw error;
|
|
564
629
|
throw Object.assign(new Error(message), { cause: error });
|
|
565
630
|
}
|
|
566
631
|
}
|
|
567
|
-
function isLikelyBase64(key, value) {
|
|
568
|
-
const base64KeyPattern = /^(data|content|body|payload|encoded|b64|base64|document)$/i;
|
|
569
|
-
if (!base64KeyPattern.test(key))
|
|
570
|
-
return false;
|
|
571
|
-
if (value.length < 20 || value.length > 1024 * 1024)
|
|
572
|
-
return false;
|
|
573
|
-
if (value.length % 4 !== 0)
|
|
574
|
-
return false;
|
|
575
|
-
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
576
|
-
return false;
|
|
577
|
-
return true;
|
|
578
|
-
}
|
|
579
|
-
function decodeBase64Fields(obj, depth = 0) {
|
|
580
|
-
if (depth > 5)
|
|
581
|
-
return obj;
|
|
582
|
-
if (!obj || typeof obj !== "object")
|
|
583
|
-
return obj;
|
|
584
|
-
if (Array.isArray(obj))
|
|
585
|
-
return obj.map((item) => decodeBase64Fields(item, depth + 1));
|
|
586
|
-
const decoded = {};
|
|
587
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
588
|
-
if (typeof value === "string" && isLikelyBase64(key, value)) {
|
|
589
|
-
try {
|
|
590
|
-
decoded[key] = Buffer.from(value, "base64").toString("utf8");
|
|
591
|
-
logger2.debug(`[decodeBase64] Decoded field '${key}' (${value.length} chars)`);
|
|
592
|
-
} catch (error) {
|
|
593
|
-
logger2.warn(`[decodeBase64] Failed to decode field '${key}': ${error}`);
|
|
594
|
-
decoded[key] = value;
|
|
595
|
-
}
|
|
596
|
-
} else if (value && typeof value === "object") {
|
|
597
|
-
decoded[key] = decodeBase64Fields(value, depth + 1);
|
|
598
|
-
} else {
|
|
599
|
-
decoded[key] = value;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
return decoded;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// src/models/text.ts
|
|
606
|
-
async function generateTextWithModel(runtime, modelType, params) {
|
|
607
|
-
const { prompt, stopSequences = [], tools, toolChoice } = params;
|
|
608
|
-
const temperature = params.temperature ?? 0.7;
|
|
609
|
-
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
610
|
-
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
611
|
-
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
612
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
613
|
-
const modelName = modelType === ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
614
|
-
const modelLabel = modelType === ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
615
|
-
logger3.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
616
|
-
const generateParams = {
|
|
617
|
-
model: openrouter.chat(modelName),
|
|
618
|
-
prompt,
|
|
619
|
-
system: runtime.character.system ?? undefined,
|
|
620
|
-
temperature,
|
|
621
|
-
frequencyPenalty,
|
|
622
|
-
presencePenalty,
|
|
623
|
-
stopSequences
|
|
624
|
-
};
|
|
625
|
-
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
626
|
-
if (tools) {
|
|
627
|
-
generateParams.tools = tools;
|
|
628
|
-
const maxSteps = getToolExecutionMaxSteps(runtime);
|
|
629
|
-
generateParams.stopWhen = stepCountIs(maxSteps);
|
|
630
|
-
logger3.debug(`[OpenRouter] Using maxSteps: ${maxSteps} for tool execution`);
|
|
631
|
-
}
|
|
632
|
-
if (toolChoice) {
|
|
633
|
-
generateParams.toolChoice = toolChoice;
|
|
634
|
-
}
|
|
635
|
-
let capturedToolResults = [];
|
|
636
|
-
let capturedToolCalls = [];
|
|
637
|
-
if (tools) {
|
|
638
|
-
generateParams.onStepFinish = async (stepResult) => {
|
|
639
|
-
if (stepResult.toolCalls && stepResult.toolCalls.length > 0) {
|
|
640
|
-
capturedToolCalls = [
|
|
641
|
-
...capturedToolCalls,
|
|
642
|
-
...stepResult.toolCalls
|
|
643
|
-
];
|
|
644
|
-
}
|
|
645
|
-
if (stepResult.content && Array.isArray(stepResult.content)) {
|
|
646
|
-
const toolResultsFromContent = stepResult.content.filter((content) => content.type === "tool-result" && content.output).map((content) => ({
|
|
647
|
-
toolCallId: content.toolCallId,
|
|
648
|
-
result: decodeBase64Fields(content.output)
|
|
649
|
-
}));
|
|
650
|
-
if (toolResultsFromContent.length > 0) {
|
|
651
|
-
capturedToolResults = [...capturedToolResults, ...toolResultsFromContent];
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
const response = await generateText(generateParams);
|
|
657
|
-
let responseText;
|
|
658
|
-
if (tools && (!response.text || response.text.trim() === "" || response.text === "Tools executed successfully.")) {
|
|
659
|
-
responseText = handleEmptyToolResponse(modelLabel);
|
|
660
|
-
} else {
|
|
661
|
-
responseText = response.text;
|
|
662
|
-
}
|
|
663
|
-
if (response.usage) {
|
|
664
|
-
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
665
|
-
}
|
|
666
|
-
if (tools && response.steps && response.steps.length > 0) {
|
|
667
|
-
return {
|
|
668
|
-
text: responseText,
|
|
669
|
-
toolCalls: capturedToolCalls,
|
|
670
|
-
toolResults: capturedToolResults,
|
|
671
|
-
steps: response.steps,
|
|
672
|
-
usage: response.usage,
|
|
673
|
-
finishReason: response.finishReason
|
|
674
|
-
};
|
|
675
|
-
}
|
|
676
|
-
return responseText;
|
|
677
|
-
}
|
|
678
|
-
async function handleTextSmall(runtime, params) {
|
|
679
|
-
return generateTextWithModel(runtime, ModelType.TEXT_SMALL, params);
|
|
680
|
-
}
|
|
681
|
-
async function handleTextLarge(runtime, params) {
|
|
682
|
-
return generateTextWithModel(runtime, ModelType.TEXT_LARGE, params);
|
|
683
|
-
}
|
|
684
632
|
|
|
685
633
|
// src/models/object.ts
|
|
686
|
-
import {
|
|
687
|
-
ModelType as ModelType2,
|
|
688
|
-
logger as logger4
|
|
689
|
-
} from "@elizaos/core";
|
|
690
|
-
import { generateObject } from "ai";
|
|
691
634
|
async function generateObjectWithModel(runtime, modelType, params) {
|
|
692
635
|
const openrouter = createOpenRouterProvider(runtime);
|
|
693
636
|
const modelName = modelType === ModelType2.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
@@ -697,7 +640,7 @@ async function generateObjectWithModel(runtime, modelType, params) {
|
|
|
697
640
|
try {
|
|
698
641
|
const { object, usage } = await generateObject({
|
|
699
642
|
model: openrouter.chat(modelName),
|
|
700
|
-
...params.schema && { schema: params.schema },
|
|
643
|
+
...params.schema && { schema: jsonSchema(params.schema) },
|
|
701
644
|
output: params.schema ? "object" : "no-schema",
|
|
702
645
|
prompt: params.prompt,
|
|
703
646
|
temperature,
|
|
@@ -934,6 +877,96 @@ async function handleImageGeneration(runtime, params) {
|
|
|
934
877
|
}
|
|
935
878
|
}
|
|
936
879
|
|
|
880
|
+
// src/models/embedding.ts
|
|
881
|
+
import { logger as logger7, ModelType as ModelType3, VECTOR_DIMS } from "@elizaos/core";
|
|
882
|
+
async function handleTextEmbedding(runtime, params) {
|
|
883
|
+
const embeddingModelName = getEmbeddingModel(runtime);
|
|
884
|
+
const embeddingDimension = Number.parseInt(getSetting(runtime, "OPENROUTER_EMBEDDING_DIMENSIONS") ?? getSetting(runtime, "EMBEDDING_DIMENSIONS") ?? "1536", 10);
|
|
885
|
+
if (!Object.values(VECTOR_DIMS).includes(embeddingDimension)) {
|
|
886
|
+
const errorMsg = `Invalid embedding dimension: ${embeddingDimension}. Must be one of: ${Object.values(VECTOR_DIMS).join(", ")}`;
|
|
887
|
+
logger7.error(errorMsg);
|
|
888
|
+
throw new Error(errorMsg);
|
|
889
|
+
}
|
|
890
|
+
if (params === null) {
|
|
891
|
+
logger7.debug("Creating test embedding for initialization");
|
|
892
|
+
const testVector = Array(embeddingDimension).fill(0);
|
|
893
|
+
testVector[0] = 0.1;
|
|
894
|
+
return testVector;
|
|
895
|
+
}
|
|
896
|
+
let text;
|
|
897
|
+
if (typeof params === "string") {
|
|
898
|
+
text = params;
|
|
899
|
+
} else if (typeof params === "object" && params.text) {
|
|
900
|
+
text = params.text;
|
|
901
|
+
} else {
|
|
902
|
+
const errorMsg = "Invalid input format for embedding";
|
|
903
|
+
logger7.warn(errorMsg);
|
|
904
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
905
|
+
fallbackVector[0] = 0.2;
|
|
906
|
+
return fallbackVector;
|
|
907
|
+
}
|
|
908
|
+
if (!text.trim()) {
|
|
909
|
+
const errorMsg = "Empty text for embedding";
|
|
910
|
+
logger7.warn(errorMsg);
|
|
911
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
912
|
+
fallbackVector[0] = 0.3;
|
|
913
|
+
return fallbackVector;
|
|
914
|
+
}
|
|
915
|
+
const apiKey = getApiKey(runtime);
|
|
916
|
+
if (!apiKey) {
|
|
917
|
+
const errorMsg = "OPENROUTER_API_KEY is not set";
|
|
918
|
+
logger7.error(errorMsg);
|
|
919
|
+
throw new Error(errorMsg);
|
|
920
|
+
}
|
|
921
|
+
const baseURL = getBaseURL(runtime);
|
|
922
|
+
try {
|
|
923
|
+
const response = await fetch(`${baseURL}/embeddings`, {
|
|
924
|
+
method: "POST",
|
|
925
|
+
headers: {
|
|
926
|
+
Authorization: `Bearer ${apiKey}`,
|
|
927
|
+
"Content-Type": "application/json",
|
|
928
|
+
"HTTP-Referer": getSetting(runtime, "OPENROUTER_HTTP_REFERER") || "",
|
|
929
|
+
"X-Title": getSetting(runtime, "OPENROUTER_X_TITLE") || "ElizaOS"
|
|
930
|
+
},
|
|
931
|
+
body: JSON.stringify({
|
|
932
|
+
model: embeddingModelName,
|
|
933
|
+
input: text
|
|
934
|
+
})
|
|
935
|
+
});
|
|
936
|
+
if (!response.ok) {
|
|
937
|
+
logger7.error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
938
|
+
throw new Error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
939
|
+
}
|
|
940
|
+
const data = await response.json();
|
|
941
|
+
if (!data?.data?.[0]?.embedding) {
|
|
942
|
+
logger7.error("API returned invalid structure");
|
|
943
|
+
throw new Error("API returned invalid structure");
|
|
944
|
+
}
|
|
945
|
+
const embedding = data.data[0].embedding;
|
|
946
|
+
if (!Array.isArray(embedding) || embedding.length !== embeddingDimension) {
|
|
947
|
+
const errorMsg = `Embedding length ${embedding?.length ?? 0} does not match configured dimension ${embeddingDimension}`;
|
|
948
|
+
logger7.error(errorMsg);
|
|
949
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
950
|
+
fallbackVector[0] = 0.4;
|
|
951
|
+
return fallbackVector;
|
|
952
|
+
}
|
|
953
|
+
if (data.usage) {
|
|
954
|
+
const usage = {
|
|
955
|
+
inputTokens: data.usage.prompt_tokens,
|
|
956
|
+
outputTokens: 0,
|
|
957
|
+
totalTokens: data.usage.total_tokens
|
|
958
|
+
};
|
|
959
|
+
emitModelUsageEvent(runtime, ModelType3.TEXT_EMBEDDING, text, usage);
|
|
960
|
+
}
|
|
961
|
+
logger7.log(`Got valid embedding with length ${embedding.length}`);
|
|
962
|
+
return embedding;
|
|
963
|
+
} catch (error) {
|
|
964
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
965
|
+
logger7.error(`Error generating embedding: ${message}`);
|
|
966
|
+
throw error instanceof Error ? error : new Error(message);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
937
970
|
// src/index.ts
|
|
938
971
|
var openrouterPlugin = {
|
|
939
972
|
name: "openrouter",
|
|
@@ -945,35 +978,162 @@ var openrouterPlugin = {
|
|
|
945
978
|
OPENROUTER_LARGE_MODEL: process.env.OPENROUTER_LARGE_MODEL,
|
|
946
979
|
OPENROUTER_IMAGE_MODEL: process.env.OPENROUTER_IMAGE_MODEL,
|
|
947
980
|
OPENROUTER_IMAGE_GENERATION_MODEL: process.env.OPENROUTER_IMAGE_GENERATION_MODEL,
|
|
981
|
+
OPENROUTER_EMBEDDING_MODEL: process.env.OPENROUTER_EMBEDDING_MODEL,
|
|
982
|
+
OPENROUTER_EMBEDDING_DIMENSIONS: process.env.OPENROUTER_EMBEDDING_DIMENSIONS,
|
|
948
983
|
OPENROUTER_AUTO_CLEANUP_IMAGES: process.env.OPENROUTER_AUTO_CLEANUP_IMAGES,
|
|
949
984
|
SMALL_MODEL: process.env.SMALL_MODEL,
|
|
950
985
|
LARGE_MODEL: process.env.LARGE_MODEL,
|
|
951
986
|
IMAGE_MODEL: process.env.IMAGE_MODEL,
|
|
952
|
-
IMAGE_GENERATION_MODEL: process.env.IMAGE_GENERATION_MODEL
|
|
987
|
+
IMAGE_GENERATION_MODEL: process.env.IMAGE_GENERATION_MODEL,
|
|
988
|
+
EMBEDDING_MODEL: process.env.EMBEDDING_MODEL,
|
|
989
|
+
EMBEDDING_DIMENSIONS: process.env.EMBEDDING_DIMENSIONS
|
|
953
990
|
},
|
|
954
991
|
async init(config, runtime) {
|
|
955
992
|
initializeOpenRouter(config, runtime);
|
|
956
993
|
},
|
|
957
994
|
models: {
|
|
958
|
-
[
|
|
995
|
+
[ModelType4.TEXT_SMALL]: async (runtime, params) => {
|
|
959
996
|
return handleTextSmall(runtime, params);
|
|
960
997
|
},
|
|
961
|
-
[
|
|
998
|
+
[ModelType4.TEXT_LARGE]: async (runtime, params) => {
|
|
962
999
|
return handleTextLarge(runtime, params);
|
|
963
1000
|
},
|
|
964
|
-
[
|
|
1001
|
+
[ModelType4.OBJECT_SMALL]: async (runtime, params) => {
|
|
965
1002
|
return handleObjectSmall(runtime, params);
|
|
966
1003
|
},
|
|
967
|
-
[
|
|
1004
|
+
[ModelType4.OBJECT_LARGE]: async (runtime, params) => {
|
|
968
1005
|
return handleObjectLarge(runtime, params);
|
|
969
1006
|
},
|
|
970
|
-
[
|
|
1007
|
+
[ModelType4.IMAGE_DESCRIPTION]: async (runtime, params) => {
|
|
971
1008
|
return handleImageDescription(runtime, params);
|
|
972
1009
|
},
|
|
973
|
-
[
|
|
1010
|
+
[ModelType4.IMAGE]: async (runtime, params) => {
|
|
974
1011
|
return handleImageGeneration(runtime, params);
|
|
1012
|
+
},
|
|
1013
|
+
[ModelType4.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
1014
|
+
return handleTextEmbedding(runtime, params);
|
|
975
1015
|
}
|
|
976
|
-
}
|
|
1016
|
+
},
|
|
1017
|
+
tests: [
|
|
1018
|
+
{
|
|
1019
|
+
name: "openrouter_plugin_tests",
|
|
1020
|
+
tests: [
|
|
1021
|
+
{
|
|
1022
|
+
name: "openrouter_test_text_small",
|
|
1023
|
+
fn: async (runtime) => {
|
|
1024
|
+
try {
|
|
1025
|
+
const text = await runtime.useModel(ModelType4.TEXT_SMALL, {
|
|
1026
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
1027
|
+
});
|
|
1028
|
+
if (text.length === 0) {
|
|
1029
|
+
throw new Error("Failed to generate text");
|
|
1030
|
+
}
|
|
1031
|
+
logger8.log({ text }, "generated with test_text_small");
|
|
1032
|
+
} catch (error) {
|
|
1033
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1034
|
+
logger8.error(`Error in test_text_small: ${message}`);
|
|
1035
|
+
throw error;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
name: "openrouter_test_text_large",
|
|
1041
|
+
fn: async (runtime) => {
|
|
1042
|
+
try {
|
|
1043
|
+
const text = await runtime.useModel(ModelType4.TEXT_LARGE, {
|
|
1044
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
1045
|
+
});
|
|
1046
|
+
if (text.length === 0) {
|
|
1047
|
+
throw new Error("Failed to generate text");
|
|
1048
|
+
}
|
|
1049
|
+
logger8.log({ text }, "generated with test_text_large");
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1052
|
+
logger8.error(`Error in test_text_large: ${message}`);
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
name: "openrouter_test_text_generation_large",
|
|
1059
|
+
fn: async (runtime) => {
|
|
1060
|
+
try {
|
|
1061
|
+
const result = await runtime.useModel(ModelType4.TEXT_LARGE, {
|
|
1062
|
+
prompt: "Say hello in 5 words."
|
|
1063
|
+
});
|
|
1064
|
+
if (!result || result.length === 0) {
|
|
1065
|
+
throw new Error("Text generation returned empty result");
|
|
1066
|
+
}
|
|
1067
|
+
logger8.log({ result }, "Text generation test completed");
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1070
|
+
logger8.error(`Error in openrouter_test_text_generation_large: ${message}`);
|
|
1071
|
+
throw error;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
},
|
|
1075
|
+
{
|
|
1076
|
+
name: "openrouter_test_streaming",
|
|
1077
|
+
fn: async (runtime) => {
|
|
1078
|
+
try {
|
|
1079
|
+
const chunks = [];
|
|
1080
|
+
const result = await runtime.useModel(ModelType4.TEXT_LARGE, {
|
|
1081
|
+
prompt: "Count from 1 to 5.",
|
|
1082
|
+
onStreamChunk: (chunk) => {
|
|
1083
|
+
chunks.push(chunk);
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
if (!result || result.length === 0) {
|
|
1087
|
+
throw new Error("Streaming returned empty result");
|
|
1088
|
+
}
|
|
1089
|
+
if (chunks.length === 0) {
|
|
1090
|
+
throw new Error("No streaming chunks received");
|
|
1091
|
+
}
|
|
1092
|
+
logger8.log({ chunks: chunks.length, result: result.substring(0, 50) }, "Streaming test completed");
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1095
|
+
logger8.error(`Error in openrouter_test_streaming: ${message}`);
|
|
1096
|
+
throw error;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
},
|
|
1100
|
+
{
|
|
1101
|
+
name: "openrouter_test_object_small",
|
|
1102
|
+
fn: async (runtime) => {
|
|
1103
|
+
try {
|
|
1104
|
+
const result = await runtime.useModel(ModelType4.OBJECT_SMALL, {
|
|
1105
|
+
prompt: "Create a simple JSON object with a message field saying hello",
|
|
1106
|
+
schema: { type: "object" }
|
|
1107
|
+
});
|
|
1108
|
+
logger8.log({ result }, "Generated object with test_object_small");
|
|
1109
|
+
if (!result || typeof result === "object" && "error" in result) {
|
|
1110
|
+
throw new Error("Failed to generate object");
|
|
1111
|
+
}
|
|
1112
|
+
} catch (error) {
|
|
1113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1114
|
+
logger8.error(`Error in test_object_small: ${message}`);
|
|
1115
|
+
throw error;
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
name: "openrouter_test_text_embedding",
|
|
1121
|
+
fn: async (runtime) => {
|
|
1122
|
+
try {
|
|
1123
|
+
const embedding = await runtime.useModel(ModelType4.TEXT_EMBEDDING, {
|
|
1124
|
+
text: "Hello, world!"
|
|
1125
|
+
});
|
|
1126
|
+
logger8.log({ embedding }, "embedding");
|
|
1127
|
+
} catch (error) {
|
|
1128
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1129
|
+
logger8.error(`Error in test_text_embedding: ${message}`);
|
|
1130
|
+
throw error;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
]
|
|
1135
|
+
}
|
|
1136
|
+
]
|
|
977
1137
|
};
|
|
978
1138
|
var src_default = openrouterPlugin;
|
|
979
1139
|
export {
|
|
@@ -981,4 +1141,4 @@ export {
|
|
|
981
1141
|
src_default as default
|
|
982
1142
|
};
|
|
983
1143
|
|
|
984
|
-
//# debugId=
|
|
1144
|
+
//# debugId=2BBC786BCBA31E1064756E2164756E21
|