@elizaos/plugin-openrouter 1.5.15 → 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 +215 -155
- package/dist/browser/index.browser.js.map +10 -10
- package/dist/cjs/index.node.cjs +210 -151
- package/dist/cjs/index.node.js.map +9 -9
- package/dist/models/object.d.ts +2 -2
- package/dist/models/text.d.ts +11 -11
- package/dist/node/index.node.js +212 -152
- package/dist/node/index.node.js.map +9 -9
- package/dist/types/index.d.ts +0 -46
- package/dist/utils/config.d.ts +0 -7
- package/dist/utils/helpers.d.ts +1 -9
- package/package.json +31 -2
package/README.md
CHANGED
|
@@ -23,13 +23,17 @@ The plugin requires the OpenRouter API key and can be configured via environment
|
|
|
23
23
|
"OPENROUTER_SMALL_MODEL": "google/gemini-flash", // Optional: Overrides default small model
|
|
24
24
|
"OPENROUTER_LARGE_MODEL": "google/gemini-pro", // Optional: Overrides default large model
|
|
25
25
|
"OPENROUTER_IMAGE_MODEL": "x-ai/grok-2-vision-1212", // Optional: Overrides default image model
|
|
26
|
-
|
|
26
|
+
"OPENROUTER_IMAGE_GENERATION_MODEL": "google/gemini-2.5-flash-image-preview", // Optional: Overrides default image generation model
|
|
27
|
+
"OPENROUTER_EMBEDDING_MODEL": "openai/text-embedding-3-small", // Optional: Overrides default embedding model
|
|
28
|
+
"OPENROUTER_EMBEDDING_DIMENSIONS": "1536", // Optional: Sets embedding vector dimensions (256, 384, 512, 768, 1024, 1536, 2048, 3072)
|
|
27
29
|
"OPENROUTER_BROWSER_BASE_URL": "https://your-proxy.example.com/openrouter"
|
|
28
30
|
// Fallbacks if specific OPENROUTER models are not set
|
|
29
31
|
"SMALL_MODEL": "google/gemini-flash",
|
|
30
32
|
"LARGE_MODEL": "google/gemini-pro",
|
|
31
33
|
"IMAGE_MODEL": "x-ai/grok-2-vision-1212",
|
|
32
|
-
"IMAGE_GENERATION_MODEL": "google/gemini-2.5-flash-image-preview"
|
|
34
|
+
"IMAGE_GENERATION_MODEL": "google/gemini-2.5-flash-image-preview",
|
|
35
|
+
"EMBEDDING_MODEL": "openai/text-embedding-3-small",
|
|
36
|
+
"EMBEDDING_DIMENSIONS": "1536"
|
|
33
37
|
}
|
|
34
38
|
```
|
|
35
39
|
|
|
@@ -43,6 +47,8 @@ OPENROUTER_SMALL_MODEL=google/gemini-flash
|
|
|
43
47
|
OPENROUTER_LARGE_MODEL=google/gemini-pro
|
|
44
48
|
OPENROUTER_IMAGE_MODEL=x-ai/grok-2-vision-1212
|
|
45
49
|
OPENROUTER_IMAGE_GENERATION_MODEL=google/gemini-2.5-flash-image-preview
|
|
50
|
+
OPENROUTER_EMBEDDING_MODEL=openai/text-embedding-3-small
|
|
51
|
+
OPENROUTER_EMBEDDING_DIMENSIONS=1536
|
|
46
52
|
# Browser proxy (frontend builds only)
|
|
47
53
|
OPENROUTER_BROWSER_BASE_URL=https://your-proxy.example.com/openrouter
|
|
48
54
|
# Fallbacks if specific OPENROUTER models are not set
|
|
@@ -50,6 +56,8 @@ SMALL_MODEL=google/gemini-flash
|
|
|
50
56
|
LARGE_MODEL=google/gemini-pro
|
|
51
57
|
IMAGE_MODEL=x-ai/grok-2-vision-1212
|
|
52
58
|
IMAGE_GENERATION_MODEL=google/gemini-2.5-flash-image-preview
|
|
59
|
+
EMBEDDING_MODEL=openai/text-embedding-3-small
|
|
60
|
+
EMBEDDING_DIMENSIONS=1536
|
|
53
61
|
```
|
|
54
62
|
|
|
55
63
|
### Configuration Options
|
|
@@ -89,11 +97,15 @@ app.listen(3000);
|
|
|
89
97
|
- `OPENROUTER_LARGE_MODEL`: Specific model to use for `TEXT_LARGE` and `OBJECT_LARGE`. Overrides `LARGE_MODEL` if set.
|
|
90
98
|
- `OPENROUTER_IMAGE_MODEL`: Specific model to use for `IMAGE_DESCRIPTION`. Overrides `IMAGE_MODEL` if set.
|
|
91
99
|
- `OPENROUTER_IMAGE_GENERATION_MODEL`: Specific model to use for `IMAGE` generation. Overrides `IMAGE_GENERATION_MODEL` if set.
|
|
100
|
+
- `OPENROUTER_EMBEDDING_MODEL`: Specific model to use for `TEXT_EMBEDDING`. Overrides `EMBEDDING_MODEL` if set.
|
|
101
|
+
- `OPENROUTER_EMBEDDING_DIMENSIONS`: Number of dimensions for embedding vectors. Supported values: 256, 384, 512, 768, 1024, 1536, 2048, 3072. Defaults to 1536.
|
|
92
102
|
- `OPENROUTER_AUTO_CLEANUP_IMAGES`: Whether to automatically delete generated images after 30 seconds (default: "false"). Set to "true" to enable auto-cleanup.
|
|
93
103
|
- `SMALL_MODEL`: Fallback model for small tasks (default: "google/gemini-2.0-flash-001"). Used if `OPENROUTER_SMALL_MODEL` is not set.
|
|
94
104
|
- `LARGE_MODEL`: Fallback model for large tasks (default: "openai/gpt-4.1-nano"). Used if `OPENROUTER_LARGE_MODEL` is not set.
|
|
95
105
|
- `IMAGE_MODEL`: Fallback model for image analysis (default: "x-ai/grok-2-vision-1212"). Used if `OPENROUTER_IMAGE_MODEL` is not set.
|
|
96
106
|
- `IMAGE_GENERATION_MODEL`: Fallback model for image generation (default: "google/gemini-2.5-flash-image-preview"). Used if `OPENROUTER_IMAGE_GENERATION_MODEL` is not set.
|
|
107
|
+
- `EMBEDDING_MODEL`: Fallback model for text embeddings (default: "openai/text-embedding-3-small"). Used if `OPENROUTER_EMBEDDING_MODEL` is not set.
|
|
108
|
+
- `EMBEDDING_DIMENSIONS`: Fallback dimension setting for embeddings (default: "1536"). Used if `OPENROUTER_EMBEDDING_DIMENSIONS` is not set.
|
|
97
109
|
|
|
98
110
|
## Provided Models
|
|
99
111
|
|
|
@@ -105,5 +117,6 @@ The plugin currently provides these model types:
|
|
|
105
117
|
- `OBJECT_LARGE`: Generates structured JSON objects based on a prompt, using the configured large model.
|
|
106
118
|
- `IMAGE_DESCRIPTION`: Analyzes images and provides descriptive text and titles, using the configured image model.
|
|
107
119
|
- `IMAGE`: Generates images from text prompts using the configured image generation model (e.g., Gemini 2.5 Flash Image Preview).
|
|
120
|
+
- `TEXT_EMBEDDING`: Generates vector embeddings for text input, supporting configurable dimensions from 256 to 3072, using the configured embedding model.
|
|
108
121
|
|
|
109
|
-
_Note:
|
|
122
|
+
_Note: Audio Transcription is not currently implemented in this specific OpenRouter plugin._
|
|
@@ -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 ModelType4
|
|
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");
|
|
@@ -414,15 +419,6 @@ function shouldAutoCleanupImages(runtime) {
|
|
|
414
419
|
const setting = getSetting(runtime, "OPENROUTER_AUTO_CLEANUP_IMAGES", "false");
|
|
415
420
|
return setting?.toLowerCase() === "true";
|
|
416
421
|
}
|
|
417
|
-
function getToolExecutionMaxSteps(runtime) {
|
|
418
|
-
const setting = getSetting(runtime, "OPENROUTER_TOOL_EXECUTION_MAX_STEPS", "15");
|
|
419
|
-
const value = parseInt(setting || "15", 10);
|
|
420
|
-
if (Number.isNaN(value) || value < 1)
|
|
421
|
-
return 15;
|
|
422
|
-
if (value > 100)
|
|
423
|
-
return 100;
|
|
424
|
-
return value;
|
|
425
|
-
}
|
|
426
422
|
|
|
427
423
|
// src/init.ts
|
|
428
424
|
function initializeOpenRouter(_config, runtime) {
|
|
@@ -461,8 +457,8 @@ function initializeOpenRouter(_config, runtime) {
|
|
|
461
457
|
}
|
|
462
458
|
|
|
463
459
|
// src/models/text.ts
|
|
464
|
-
import { logger as
|
|
465
|
-
import { generateText,
|
|
460
|
+
import { logger as logger2, ModelType } from "@elizaos/core";
|
|
461
|
+
import { generateText, streamText } from "ai";
|
|
466
462
|
|
|
467
463
|
// src/providers/openrouter.ts
|
|
468
464
|
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
|
|
@@ -485,6 +481,8 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
485
481
|
const outputTokens = Number(usage.outputTokens || 0);
|
|
486
482
|
const totalTokens = Number(usage.totalTokens != null ? usage.totalTokens : inputTokens + outputTokens);
|
|
487
483
|
runtime.emitEvent(EventType.MODEL_USED, {
|
|
484
|
+
runtime,
|
|
485
|
+
source: "openrouter",
|
|
488
486
|
provider: "openrouter",
|
|
489
487
|
type,
|
|
490
488
|
prompt: truncatedPrompt,
|
|
@@ -496,8 +494,78 @@ function emitModelUsageEvent(runtime, type, prompt, usage) {
|
|
|
496
494
|
});
|
|
497
495
|
}
|
|
498
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
|
+
|
|
499
567
|
// src/utils/helpers.ts
|
|
500
|
-
import { logger as
|
|
568
|
+
import { logger as logger3 } from "@elizaos/core";
|
|
501
569
|
import { JSONParseError } from "ai";
|
|
502
570
|
function getJsonRepairFunction() {
|
|
503
571
|
return async ({ text, error }) => {
|
|
@@ -510,17 +578,11 @@ function getJsonRepairFunction() {
|
|
|
510
578
|
return null;
|
|
511
579
|
} catch (jsonError) {
|
|
512
580
|
const message = jsonError instanceof Error ? jsonError.message : String(jsonError);
|
|
513
|
-
|
|
581
|
+
logger3.warn(`Failed to repair JSON text: ${message}`);
|
|
514
582
|
return null;
|
|
515
583
|
}
|
|
516
584
|
};
|
|
517
585
|
}
|
|
518
|
-
function handleEmptyToolResponse(modelType) {
|
|
519
|
-
logger2.warn(`[${modelType}] No text generated after tool execution`);
|
|
520
|
-
const fallbackText = "I executed the requested action. The tool completed successfully.";
|
|
521
|
-
logger2.warn(`[${modelType}] Using fallback response text`);
|
|
522
|
-
return fallbackText;
|
|
523
|
-
}
|
|
524
586
|
function parseImageDescriptionResponse(responseText) {
|
|
525
587
|
try {
|
|
526
588
|
const jsonResponse = JSON.parse(responseText);
|
|
@@ -528,7 +590,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
528
590
|
return jsonResponse;
|
|
529
591
|
}
|
|
530
592
|
} catch (e) {
|
|
531
|
-
|
|
593
|
+
logger3.debug(`Parsing as JSON failed, processing as text: ${e}`);
|
|
532
594
|
}
|
|
533
595
|
const titleMatch = responseText.match(/title[:\s]+(.+?)(?:\n|$)/i);
|
|
534
596
|
const title = titleMatch?.[1]?.trim() || "Image Analysis";
|
|
@@ -537,7 +599,7 @@ function parseImageDescriptionResponse(responseText) {
|
|
|
537
599
|
}
|
|
538
600
|
async function handleObjectGenerationError(error) {
|
|
539
601
|
if (error instanceof JSONParseError) {
|
|
540
|
-
|
|
602
|
+
logger3.error(`[generateObject] Failed to parse JSON: ${error.message}`);
|
|
541
603
|
const repairFunction = getJsonRepairFunction();
|
|
542
604
|
const repairedJsonString = await repairFunction({
|
|
543
605
|
text: error.text,
|
|
@@ -546,151 +608,29 @@ async function handleObjectGenerationError(error) {
|
|
|
546
608
|
if (repairedJsonString) {
|
|
547
609
|
try {
|
|
548
610
|
const repairedObject = JSON.parse(repairedJsonString);
|
|
549
|
-
|
|
611
|
+
logger3.log("[generateObject] Successfully repaired JSON.");
|
|
550
612
|
return repairedObject;
|
|
551
613
|
} catch (repairParseError) {
|
|
552
614
|
const message = repairParseError instanceof Error ? repairParseError.message : String(repairParseError);
|
|
553
|
-
|
|
615
|
+
logger3.error(`[generateObject] Failed to parse repaired JSON: ${message}`);
|
|
554
616
|
if (repairParseError instanceof Error)
|
|
555
617
|
throw repairParseError;
|
|
556
618
|
throw Object.assign(new Error(message), { cause: repairParseError });
|
|
557
619
|
}
|
|
558
620
|
} else {
|
|
559
|
-
|
|
621
|
+
logger3.error("[generateObject] JSON repair failed.");
|
|
560
622
|
throw error;
|
|
561
623
|
}
|
|
562
624
|
} else {
|
|
563
625
|
const message = error instanceof Error ? error.message : String(error);
|
|
564
|
-
|
|
626
|
+
logger3.error(`[generateObject] Unknown error: ${message}`);
|
|
565
627
|
if (error instanceof Error)
|
|
566
628
|
throw error;
|
|
567
629
|
throw Object.assign(new Error(message), { cause: error });
|
|
568
630
|
}
|
|
569
631
|
}
|
|
570
|
-
function isLikelyBase64(key, value) {
|
|
571
|
-
const base64KeyPattern = /^(data|content|body|payload|encoded|b64|base64|document)$/i;
|
|
572
|
-
if (!base64KeyPattern.test(key))
|
|
573
|
-
return false;
|
|
574
|
-
if (value.length < 20 || value.length > 1024 * 1024)
|
|
575
|
-
return false;
|
|
576
|
-
if (value.length % 4 !== 0)
|
|
577
|
-
return false;
|
|
578
|
-
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(value))
|
|
579
|
-
return false;
|
|
580
|
-
return true;
|
|
581
|
-
}
|
|
582
|
-
function decodeBase64Fields(obj, depth = 0) {
|
|
583
|
-
if (depth > 5)
|
|
584
|
-
return obj;
|
|
585
|
-
if (!obj || typeof obj !== "object")
|
|
586
|
-
return obj;
|
|
587
|
-
if (Array.isArray(obj))
|
|
588
|
-
return obj.map((item) => decodeBase64Fields(item, depth + 1));
|
|
589
|
-
const decoded = {};
|
|
590
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
591
|
-
if (typeof value === "string" && isLikelyBase64(key, value)) {
|
|
592
|
-
try {
|
|
593
|
-
decoded[key] = Buffer.from(value, "base64").toString("utf8");
|
|
594
|
-
logger2.debug(`[decodeBase64] Decoded field '${key}' (${value.length} chars)`);
|
|
595
|
-
} catch (error) {
|
|
596
|
-
logger2.warn(`[decodeBase64] Failed to decode field '${key}': ${error}`);
|
|
597
|
-
decoded[key] = value;
|
|
598
|
-
}
|
|
599
|
-
} else if (value && typeof value === "object") {
|
|
600
|
-
decoded[key] = decodeBase64Fields(value, depth + 1);
|
|
601
|
-
} else {
|
|
602
|
-
decoded[key] = value;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
return decoded;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// src/models/text.ts
|
|
609
|
-
async function generateTextWithModel(runtime, modelType, params) {
|
|
610
|
-
const { prompt, stopSequences = [], tools, toolChoice } = params;
|
|
611
|
-
const temperature = params.temperature ?? 0.7;
|
|
612
|
-
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
613
|
-
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
614
|
-
const resolvedMaxOutput = params.maxOutputTokens ?? params.maxTokens ?? 8192;
|
|
615
|
-
const openrouter = createOpenRouterProvider(runtime);
|
|
616
|
-
const modelName = modelType === ModelType.TEXT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
617
|
-
const modelLabel = modelType === ModelType.TEXT_SMALL ? "TEXT_SMALL" : "TEXT_LARGE";
|
|
618
|
-
logger3.debug(`[OpenRouter] Generating text with ${modelLabel} model: ${modelName}`);
|
|
619
|
-
const generateParams = {
|
|
620
|
-
model: openrouter.chat(modelName),
|
|
621
|
-
prompt,
|
|
622
|
-
system: runtime.character.system ?? undefined,
|
|
623
|
-
temperature,
|
|
624
|
-
frequencyPenalty,
|
|
625
|
-
presencePenalty,
|
|
626
|
-
stopSequences
|
|
627
|
-
};
|
|
628
|
-
generateParams.maxOutputTokens = resolvedMaxOutput;
|
|
629
|
-
if (tools) {
|
|
630
|
-
generateParams.tools = tools;
|
|
631
|
-
const maxSteps = getToolExecutionMaxSteps(runtime);
|
|
632
|
-
generateParams.stopWhen = stepCountIs(maxSteps);
|
|
633
|
-
logger3.debug(`[OpenRouter] Using maxSteps: ${maxSteps} for tool execution`);
|
|
634
|
-
}
|
|
635
|
-
if (toolChoice) {
|
|
636
|
-
generateParams.toolChoice = toolChoice;
|
|
637
|
-
}
|
|
638
|
-
let capturedToolResults = [];
|
|
639
|
-
let capturedToolCalls = [];
|
|
640
|
-
if (tools) {
|
|
641
|
-
generateParams.onStepFinish = async (stepResult) => {
|
|
642
|
-
if (stepResult.toolCalls && stepResult.toolCalls.length > 0) {
|
|
643
|
-
capturedToolCalls = [
|
|
644
|
-
...capturedToolCalls,
|
|
645
|
-
...stepResult.toolCalls
|
|
646
|
-
];
|
|
647
|
-
}
|
|
648
|
-
if (stepResult.content && Array.isArray(stepResult.content)) {
|
|
649
|
-
const toolResultsFromContent = stepResult.content.filter((content) => content.type === "tool-result" && content.output).map((content) => ({
|
|
650
|
-
toolCallId: content.toolCallId,
|
|
651
|
-
result: decodeBase64Fields(content.output)
|
|
652
|
-
}));
|
|
653
|
-
if (toolResultsFromContent.length > 0) {
|
|
654
|
-
capturedToolResults = [...capturedToolResults, ...toolResultsFromContent];
|
|
655
|
-
}
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
}
|
|
659
|
-
const response = await generateText(generateParams);
|
|
660
|
-
let responseText;
|
|
661
|
-
if (tools && (!response.text || response.text.trim() === "" || response.text === "Tools executed successfully.")) {
|
|
662
|
-
responseText = handleEmptyToolResponse(modelLabel);
|
|
663
|
-
} else {
|
|
664
|
-
responseText = response.text;
|
|
665
|
-
}
|
|
666
|
-
if (response.usage) {
|
|
667
|
-
emitModelUsageEvent(runtime, modelType, prompt, response.usage);
|
|
668
|
-
}
|
|
669
|
-
if (tools && response.steps && response.steps.length > 0) {
|
|
670
|
-
return {
|
|
671
|
-
text: responseText,
|
|
672
|
-
toolCalls: capturedToolCalls,
|
|
673
|
-
toolResults: capturedToolResults,
|
|
674
|
-
steps: response.steps,
|
|
675
|
-
usage: response.usage,
|
|
676
|
-
finishReason: response.finishReason
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
return responseText;
|
|
680
|
-
}
|
|
681
|
-
async function handleTextSmall(runtime, params) {
|
|
682
|
-
return generateTextWithModel(runtime, ModelType.TEXT_SMALL, params);
|
|
683
|
-
}
|
|
684
|
-
async function handleTextLarge(runtime, params) {
|
|
685
|
-
return generateTextWithModel(runtime, ModelType.TEXT_LARGE, params);
|
|
686
|
-
}
|
|
687
632
|
|
|
688
633
|
// src/models/object.ts
|
|
689
|
-
import {
|
|
690
|
-
ModelType as ModelType2,
|
|
691
|
-
logger as logger4
|
|
692
|
-
} from "@elizaos/core";
|
|
693
|
-
import { generateObject } from "ai";
|
|
694
634
|
async function generateObjectWithModel(runtime, modelType, params) {
|
|
695
635
|
const openrouter = createOpenRouterProvider(runtime);
|
|
696
636
|
const modelName = modelType === ModelType2.OBJECT_SMALL ? getSmallModel(runtime) : getLargeModel(runtime);
|
|
@@ -700,7 +640,7 @@ async function generateObjectWithModel(runtime, modelType, params) {
|
|
|
700
640
|
try {
|
|
701
641
|
const { object, usage } = await generateObject({
|
|
702
642
|
model: openrouter.chat(modelName),
|
|
703
|
-
...params.schema && { schema: params.schema },
|
|
643
|
+
...params.schema && { schema: jsonSchema(params.schema) },
|
|
704
644
|
output: params.schema ? "object" : "no-schema",
|
|
705
645
|
prompt: params.prompt,
|
|
706
646
|
temperature,
|
|
@@ -1073,7 +1013,127 @@ var openrouterPlugin = {
|
|
|
1073
1013
|
[ModelType4.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
1074
1014
|
return handleTextEmbedding(runtime, params);
|
|
1075
1015
|
}
|
|
1076
|
-
}
|
|
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
|
+
]
|
|
1077
1137
|
};
|
|
1078
1138
|
var src_default = openrouterPlugin;
|
|
1079
1139
|
export {
|
|
@@ -1081,4 +1141,4 @@ export {
|
|
|
1081
1141
|
src_default as default
|
|
1082
1142
|
};
|
|
1083
1143
|
|
|
1084
|
-
//# debugId=
|
|
1144
|
+
//# debugId=2BBC786BCBA31E1064756E2164756E21
|