@elizaos/plugin-openrouter 2.0.0-alpha.9 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +125 -0
- package/auto-enable.ts +17 -0
- package/dist/browser/index.browser.js +684 -2
- package/dist/browser/index.browser.js.map +12 -4
- package/dist/cjs/index.node.cjs +677 -6
- package/dist/cjs/index.node.cjs.map +12 -4
- package/dist/node/index.node.js +684 -2
- package/dist/node/index.node.js.map +12 -4
- package/package.json +29 -13
package/dist/cjs/index.node.cjs
CHANGED
|
@@ -37,12 +37,16 @@ var __export = (target, all) => {
|
|
|
37
37
|
};
|
|
38
38
|
|
|
39
39
|
// index.ts
|
|
40
|
-
var
|
|
41
|
-
__export(
|
|
40
|
+
var exports_plugin_openrouter = {};
|
|
41
|
+
__export(exports_plugin_openrouter, {
|
|
42
42
|
shouldAutoCleanupImages: () => shouldAutoCleanupImages,
|
|
43
|
-
openrouterPlugin: () =>
|
|
43
|
+
openrouterPlugin: () => openrouterPlugin2,
|
|
44
44
|
getSmallModel: () => getSmallModel,
|
|
45
45
|
getSetting: () => getSetting,
|
|
46
|
+
getResponseHandlerModel: () => getResponseHandlerModel,
|
|
47
|
+
getNanoModel: () => getNanoModel,
|
|
48
|
+
getMegaModel: () => getMegaModel,
|
|
49
|
+
getMediumModel: () => getMediumModel,
|
|
46
50
|
getLargeModel: () => getLargeModel,
|
|
47
51
|
getImageModel: () => getImageModel,
|
|
48
52
|
getImageGenerationModel: () => getImageGenerationModel,
|
|
@@ -50,7 +54,8 @@ __export(exports_typescript, {
|
|
|
50
54
|
getEmbeddingDimensions: () => getEmbeddingDimensions,
|
|
51
55
|
getBaseURL: () => getBaseURL,
|
|
52
56
|
getApiKey: () => getApiKey,
|
|
53
|
-
|
|
57
|
+
getActionPlannerModel: () => getActionPlannerModel,
|
|
58
|
+
default: () => openrouterPlugin2,
|
|
54
59
|
DEFAULT_SMALL_MODEL: () => DEFAULT_SMALL_MODEL,
|
|
55
60
|
DEFAULT_LARGE_MODEL: () => DEFAULT_LARGE_MODEL,
|
|
56
61
|
DEFAULT_IMAGE_MODEL: () => DEFAULT_IMAGE_MODEL,
|
|
@@ -59,7 +64,13 @@ __export(exports_typescript, {
|
|
|
59
64
|
DEFAULT_EMBEDDING_DIMENSIONS: () => DEFAULT_EMBEDDING_DIMENSIONS,
|
|
60
65
|
DEFAULT_BASE_URL: () => DEFAULT_BASE_URL
|
|
61
66
|
});
|
|
62
|
-
module.exports = __toCommonJS(
|
|
67
|
+
module.exports = __toCommonJS(exports_plugin_openrouter);
|
|
68
|
+
|
|
69
|
+
// plugin.ts
|
|
70
|
+
var import_core6 = require("@elizaos/core");
|
|
71
|
+
|
|
72
|
+
// init.ts
|
|
73
|
+
var import_core = require("@elizaos/core");
|
|
63
74
|
|
|
64
75
|
// utils/config.ts
|
|
65
76
|
var DEFAULT_BASE_URL = "https://openrouter.ai/api/v1";
|
|
@@ -96,9 +107,24 @@ function getApiKey(runtime) {
|
|
|
96
107
|
function getSmallModel(runtime) {
|
|
97
108
|
return getSetting(runtime, "OPENROUTER_SMALL_MODEL") ?? getSetting(runtime, "SMALL_MODEL", DEFAULT_SMALL_MODEL) ?? DEFAULT_SMALL_MODEL;
|
|
98
109
|
}
|
|
110
|
+
function getNanoModel(runtime) {
|
|
111
|
+
return getSetting(runtime, "OPENROUTER_NANO_MODEL") ?? getSetting(runtime, "NANO_MODEL") ?? getSmallModel(runtime);
|
|
112
|
+
}
|
|
113
|
+
function getMediumModel(runtime) {
|
|
114
|
+
return getSetting(runtime, "OPENROUTER_MEDIUM_MODEL") ?? getSetting(runtime, "MEDIUM_MODEL") ?? getSmallModel(runtime);
|
|
115
|
+
}
|
|
99
116
|
function getLargeModel(runtime) {
|
|
100
117
|
return getSetting(runtime, "OPENROUTER_LARGE_MODEL") ?? getSetting(runtime, "LARGE_MODEL", DEFAULT_LARGE_MODEL) ?? DEFAULT_LARGE_MODEL;
|
|
101
118
|
}
|
|
119
|
+
function getMegaModel(runtime) {
|
|
120
|
+
return getSetting(runtime, "OPENROUTER_MEGA_MODEL") ?? getSetting(runtime, "MEGA_MODEL") ?? getLargeModel(runtime);
|
|
121
|
+
}
|
|
122
|
+
function getResponseHandlerModel(runtime) {
|
|
123
|
+
return getSetting(runtime, "OPENROUTER_RESPONSE_HANDLER_MODEL") ?? getSetting(runtime, "OPENROUTER_SHOULD_RESPOND_MODEL") ?? getSetting(runtime, "RESPONSE_HANDLER_MODEL") ?? getSetting(runtime, "SHOULD_RESPOND_MODEL") ?? getNanoModel(runtime);
|
|
124
|
+
}
|
|
125
|
+
function getActionPlannerModel(runtime) {
|
|
126
|
+
return getSetting(runtime, "OPENROUTER_ACTION_PLANNER_MODEL") ?? getSetting(runtime, "OPENROUTER_PLANNER_MODEL") ?? getSetting(runtime, "ACTION_PLANNER_MODEL") ?? getSetting(runtime, "PLANNER_MODEL") ?? getMediumModel(runtime);
|
|
127
|
+
}
|
|
102
128
|
function getImageModel(runtime) {
|
|
103
129
|
return getSetting(runtime, "OPENROUTER_IMAGE_MODEL") ?? getSetting(runtime, "IMAGE_MODEL", DEFAULT_IMAGE_MODEL) ?? DEFAULT_IMAGE_MODEL;
|
|
104
130
|
}
|
|
@@ -117,5 +143,650 @@ function shouldAutoCleanupImages(runtime) {
|
|
|
117
143
|
return setting?.toLowerCase() === "true";
|
|
118
144
|
}
|
|
119
145
|
|
|
120
|
-
|
|
146
|
+
// init.ts
|
|
147
|
+
globalThis.AI_SDK_LOG_WARNINGS ??= false;
|
|
148
|
+
function initializeOpenRouter(_config, runtime) {
|
|
149
|
+
(async () => {
|
|
150
|
+
try {
|
|
151
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.document;
|
|
152
|
+
if (isBrowser) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (!getApiKey(runtime)) {
|
|
156
|
+
import_core.logger.warn("OPENROUTER_API_KEY is not set in environment - OpenRouter functionality will be limited");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const baseURL = getBaseURL(runtime);
|
|
160
|
+
const response = await fetch(`${baseURL}/models`, {
|
|
161
|
+
headers: { Authorization: `Bearer ${getApiKey(runtime)}` }
|
|
162
|
+
});
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
import_core.logger.warn(`OpenRouter API key validation failed: ${response.statusText}`);
|
|
165
|
+
} else {
|
|
166
|
+
import_core.logger.log("OpenRouter API key validated successfully");
|
|
167
|
+
}
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
170
|
+
import_core.logger.warn(`Error validating OpenRouter API key: ${message}`);
|
|
171
|
+
}
|
|
172
|
+
})();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// models/embedding.ts
|
|
176
|
+
var import_core3 = require("@elizaos/core");
|
|
177
|
+
|
|
178
|
+
// utils/events.ts
|
|
179
|
+
var import_core2 = require("@elizaos/core");
|
|
180
|
+
function emitModelUsageEvent(runtime, modelType, _prompt, usage, modelName, modelLabel) {
|
|
181
|
+
const inputTokens = usage.inputTokens ?? usage.promptTokens ?? 0;
|
|
182
|
+
const outputTokens = usage.outputTokens ?? usage.completionTokens ?? 0;
|
|
183
|
+
const totalTokens = usage.totalTokens ?? inputTokens + outputTokens;
|
|
184
|
+
const model = modelName?.trim() || modelLabel?.trim() || String(modelType);
|
|
185
|
+
runtime.emitEvent(import_core2.EventType.MODEL_USED, {
|
|
186
|
+
runtime,
|
|
187
|
+
source: "openrouter",
|
|
188
|
+
provider: "openrouter",
|
|
189
|
+
type: modelType,
|
|
190
|
+
model,
|
|
191
|
+
modelName: model,
|
|
192
|
+
modelLabel: modelLabel ?? String(modelType),
|
|
193
|
+
tokens: {
|
|
194
|
+
prompt: inputTokens,
|
|
195
|
+
completion: outputTokens,
|
|
196
|
+
total: totalTokens
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
promptTokens: inputTokens,
|
|
201
|
+
completionTokens: outputTokens,
|
|
202
|
+
totalTokens
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// models/embedding.ts
|
|
207
|
+
async function handleTextEmbedding(runtime, params) {
|
|
208
|
+
const embeddingModelName = getEmbeddingModel(runtime);
|
|
209
|
+
const embeddingDimension = Number.parseInt(getSetting(runtime, "OPENROUTER_EMBEDDING_DIMENSIONS") ?? getSetting(runtime, "EMBEDDING_DIMENSIONS") ?? "1536", 10);
|
|
210
|
+
if (!Object.values(import_core3.VECTOR_DIMS).includes(embeddingDimension)) {
|
|
211
|
+
const errorMsg = `Invalid embedding dimension: ${embeddingDimension}. Must be one of: ${Object.values(import_core3.VECTOR_DIMS).join(", ")}`;
|
|
212
|
+
import_core3.logger.error(errorMsg);
|
|
213
|
+
throw new Error(errorMsg);
|
|
214
|
+
}
|
|
215
|
+
if (params === null) {
|
|
216
|
+
const testVector = Array(embeddingDimension).fill(0);
|
|
217
|
+
testVector[0] = 0.1;
|
|
218
|
+
return testVector;
|
|
219
|
+
}
|
|
220
|
+
let text;
|
|
221
|
+
if (typeof params === "string") {
|
|
222
|
+
text = params;
|
|
223
|
+
} else if (typeof params === "object" && params.text) {
|
|
224
|
+
text = params.text;
|
|
225
|
+
} else {
|
|
226
|
+
const errorMsg = "Invalid input format for embedding";
|
|
227
|
+
import_core3.logger.warn(errorMsg);
|
|
228
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
229
|
+
fallbackVector[0] = 0.2;
|
|
230
|
+
return fallbackVector;
|
|
231
|
+
}
|
|
232
|
+
if (!text.trim()) {
|
|
233
|
+
const errorMsg = "Empty text for embedding";
|
|
234
|
+
import_core3.logger.warn(errorMsg);
|
|
235
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
236
|
+
fallbackVector[0] = 0.3;
|
|
237
|
+
return fallbackVector;
|
|
238
|
+
}
|
|
239
|
+
const maxChars = 8000 * 4;
|
|
240
|
+
if (text.length > maxChars) {
|
|
241
|
+
import_core3.logger.warn(`[OpenRouter] Embedding input too long (~${Math.ceil(text.length / 4)} tokens), truncating to ~8000 tokens`);
|
|
242
|
+
text = text.slice(0, maxChars);
|
|
243
|
+
}
|
|
244
|
+
const apiKey = getApiKey(runtime);
|
|
245
|
+
if (!apiKey) {
|
|
246
|
+
const errorMsg = "OPENROUTER_API_KEY is not set";
|
|
247
|
+
import_core3.logger.error(errorMsg);
|
|
248
|
+
throw new Error(errorMsg);
|
|
249
|
+
}
|
|
250
|
+
const baseURL = getBaseURL(runtime);
|
|
251
|
+
try {
|
|
252
|
+
const response = await fetch(`${baseURL}/embeddings`, {
|
|
253
|
+
method: "POST",
|
|
254
|
+
headers: {
|
|
255
|
+
Authorization: `Bearer ${apiKey}`,
|
|
256
|
+
"Content-Type": "application/json",
|
|
257
|
+
"HTTP-Referer": getSetting(runtime, "OPENROUTER_HTTP_REFERER") || "",
|
|
258
|
+
"X-Title": getSetting(runtime, "OPENROUTER_X_TITLE") || "ElizaOS"
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify({
|
|
261
|
+
model: embeddingModelName,
|
|
262
|
+
input: text
|
|
263
|
+
})
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
import_core3.logger.error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
267
|
+
throw new Error(`OpenRouter API error: ${response.status} - ${response.statusText}`);
|
|
268
|
+
}
|
|
269
|
+
const data = await response.json();
|
|
270
|
+
if (!data?.data?.[0]?.embedding) {
|
|
271
|
+
import_core3.logger.error("API returned invalid structure");
|
|
272
|
+
throw new Error("API returned invalid structure");
|
|
273
|
+
}
|
|
274
|
+
const embedding = data.data[0].embedding;
|
|
275
|
+
if (!Array.isArray(embedding) || embedding.length !== embeddingDimension) {
|
|
276
|
+
const errorMsg = `Embedding length ${embedding?.length ?? 0} does not match configured dimension ${embeddingDimension}`;
|
|
277
|
+
import_core3.logger.error(errorMsg);
|
|
278
|
+
const fallbackVector = Array(embeddingDimension).fill(0);
|
|
279
|
+
fallbackVector[0] = 0.4;
|
|
280
|
+
return fallbackVector;
|
|
281
|
+
}
|
|
282
|
+
if (data.usage) {
|
|
283
|
+
const usage = {
|
|
284
|
+
inputTokens: data.usage.prompt_tokens,
|
|
285
|
+
outputTokens: 0,
|
|
286
|
+
totalTokens: data.usage.total_tokens
|
|
287
|
+
};
|
|
288
|
+
emitModelUsageEvent(runtime, import_core3.ModelType.TEXT_EMBEDDING, text, usage, embeddingModelName);
|
|
289
|
+
}
|
|
290
|
+
return embedding;
|
|
291
|
+
} catch (error) {
|
|
292
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
293
|
+
import_core3.logger.error(`Error generating embedding: ${message}`);
|
|
294
|
+
throw error instanceof Error ? error : new Error(message);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// models/image.ts
|
|
299
|
+
var import_core4 = require("@elizaos/core");
|
|
300
|
+
var import_ai = require("ai");
|
|
301
|
+
|
|
302
|
+
// providers/openrouter.ts
|
|
303
|
+
var import_ai_sdk_provider = require("@openrouter/ai-sdk-provider");
|
|
304
|
+
function createOpenRouterProvider(runtime) {
|
|
305
|
+
const apiKey = getApiKey(runtime);
|
|
306
|
+
const isBrowser = typeof globalThis !== "undefined" && globalThis.document;
|
|
307
|
+
const baseURL = getBaseURL(runtime);
|
|
308
|
+
return import_ai_sdk_provider.createOpenRouter({
|
|
309
|
+
apiKey: isBrowser ? undefined : apiKey,
|
|
310
|
+
baseURL
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
// models/image.ts
|
|
314
|
+
async function handleImageDescription(runtime, params) {
|
|
315
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
316
|
+
const modelName = getImageModel(runtime);
|
|
317
|
+
const imageUrl = typeof params === "string" ? params : params.imageUrl;
|
|
318
|
+
const prompt = typeof params === "string" ? "Describe this image" : params.prompt || "Describe this image";
|
|
319
|
+
try {
|
|
320
|
+
const generateParams = {
|
|
321
|
+
model: openrouter.chat(modelName),
|
|
322
|
+
messages: [
|
|
323
|
+
{
|
|
324
|
+
role: "user",
|
|
325
|
+
content: [
|
|
326
|
+
{ type: "text", text: prompt },
|
|
327
|
+
{ type: "image", image: imageUrl }
|
|
328
|
+
]
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
};
|
|
332
|
+
const response = await import_ai.generateText(generateParams);
|
|
333
|
+
if (response.usage) {
|
|
334
|
+
emitModelUsageEvent(runtime, import_core4.ModelType.IMAGE_DESCRIPTION, prompt, response.usage, modelName);
|
|
335
|
+
}
|
|
336
|
+
return response.text;
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
339
|
+
import_core4.logger.error(`Error describing image: ${message}`);
|
|
340
|
+
throw error instanceof Error ? error : new Error(message);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
async function handleImageGeneration(runtime, params) {
|
|
344
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
345
|
+
const modelName = getImageGenerationModel(runtime);
|
|
346
|
+
try {
|
|
347
|
+
const generateParams = {
|
|
348
|
+
model: openrouter.chat(modelName),
|
|
349
|
+
prompt: `Generate an image: ${params.prompt}`
|
|
350
|
+
};
|
|
351
|
+
const response = await import_ai.generateText(generateParams);
|
|
352
|
+
if (response.usage) {
|
|
353
|
+
emitModelUsageEvent(runtime, import_core4.ModelType.IMAGE, params.prompt, response.usage, modelName);
|
|
354
|
+
}
|
|
355
|
+
return {
|
|
356
|
+
imageUrl: response.text,
|
|
357
|
+
caption: params.prompt
|
|
358
|
+
};
|
|
359
|
+
} catch (error) {
|
|
360
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
361
|
+
import_core4.logger.error(`Error generating image: ${message}`);
|
|
362
|
+
throw error instanceof Error ? error : new Error(message);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// models/text.ts
|
|
367
|
+
var import_core5 = require("@elizaos/core");
|
|
368
|
+
var import_ai2 = require("ai");
|
|
369
|
+
var RESPONSES_ROUTED_PREFIXES = ["openai/", "anthropic/"];
|
|
370
|
+
var NO_SAMPLING_MODEL_PATTERNS = ["o1", "o3", "o4", "gpt-5", "gpt-5-mini"];
|
|
371
|
+
var TEXT_NANO_MODEL_TYPE = import_core5.ModelType.TEXT_NANO ?? "TEXT_NANO";
|
|
372
|
+
var TEXT_MEDIUM_MODEL_TYPE = import_core5.ModelType.TEXT_MEDIUM ?? "TEXT_MEDIUM";
|
|
373
|
+
var TEXT_MEGA_MODEL_TYPE = import_core5.ModelType.TEXT_MEGA ?? "TEXT_MEGA";
|
|
374
|
+
var RESPONSE_HANDLER_MODEL_TYPE = import_core5.ModelType.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
|
|
375
|
+
var ACTION_PLANNER_MODEL_TYPE = import_core5.ModelType.ACTION_PLANNER ?? "ACTION_PLANNER";
|
|
376
|
+
function buildUserContent(params) {
|
|
377
|
+
const content = [{ type: "text", text: params.prompt }];
|
|
378
|
+
for (const attachment of params.attachments ?? []) {
|
|
379
|
+
content.push({
|
|
380
|
+
type: "file",
|
|
381
|
+
data: attachment.data,
|
|
382
|
+
mediaType: attachment.mediaType,
|
|
383
|
+
...attachment.filename ? { filename: attachment.filename } : {}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return content;
|
|
387
|
+
}
|
|
388
|
+
function supportsSamplingParameters(modelName) {
|
|
389
|
+
const lowerModelName = modelName.toLowerCase();
|
|
390
|
+
if (RESPONSES_ROUTED_PREFIXES.some((prefix) => lowerModelName.startsWith(prefix))) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
return !NO_SAMPLING_MODEL_PATTERNS.some((pattern) => lowerModelName.includes(pattern));
|
|
394
|
+
}
|
|
395
|
+
function buildStructuredOutput(responseSchema) {
|
|
396
|
+
if (responseSchema && typeof responseSchema === "object" && "responseFormat" in responseSchema && "parseCompleteOutput" in responseSchema) {
|
|
397
|
+
return responseSchema;
|
|
398
|
+
}
|
|
399
|
+
const schemaOptions = responseSchema && typeof responseSchema === "object" && "schema" in responseSchema ? responseSchema : { schema: responseSchema };
|
|
400
|
+
return {
|
|
401
|
+
name: "object",
|
|
402
|
+
responseFormat: Promise.resolve({
|
|
403
|
+
type: "json",
|
|
404
|
+
schema: schemaOptions.schema,
|
|
405
|
+
...schemaOptions.name ? { name: schemaOptions.name } : {},
|
|
406
|
+
...schemaOptions.description ? { description: schemaOptions.description } : {}
|
|
407
|
+
}),
|
|
408
|
+
async parseCompleteOutput({ text }) {
|
|
409
|
+
return JSON.parse(text);
|
|
410
|
+
},
|
|
411
|
+
async parsePartialOutput() {
|
|
412
|
+
return;
|
|
413
|
+
},
|
|
414
|
+
createElementStreamTransform() {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function usesNativeTextResult(params) {
|
|
420
|
+
return Boolean(params.messages || params.tools || params.toolChoice || params.responseSchema);
|
|
421
|
+
}
|
|
422
|
+
function getModelNameForType(runtime, modelType) {
|
|
423
|
+
switch (modelType) {
|
|
424
|
+
case TEXT_NANO_MODEL_TYPE:
|
|
425
|
+
return getNanoModel(runtime);
|
|
426
|
+
case TEXT_MEDIUM_MODEL_TYPE:
|
|
427
|
+
return getMediumModel(runtime);
|
|
428
|
+
case import_core5.ModelType.TEXT_SMALL:
|
|
429
|
+
return getSmallModel(runtime);
|
|
430
|
+
case import_core5.ModelType.TEXT_LARGE:
|
|
431
|
+
return getLargeModel(runtime);
|
|
432
|
+
case TEXT_MEGA_MODEL_TYPE:
|
|
433
|
+
return getMegaModel(runtime);
|
|
434
|
+
case RESPONSE_HANDLER_MODEL_TYPE:
|
|
435
|
+
return getResponseHandlerModel(runtime);
|
|
436
|
+
case ACTION_PLANNER_MODEL_TYPE:
|
|
437
|
+
return getActionPlannerModel(runtime);
|
|
438
|
+
default:
|
|
439
|
+
return getLargeModel(runtime);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function getModelLabelForType(modelType) {
|
|
443
|
+
switch (modelType) {
|
|
444
|
+
case TEXT_NANO_MODEL_TYPE:
|
|
445
|
+
return "TEXT_NANO";
|
|
446
|
+
case TEXT_MEDIUM_MODEL_TYPE:
|
|
447
|
+
return "TEXT_MEDIUM";
|
|
448
|
+
case import_core5.ModelType.TEXT_SMALL:
|
|
449
|
+
return "TEXT_SMALL";
|
|
450
|
+
case import_core5.ModelType.TEXT_LARGE:
|
|
451
|
+
return "TEXT_LARGE";
|
|
452
|
+
case TEXT_MEGA_MODEL_TYPE:
|
|
453
|
+
return "TEXT_MEGA";
|
|
454
|
+
case RESPONSE_HANDLER_MODEL_TYPE:
|
|
455
|
+
return "RESPONSE_HANDLER";
|
|
456
|
+
case ACTION_PLANNER_MODEL_TYPE:
|
|
457
|
+
return "ACTION_PLANNER";
|
|
458
|
+
default:
|
|
459
|
+
return String(modelType);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function buildGenerateParams(runtime, modelType, params) {
|
|
463
|
+
const paramsWithAttachments = params;
|
|
464
|
+
const { prompt } = params;
|
|
465
|
+
const paramsWithMax = params;
|
|
466
|
+
const resolvedMaxOutput = paramsWithMax.maxOutputTokens ?? paramsWithMax.maxTokens ?? 8192;
|
|
467
|
+
const openrouter = createOpenRouterProvider(runtime);
|
|
468
|
+
const modelName = getModelNameForType(runtime, modelType);
|
|
469
|
+
const modelLabel = getModelLabelForType(modelType);
|
|
470
|
+
const supportsSampling = supportsSamplingParameters(modelName);
|
|
471
|
+
const stopSequences = Array.isArray(params.stopSequences) && params.stopSequences.length > 0 ? params.stopSequences : undefined;
|
|
472
|
+
const userContent = (paramsWithAttachments.attachments?.length ?? 0) > 0 ? buildUserContent(paramsWithAttachments) : undefined;
|
|
473
|
+
const temperature = params.temperature ?? 0.7;
|
|
474
|
+
const frequencyPenalty = params.frequencyPenalty ?? 0.7;
|
|
475
|
+
const presencePenalty = params.presencePenalty ?? 0.7;
|
|
476
|
+
const systemPrompt = import_core5.resolveEffectiveSystemPrompt({
|
|
477
|
+
params: paramsWithAttachments,
|
|
478
|
+
fallback: import_core5.buildCanonicalSystemPrompt({ character: runtime.character })
|
|
479
|
+
});
|
|
480
|
+
const wireMessages = import_core5.dropDuplicateLeadingSystemMessage(paramsWithAttachments.messages, systemPrompt);
|
|
481
|
+
const promptOrMessages = paramsWithAttachments.messages ? wireMessages && wireMessages.length > 0 ? { messages: wireMessages } : userContent ? { messages: [{ role: "user", content: userContent }] } : { prompt } : userContent ? { messages: [{ role: "user", content: userContent }] } : { prompt };
|
|
482
|
+
const rawProviderOptions = paramsWithAttachments.providerOptions;
|
|
483
|
+
const { openrouter: rawOpenrouterOptions, ...restProviderOptions } = rawProviderOptions ?? {};
|
|
484
|
+
const openrouterOptions = {
|
|
485
|
+
...rawOpenrouterOptions ?? {}
|
|
486
|
+
};
|
|
487
|
+
const mergedProviderOptions = {
|
|
488
|
+
...restProviderOptions,
|
|
489
|
+
...Object.keys(openrouterOptions).length > 0 ? { openrouter: openrouterOptions } : {}
|
|
490
|
+
};
|
|
491
|
+
const resolvedProviderOptions = Object.keys(mergedProviderOptions).length > 0 ? mergedProviderOptions : undefined;
|
|
492
|
+
const generateParams = {
|
|
493
|
+
model: openrouter.chat(modelName),
|
|
494
|
+
...promptOrMessages,
|
|
495
|
+
system: systemPrompt,
|
|
496
|
+
...supportsSampling ? {
|
|
497
|
+
temperature,
|
|
498
|
+
frequencyPenalty,
|
|
499
|
+
presencePenalty,
|
|
500
|
+
...stopSequences ? { stopSequences } : {}
|
|
501
|
+
} : {},
|
|
502
|
+
maxOutputTokens: resolvedMaxOutput,
|
|
503
|
+
...paramsWithAttachments.tools ? { tools: paramsWithAttachments.tools } : {},
|
|
504
|
+
...paramsWithAttachments.toolChoice ? { toolChoice: paramsWithAttachments.toolChoice } : {},
|
|
505
|
+
...paramsWithAttachments.responseSchema ? { output: buildStructuredOutput(paramsWithAttachments.responseSchema) } : {},
|
|
506
|
+
...resolvedProviderOptions ? { providerOptions: resolvedProviderOptions } : {}
|
|
507
|
+
};
|
|
508
|
+
return {
|
|
509
|
+
generateParams,
|
|
510
|
+
modelName,
|
|
511
|
+
modelLabel,
|
|
512
|
+
prompt,
|
|
513
|
+
shouldReturnNativeResult: usesNativeTextResult(paramsWithAttachments)
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
function handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelName, modelLabel, shouldReturnNativeResult) {
|
|
517
|
+
const streamResult = import_ai2.streamText(generateParams);
|
|
518
|
+
const usagePromise = Promise.resolve(streamResult.usage).then((usage) => {
|
|
519
|
+
if (!usage) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
return emitModelUsageEvent(runtime, modelType, prompt, usage, modelName, modelLabel);
|
|
523
|
+
});
|
|
524
|
+
const ignoreUsageError = () => {
|
|
525
|
+
return;
|
|
526
|
+
};
|
|
527
|
+
async function* textStreamWithUsage() {
|
|
528
|
+
let completed = false;
|
|
529
|
+
try {
|
|
530
|
+
for await (const chunk of streamResult.textStream) {
|
|
531
|
+
yield chunk;
|
|
532
|
+
}
|
|
533
|
+
completed = true;
|
|
534
|
+
} finally {
|
|
535
|
+
if (completed) {
|
|
536
|
+
await usagePromise.catch(ignoreUsageError);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
textStream: textStreamWithUsage(),
|
|
542
|
+
text: Promise.resolve(streamResult.text).then(async (text) => {
|
|
543
|
+
await usagePromise.catch(ignoreUsageError);
|
|
544
|
+
return text;
|
|
545
|
+
}),
|
|
546
|
+
...shouldReturnNativeResult ? { toolCalls: Promise.resolve(streamResult.toolCalls) } : {},
|
|
547
|
+
usage: usagePromise,
|
|
548
|
+
finishReason: Promise.resolve(streamResult.finishReason)
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function buildNativeTextResult(result) {
|
|
552
|
+
const inputTokens = result.usage?.inputTokens ?? result.usage?.promptTokens ?? 0;
|
|
553
|
+
const outputTokens = result.usage?.outputTokens ?? result.usage?.completionTokens ?? 0;
|
|
554
|
+
if (!result.usage) {
|
|
555
|
+
return {
|
|
556
|
+
text: result.text,
|
|
557
|
+
toolCalls: result.toolCalls ?? [],
|
|
558
|
+
finishReason: result.finishReason
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
const cacheRead = result.usage.cacheReadInputTokens ?? result.usage.cachedInputTokens;
|
|
562
|
+
const cacheCreation = result.usage.cacheCreationInputTokens;
|
|
563
|
+
const usage = {
|
|
564
|
+
promptTokens: inputTokens,
|
|
565
|
+
completionTokens: outputTokens,
|
|
566
|
+
totalTokens: result.usage.totalTokens ?? inputTokens + outputTokens,
|
|
567
|
+
...typeof cacheRead === "number" ? { cacheReadInputTokens: cacheRead } : {},
|
|
568
|
+
...typeof cacheCreation === "number" ? { cacheCreationInputTokens: cacheCreation } : {}
|
|
569
|
+
};
|
|
570
|
+
return {
|
|
571
|
+
text: result.text,
|
|
572
|
+
toolCalls: result.toolCalls ?? [],
|
|
573
|
+
finishReason: result.finishReason,
|
|
574
|
+
usage
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
async function generateTextWithModel(runtime, modelType, params) {
|
|
578
|
+
const { generateParams, modelName, modelLabel, prompt, shouldReturnNativeResult } = buildGenerateParams(runtime, modelType, params);
|
|
579
|
+
if (params.stream) {
|
|
580
|
+
return handleStreamingGeneration(runtime, modelType, generateParams, prompt, modelName, modelLabel, shouldReturnNativeResult);
|
|
581
|
+
}
|
|
582
|
+
const response = await import_ai2.generateText(generateParams);
|
|
583
|
+
if (response.usage) {
|
|
584
|
+
emitModelUsageEvent(runtime, modelType, prompt, response.usage, modelName, modelLabel);
|
|
585
|
+
}
|
|
586
|
+
if (shouldReturnNativeResult) {
|
|
587
|
+
return buildNativeTextResult(response);
|
|
588
|
+
}
|
|
589
|
+
return response.text;
|
|
590
|
+
}
|
|
591
|
+
async function handleTextSmall(runtime, params) {
|
|
592
|
+
return generateTextWithModel(runtime, import_core5.ModelType.TEXT_SMALL, params);
|
|
593
|
+
}
|
|
594
|
+
async function handleTextNano(runtime, params) {
|
|
595
|
+
return generateTextWithModel(runtime, TEXT_NANO_MODEL_TYPE, params);
|
|
596
|
+
}
|
|
597
|
+
async function handleTextMedium(runtime, params) {
|
|
598
|
+
return generateTextWithModel(runtime, TEXT_MEDIUM_MODEL_TYPE, params);
|
|
599
|
+
}
|
|
600
|
+
async function handleTextLarge(runtime, params) {
|
|
601
|
+
return generateTextWithModel(runtime, import_core5.ModelType.TEXT_LARGE, params);
|
|
602
|
+
}
|
|
603
|
+
async function handleTextMega(runtime, params) {
|
|
604
|
+
return generateTextWithModel(runtime, TEXT_MEGA_MODEL_TYPE, params);
|
|
605
|
+
}
|
|
606
|
+
async function handleResponseHandler(runtime, params) {
|
|
607
|
+
return generateTextWithModel(runtime, RESPONSE_HANDLER_MODEL_TYPE, params);
|
|
608
|
+
}
|
|
609
|
+
async function handleActionPlanner(runtime, params) {
|
|
610
|
+
return generateTextWithModel(runtime, ACTION_PLANNER_MODEL_TYPE, params);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// plugin.ts
|
|
614
|
+
function getProcessEnv() {
|
|
615
|
+
if (typeof process === "undefined" || !process.env) {
|
|
616
|
+
return {};
|
|
617
|
+
}
|
|
618
|
+
return process.env;
|
|
619
|
+
}
|
|
620
|
+
var env = getProcessEnv();
|
|
621
|
+
var TEXT_NANO_MODEL_TYPE2 = import_core6.ModelType.TEXT_NANO ?? "TEXT_NANO";
|
|
622
|
+
var TEXT_MEDIUM_MODEL_TYPE2 = import_core6.ModelType.TEXT_MEDIUM ?? "TEXT_MEDIUM";
|
|
623
|
+
var TEXT_MEGA_MODEL_TYPE2 = import_core6.ModelType.TEXT_MEGA ?? "TEXT_MEGA";
|
|
624
|
+
var RESPONSE_HANDLER_MODEL_TYPE2 = import_core6.ModelType.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
|
|
625
|
+
var ACTION_PLANNER_MODEL_TYPE2 = import_core6.ModelType.ACTION_PLANNER ?? "ACTION_PLANNER";
|
|
626
|
+
var openrouterPlugin = {
|
|
627
|
+
name: "openrouter",
|
|
628
|
+
description: "OpenRouter multi-model AI gateway plugin",
|
|
629
|
+
autoEnable: {
|
|
630
|
+
envKeys: ["OPENROUTER_API_KEY"]
|
|
631
|
+
},
|
|
632
|
+
config: {
|
|
633
|
+
OPENROUTER_API_KEY: env.OPENROUTER_API_KEY ?? null,
|
|
634
|
+
OPENROUTER_BASE_URL: env.OPENROUTER_BASE_URL ?? null,
|
|
635
|
+
OPENROUTER_NANO_MODEL: env.OPENROUTER_NANO_MODEL ?? null,
|
|
636
|
+
OPENROUTER_MEDIUM_MODEL: env.OPENROUTER_MEDIUM_MODEL ?? null,
|
|
637
|
+
OPENROUTER_SMALL_MODEL: env.OPENROUTER_SMALL_MODEL ?? null,
|
|
638
|
+
OPENROUTER_LARGE_MODEL: env.OPENROUTER_LARGE_MODEL ?? null,
|
|
639
|
+
OPENROUTER_MEGA_MODEL: env.OPENROUTER_MEGA_MODEL ?? null,
|
|
640
|
+
OPENROUTER_RESPONSE_HANDLER_MODEL: env.OPENROUTER_RESPONSE_HANDLER_MODEL ?? null,
|
|
641
|
+
OPENROUTER_SHOULD_RESPOND_MODEL: env.OPENROUTER_SHOULD_RESPOND_MODEL ?? null,
|
|
642
|
+
OPENROUTER_ACTION_PLANNER_MODEL: env.OPENROUTER_ACTION_PLANNER_MODEL ?? null,
|
|
643
|
+
OPENROUTER_PLANNER_MODEL: env.OPENROUTER_PLANNER_MODEL ?? null,
|
|
644
|
+
OPENROUTER_IMAGE_MODEL: env.OPENROUTER_IMAGE_MODEL ?? null,
|
|
645
|
+
OPENROUTER_IMAGE_GENERATION_MODEL: env.OPENROUTER_IMAGE_GENERATION_MODEL ?? null,
|
|
646
|
+
OPENROUTER_EMBEDDING_MODEL: env.OPENROUTER_EMBEDDING_MODEL ?? null,
|
|
647
|
+
OPENROUTER_EMBEDDING_DIMENSIONS: env.OPENROUTER_EMBEDDING_DIMENSIONS ?? null,
|
|
648
|
+
OPENROUTER_AUTO_CLEANUP_IMAGES: env.OPENROUTER_AUTO_CLEANUP_IMAGES ?? null,
|
|
649
|
+
NANO_MODEL: env.NANO_MODEL ?? null,
|
|
650
|
+
MEDIUM_MODEL: env.MEDIUM_MODEL ?? null,
|
|
651
|
+
SMALL_MODEL: env.SMALL_MODEL ?? null,
|
|
652
|
+
LARGE_MODEL: env.LARGE_MODEL ?? null,
|
|
653
|
+
MEGA_MODEL: env.MEGA_MODEL ?? null,
|
|
654
|
+
RESPONSE_HANDLER_MODEL: env.RESPONSE_HANDLER_MODEL ?? null,
|
|
655
|
+
SHOULD_RESPOND_MODEL: env.SHOULD_RESPOND_MODEL ?? null,
|
|
656
|
+
ACTION_PLANNER_MODEL: env.ACTION_PLANNER_MODEL ?? null,
|
|
657
|
+
PLANNER_MODEL: env.PLANNER_MODEL ?? null,
|
|
658
|
+
IMAGE_MODEL: env.IMAGE_MODEL ?? null,
|
|
659
|
+
IMAGE_GENERATION_MODEL: env.IMAGE_GENERATION_MODEL ?? null,
|
|
660
|
+
EMBEDDING_MODEL: env.EMBEDDING_MODEL ?? null,
|
|
661
|
+
EMBEDDING_DIMENSIONS: env.EMBEDDING_DIMENSIONS ?? null
|
|
662
|
+
},
|
|
663
|
+
async init(config, runtime) {
|
|
664
|
+
initializeOpenRouter(config, runtime);
|
|
665
|
+
},
|
|
666
|
+
models: {
|
|
667
|
+
[TEXT_NANO_MODEL_TYPE2]: async (runtime, params) => {
|
|
668
|
+
return handleTextNano(runtime, params);
|
|
669
|
+
},
|
|
670
|
+
[import_core6.ModelType.TEXT_SMALL]: async (runtime, params) => {
|
|
671
|
+
return handleTextSmall(runtime, params);
|
|
672
|
+
},
|
|
673
|
+
[TEXT_MEDIUM_MODEL_TYPE2]: async (runtime, params) => {
|
|
674
|
+
return handleTextMedium(runtime, params);
|
|
675
|
+
},
|
|
676
|
+
[import_core6.ModelType.TEXT_LARGE]: async (runtime, params) => {
|
|
677
|
+
return handleTextLarge(runtime, params);
|
|
678
|
+
},
|
|
679
|
+
[TEXT_MEGA_MODEL_TYPE2]: async (runtime, params) => {
|
|
680
|
+
return handleTextMega(runtime, params);
|
|
681
|
+
},
|
|
682
|
+
[RESPONSE_HANDLER_MODEL_TYPE2]: async (runtime, params) => {
|
|
683
|
+
return handleResponseHandler(runtime, params);
|
|
684
|
+
},
|
|
685
|
+
[ACTION_PLANNER_MODEL_TYPE2]: async (runtime, params) => {
|
|
686
|
+
return handleActionPlanner(runtime, params);
|
|
687
|
+
},
|
|
688
|
+
[import_core6.ModelType.IMAGE_DESCRIPTION]: async (runtime, params) => {
|
|
689
|
+
const description = await handleImageDescription(runtime, params);
|
|
690
|
+
return { title: "", description };
|
|
691
|
+
},
|
|
692
|
+
[import_core6.ModelType.IMAGE]: async (runtime, params) => {
|
|
693
|
+
const result = await handleImageGeneration(runtime, params);
|
|
694
|
+
return [{ url: result.imageUrl }];
|
|
695
|
+
},
|
|
696
|
+
[import_core6.ModelType.TEXT_EMBEDDING]: async (runtime, params) => {
|
|
697
|
+
return handleTextEmbedding(runtime, params);
|
|
698
|
+
}
|
|
699
|
+
},
|
|
700
|
+
tests: [
|
|
701
|
+
{
|
|
702
|
+
name: "openrouter_plugin_tests",
|
|
703
|
+
tests: [
|
|
704
|
+
{
|
|
705
|
+
name: "openrouter_test_text_small",
|
|
706
|
+
fn: async (runtime) => {
|
|
707
|
+
try {
|
|
708
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
709
|
+
const text = await runModel(import_core6.ModelType.TEXT_SMALL, {
|
|
710
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
711
|
+
});
|
|
712
|
+
if (text.length === 0) {
|
|
713
|
+
throw new Error("Failed to generate text");
|
|
714
|
+
}
|
|
715
|
+
import_core6.logger.log({ text }, "generated with test_text_small");
|
|
716
|
+
} catch (error) {
|
|
717
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
718
|
+
import_core6.logger.error(`Error in test_text_small: ${message}`);
|
|
719
|
+
throw error;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
name: "openrouter_test_text_large",
|
|
725
|
+
fn: async (runtime) => {
|
|
726
|
+
try {
|
|
727
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
728
|
+
const text = await runModel(import_core6.ModelType.TEXT_LARGE, {
|
|
729
|
+
prompt: "What is the nature of reality in 10 words?"
|
|
730
|
+
});
|
|
731
|
+
if (text.length === 0) {
|
|
732
|
+
throw new Error("Failed to generate text");
|
|
733
|
+
}
|
|
734
|
+
import_core6.logger.log({ text }, "generated with test_text_large");
|
|
735
|
+
} catch (error) {
|
|
736
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
737
|
+
import_core6.logger.error(`Error in test_text_large: ${message}`);
|
|
738
|
+
throw error;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
{
|
|
743
|
+
name: "openrouter_test_structured_output_via_text_large",
|
|
744
|
+
fn: async (runtime) => {
|
|
745
|
+
try {
|
|
746
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
747
|
+
const result = await runModel(import_core6.ModelType.TEXT_LARGE, {
|
|
748
|
+
prompt: "Create a simple JSON object with a message field saying hello",
|
|
749
|
+
responseSchema: {
|
|
750
|
+
type: "object",
|
|
751
|
+
properties: { message: { type: "string" } },
|
|
752
|
+
required: ["message"]
|
|
753
|
+
}
|
|
754
|
+
});
|
|
755
|
+
import_core6.logger.log({ result }, "Generated structured output via TEXT_LARGE");
|
|
756
|
+
if (!result) {
|
|
757
|
+
throw new Error("Failed to generate structured output");
|
|
758
|
+
}
|
|
759
|
+
} catch (error) {
|
|
760
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
761
|
+
import_core6.logger.error(`Error in test_structured_output_via_text_large: ${message}`);
|
|
762
|
+
throw error;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
{
|
|
767
|
+
name: "openrouter_test_text_embedding",
|
|
768
|
+
fn: async (runtime) => {
|
|
769
|
+
try {
|
|
770
|
+
const runModel = runtime.useModel.bind(runtime);
|
|
771
|
+
const embedding = await runModel(import_core6.ModelType.TEXT_EMBEDDING, {
|
|
772
|
+
text: "Hello, world!"
|
|
773
|
+
});
|
|
774
|
+
import_core6.logger.log({ embedding }, "embedding");
|
|
775
|
+
} catch (error) {
|
|
776
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
777
|
+
import_core6.logger.error(`Error in test_text_embedding: ${message}`);
|
|
778
|
+
throw error;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
]
|
|
783
|
+
}
|
|
784
|
+
]
|
|
785
|
+
};
|
|
786
|
+
var plugin_default = openrouterPlugin;
|
|
787
|
+
|
|
788
|
+
// index.ts
|
|
789
|
+
var openrouterPlugin2 = plugin_default;
|
|
790
|
+
|
|
791
|
+
//# debugId=9F505D89FE343F3864756E2164756E21
|
|
121
792
|
//# sourceMappingURL=index.node.cjs.map
|