@elizaos/plugin-openai 2.0.0-alpha.8 → 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.
@@ -2,35 +2,45 @@ var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __hasOwnProp = Object.prototype.hasOwnProperty;
5
- var __moduleCache = /* @__PURE__ */ new WeakMap;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
6
8
  var __toCommonJS = (from) => {
7
- var entry = __moduleCache.get(from), desc;
8
- if (entry)
9
- return entry;
10
- entry = __defProp({}, "__esModule", { value: true });
11
- if (from && typeof from === "object" || typeof from === "function")
12
- __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
13
- get: () => from[key],
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- }));
16
- __moduleCache.set(from, entry);
17
- return entry;
9
+ var entry = (__moduleCache ??= new WeakMap()).get(from),
10
+ desc;
11
+ if (entry) return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if ((from && typeof from === "object") || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
18
23
  };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
19
29
  var __export = (target, all) => {
20
- for (var name in all)
21
- __defProp(target, name, {
22
- get: all[name],
23
- enumerable: true,
24
- configurable: true,
25
- set: (newValue) => all[name] = () => newValue
26
- });
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name),
36
+ });
27
37
  };
28
38
 
29
39
  // index.node.ts
30
40
  var exports_index_node = {};
31
41
  __export(exports_index_node, {
32
- openaiPlugin: () => openaiPlugin,
33
- default: () => typescript_default
42
+ openaiPlugin: () => openaiPlugin,
43
+ default: () => index_node_default,
34
44
  });
35
45
  module.exports = __toCommonJS(exports_index_node);
36
46
 
@@ -43,149 +53,231 @@ var import_core2 = require("@elizaos/core");
43
53
  // utils/config.ts
44
54
  var import_core = require("@elizaos/core");
45
55
  function getEnvValue(key) {
46
- if (typeof process === "undefined" || !process.env) {
47
- return;
48
- }
49
- const value = process.env[key];
50
- return value === undefined ? undefined : String(value);
56
+ if (typeof process === "undefined" || !process.env) {
57
+ return;
58
+ }
59
+ const value = process.env[key];
60
+ return value === undefined ? undefined : String(value);
51
61
  }
52
62
  function getSetting(runtime, key, defaultValue) {
53
- const value = runtime.getSetting(key);
54
- if (value !== undefined && value !== null) {
55
- return String(value);
56
- }
57
- return getEnvValue(key) ?? defaultValue;
63
+ const value = runtime.getSetting(key);
64
+ if (value !== undefined && value !== null) {
65
+ return String(value);
66
+ }
67
+ return getEnvValue(key) ?? defaultValue;
58
68
  }
59
69
  function getNumericSetting(runtime, key, defaultValue) {
60
- const value = getSetting(runtime, key);
61
- if (value === undefined) {
62
- return defaultValue;
63
- }
64
- const parsed = Number.parseInt(value, 10);
65
- if (!Number.isFinite(parsed)) {
66
- throw new Error(`Setting '${key}' must be a valid integer, got: ${value}`);
67
- }
68
- return parsed;
70
+ const value = getSetting(runtime, key);
71
+ if (value === undefined) {
72
+ return defaultValue;
73
+ }
74
+ const parsed = Number.parseInt(value, 10);
75
+ if (!Number.isFinite(parsed)) {
76
+ throw new Error(`Setting '${key}' must be a valid integer, got: ${value}`);
77
+ }
78
+ return parsed;
69
79
  }
70
80
  function getBooleanSetting(runtime, key, defaultValue) {
71
- const value = getSetting(runtime, key);
72
- if (value === undefined) {
73
- return defaultValue;
74
- }
75
- const normalized = value.toLowerCase();
76
- return normalized === "true" || normalized === "1" || normalized === "yes";
81
+ const value = getSetting(runtime, key);
82
+ if (value === undefined) {
83
+ return defaultValue;
84
+ }
85
+ const normalized = value.toLowerCase();
86
+ return normalized === "true" || normalized === "1" || normalized === "yes";
77
87
  }
78
88
  function isBrowser() {
79
- return typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
89
+ return (
90
+ typeof globalThis !== "undefined" &&
91
+ typeof globalThis.document !== "undefined"
92
+ );
80
93
  }
81
94
  function isProxyMode(runtime) {
82
- return isBrowser() && !!getSetting(runtime, "OPENAI_BROWSER_BASE_URL");
95
+ return isBrowser() && !!getSetting(runtime, "OPENAI_BROWSER_BASE_URL");
83
96
  }
84
97
  function getApiKey(runtime) {
85
- return getSetting(runtime, "OPENAI_API_KEY");
98
+ return getSetting(runtime, "OPENAI_API_KEY");
86
99
  }
87
100
  function getEmbeddingApiKey(runtime) {
88
- const embeddingApiKey = getSetting(runtime, "OPENAI_EMBEDDING_API_KEY");
89
- if (embeddingApiKey) {
90
- import_core.logger.debug("[OpenAI] Using specific embedding API key");
91
- return embeddingApiKey;
92
- }
93
- import_core.logger.debug("[OpenAI] Falling back to general API key for embeddings");
94
- return getApiKey(runtime);
101
+ const embeddingApiKey = getSetting(runtime, "OPENAI_EMBEDDING_API_KEY");
102
+ if (embeddingApiKey) {
103
+ import_core.logger.debug("[OpenAI] Using specific embedding API key");
104
+ return embeddingApiKey;
105
+ }
106
+ import_core.logger.debug(
107
+ "[OpenAI] Falling back to general API key for embeddings",
108
+ );
109
+ return getApiKey(runtime);
95
110
  }
96
111
  function getAuthHeader(runtime, forEmbedding = false) {
97
- if (isBrowser() && !getBooleanSetting(runtime, "OPENAI_ALLOW_BROWSER_API_KEY", false)) {
98
- return {};
99
- }
100
- const key = forEmbedding ? getEmbeddingApiKey(runtime) : getApiKey(runtime);
101
- return key ? { Authorization: `Bearer ${key}` } : {};
112
+ if (
113
+ isBrowser() &&
114
+ !getBooleanSetting(runtime, "OPENAI_ALLOW_BROWSER_API_KEY", false)
115
+ ) {
116
+ return {};
117
+ }
118
+ const key = forEmbedding ? getEmbeddingApiKey(runtime) : getApiKey(runtime);
119
+ return key ? { Authorization: `Bearer ${key}` } : {};
102
120
  }
103
121
  function getBaseURL(runtime) {
104
- const browserURL = getSetting(runtime, "OPENAI_BROWSER_BASE_URL");
105
- const baseURL = isBrowser() && browserURL ? browserURL : getSetting(runtime, "OPENAI_BASE_URL") ?? "https://api.openai.com/v1";
106
- import_core.logger.debug(`[OpenAI] Base URL: ${baseURL}`);
107
- return baseURL;
122
+ const browserURL = getSetting(runtime, "OPENAI_BROWSER_BASE_URL");
123
+ const baseURL =
124
+ isBrowser() && browserURL
125
+ ? browserURL
126
+ : (getSetting(runtime, "OPENAI_BASE_URL") ?? "https://api.openai.com/v1");
127
+ import_core.logger.debug(`[OpenAI] Base URL: ${baseURL}`);
128
+ return baseURL;
108
129
  }
109
130
  function getEmbeddingBaseURL(runtime) {
110
- const embeddingURL = isBrowser() ? getSetting(runtime, "OPENAI_BROWSER_EMBEDDING_URL") ?? getSetting(runtime, "OPENAI_BROWSER_BASE_URL") : getSetting(runtime, "OPENAI_EMBEDDING_URL");
111
- if (embeddingURL) {
112
- import_core.logger.debug(`[OpenAI] Using embedding base URL: ${embeddingURL}`);
113
- return embeddingURL;
114
- }
115
- import_core.logger.debug("[OpenAI] Falling back to general base URL for embeddings");
116
- return getBaseURL(runtime);
131
+ const embeddingURL = isBrowser()
132
+ ? (getSetting(runtime, "OPENAI_BROWSER_EMBEDDING_URL") ??
133
+ getSetting(runtime, "OPENAI_BROWSER_BASE_URL"))
134
+ : getSetting(runtime, "OPENAI_EMBEDDING_URL");
135
+ if (embeddingURL) {
136
+ import_core.logger.debug(
137
+ `[OpenAI] Using embedding base URL: ${embeddingURL}`,
138
+ );
139
+ return embeddingURL;
140
+ }
141
+ import_core.logger.debug(
142
+ "[OpenAI] Falling back to general base URL for embeddings",
143
+ );
144
+ return getBaseURL(runtime);
117
145
  }
118
146
  function getSmallModel(runtime) {
119
- return getSetting(runtime, "OPENAI_SMALL_MODEL") ?? getSetting(runtime, "SMALL_MODEL") ?? "gpt-5-mini";
147
+ return (
148
+ getSetting(runtime, "OPENAI_SMALL_MODEL") ??
149
+ getSetting(runtime, "SMALL_MODEL") ??
150
+ "gpt-5-mini"
151
+ );
152
+ }
153
+ function getNanoModel(runtime) {
154
+ return (
155
+ getSetting(runtime, "OPENAI_NANO_MODEL") ??
156
+ getSetting(runtime, "NANO_MODEL") ??
157
+ getSmallModel(runtime)
158
+ );
159
+ }
160
+ function getMediumModel(runtime) {
161
+ return (
162
+ getSetting(runtime, "OPENAI_MEDIUM_MODEL") ??
163
+ getSetting(runtime, "MEDIUM_MODEL") ??
164
+ getSmallModel(runtime)
165
+ );
120
166
  }
121
167
  function getLargeModel(runtime) {
122
- return getSetting(runtime, "OPENAI_LARGE_MODEL") ?? getSetting(runtime, "LARGE_MODEL") ?? "gpt-5";
168
+ return (
169
+ getSetting(runtime, "OPENAI_LARGE_MODEL") ??
170
+ getSetting(runtime, "LARGE_MODEL") ??
171
+ "gpt-5"
172
+ );
173
+ }
174
+ function getMegaModel(runtime) {
175
+ return (
176
+ getSetting(runtime, "OPENAI_MEGA_MODEL") ??
177
+ getSetting(runtime, "MEGA_MODEL") ??
178
+ getLargeModel(runtime)
179
+ );
180
+ }
181
+ function getResponseHandlerModel(runtime) {
182
+ return (
183
+ getSetting(runtime, "OPENAI_RESPONSE_HANDLER_MODEL") ??
184
+ getSetting(runtime, "OPENAI_SHOULD_RESPOND_MODEL") ??
185
+ getSetting(runtime, "RESPONSE_HANDLER_MODEL") ??
186
+ getSetting(runtime, "SHOULD_RESPOND_MODEL") ??
187
+ getNanoModel(runtime)
188
+ );
189
+ }
190
+ function getActionPlannerModel(runtime) {
191
+ return (
192
+ getSetting(runtime, "OPENAI_ACTION_PLANNER_MODEL") ??
193
+ getSetting(runtime, "OPENAI_PLANNER_MODEL") ??
194
+ getSetting(runtime, "ACTION_PLANNER_MODEL") ??
195
+ getSetting(runtime, "PLANNER_MODEL") ??
196
+ getMediumModel(runtime)
197
+ );
123
198
  }
124
199
  function getEmbeddingModel(runtime) {
125
- return getSetting(runtime, "OPENAI_EMBEDDING_MODEL") ?? "text-embedding-3-small";
200
+ return (
201
+ getSetting(runtime, "OPENAI_EMBEDDING_MODEL") ?? "text-embedding-3-small"
202
+ );
126
203
  }
127
204
  function getImageDescriptionModel(runtime) {
128
- return getSetting(runtime, "OPENAI_IMAGE_DESCRIPTION_MODEL") ?? "gpt-5-mini";
205
+ return getSetting(runtime, "OPENAI_IMAGE_DESCRIPTION_MODEL") ?? "gpt-5-mini";
129
206
  }
130
207
  function getTranscriptionModel(runtime) {
131
- return getSetting(runtime, "OPENAI_TRANSCRIPTION_MODEL") ?? "gpt-5-mini-transcribe";
208
+ return (
209
+ getSetting(runtime, "OPENAI_TRANSCRIPTION_MODEL") ?? "gpt-5-mini-transcribe"
210
+ );
132
211
  }
133
212
  function getTTSModel(runtime) {
134
- return getSetting(runtime, "OPENAI_TTS_MODEL") ?? "tts-1";
213
+ return getSetting(runtime, "OPENAI_TTS_MODEL") ?? "tts-1";
135
214
  }
136
215
  function getTTSVoice(runtime) {
137
- return getSetting(runtime, "OPENAI_TTS_VOICE") ?? "nova";
216
+ return getSetting(runtime, "OPENAI_TTS_VOICE") ?? "nova";
138
217
  }
139
218
  function getTTSInstructions(runtime) {
140
- return getSetting(runtime, "OPENAI_TTS_INSTRUCTIONS") ?? "";
219
+ return getSetting(runtime, "OPENAI_TTS_INSTRUCTIONS") ?? "";
141
220
  }
142
221
  function getImageModel(runtime) {
143
- return getSetting(runtime, "OPENAI_IMAGE_MODEL") ?? "dall-e-3";
222
+ return getSetting(runtime, "OPENAI_IMAGE_MODEL") ?? "dall-e-3";
144
223
  }
145
224
  function getExperimentalTelemetry(runtime) {
146
- return getBooleanSetting(runtime, "OPENAI_EXPERIMENTAL_TELEMETRY", false);
225
+ return getBooleanSetting(runtime, "OPENAI_EXPERIMENTAL_TELEMETRY", false);
147
226
  }
148
227
  function getEmbeddingDimensions(runtime) {
149
- return getNumericSetting(runtime, "OPENAI_EMBEDDING_DIMENSIONS", 1536);
228
+ return getNumericSetting(runtime, "OPENAI_EMBEDDING_DIMENSIONS", 1536);
150
229
  }
151
230
  function getImageDescriptionMaxTokens(runtime) {
152
- return getNumericSetting(runtime, "OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS", 8192);
231
+ return getNumericSetting(
232
+ runtime,
233
+ "OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS",
234
+ 8192,
235
+ );
153
236
  }
154
237
  function getResearchModel(runtime) {
155
- return getSetting(runtime, "OPENAI_RESEARCH_MODEL") ?? "o3-deep-research";
238
+ return getSetting(runtime, "OPENAI_RESEARCH_MODEL") ?? "o3-deep-research";
156
239
  }
157
240
  function getResearchTimeout(runtime) {
158
- return getNumericSetting(runtime, "OPENAI_RESEARCH_TIMEOUT", 3600000);
241
+ return getNumericSetting(runtime, "OPENAI_RESEARCH_TIMEOUT", 3600000);
159
242
  }
160
243
 
161
244
  // init.ts
162
245
  globalThis.AI_SDK_LOG_WARNINGS ??= false;
163
246
  function initializeOpenAI(_config, runtime) {
164
- validateOpenAIConfiguration(runtime);
247
+ validateOpenAIConfiguration(runtime);
165
248
  }
166
249
  async function validateOpenAIConfiguration(runtime) {
167
- if (isBrowser()) {
168
- import_core2.logger.debug("[OpenAI] Skipping API validation in browser environment");
169
- return;
170
- }
171
- const apiKey = getApiKey(runtime);
172
- if (!apiKey) {
173
- import_core2.logger.warn("[OpenAI] OPENAI_API_KEY is not configured. " + "OpenAI functionality will fail until a valid API key is provided.");
174
- return;
175
- }
176
- try {
177
- const baseURL = getBaseURL(runtime);
178
- const response = await fetch(`${baseURL}/models`, {
179
- headers: getAuthHeader(runtime)
180
- });
181
- if (!response.ok) {
182
- import_core2.logger.warn(`[OpenAI] API key validation failed: ${response.status} ${response.statusText}. ` + "Please verify your OPENAI_API_KEY is correct.");
183
- return;
184
- }
185
- } catch (error) {
186
- const message = error instanceof Error ? error.message : String(error);
187
- import_core2.logger.warn(`[OpenAI] API validation error: ${message}. OpenAI functionality may be limited.`);
188
- }
250
+ if (isBrowser()) {
251
+ import_core2.logger.debug(
252
+ "[OpenAI] Skipping API validation in browser environment",
253
+ );
254
+ return;
255
+ }
256
+ const apiKey = getApiKey(runtime);
257
+ if (!apiKey) {
258
+ import_core2.logger.warn(
259
+ "[OpenAI] OPENAI_API_KEY is not configured. " +
260
+ "OpenAI functionality will fail until a valid API key is provided.",
261
+ );
262
+ return;
263
+ }
264
+ try {
265
+ const baseURL = getBaseURL(runtime);
266
+ const response = await fetch(`${baseURL}/models`, {
267
+ headers: getAuthHeader(runtime),
268
+ });
269
+ if (!response.ok) {
270
+ import_core2.logger.warn(
271
+ `[OpenAI] API key validation failed: ${response.status} ${response.statusText}. Please verify your OPENAI_API_KEY is correct.`,
272
+ );
273
+ return;
274
+ }
275
+ } catch (error) {
276
+ const message = error instanceof Error ? error.message : String(error);
277
+ import_core2.logger.warn(
278
+ `[OpenAI] API validation error: ${message}. OpenAI functionality may be limited.`,
279
+ );
280
+ }
189
281
  }
190
282
 
191
283
  // models/audio.ts
@@ -194,224 +286,260 @@ var import_core4 = require("@elizaos/core");
194
286
  // utils/audio.ts
195
287
  var import_core3 = require("@elizaos/core");
196
288
  var MAGIC_BYTES = {
197
- WAV: {
198
- HEADER: [82, 73, 70, 70],
199
- IDENTIFIER: [87, 65, 86, 69]
200
- },
201
- MP3_ID3: [73, 68, 51],
202
- OGG: [79, 103, 103, 83],
203
- FLAC: [102, 76, 97, 67],
204
- FTYP: [102, 116, 121, 112],
205
- WEBM_EBML: [26, 69, 223, 163]
289
+ WAV: {
290
+ HEADER: [82, 73, 70, 70],
291
+ IDENTIFIER: [87, 65, 86, 69],
292
+ },
293
+ MP3_ID3: [73, 68, 51],
294
+ OGG: [79, 103, 103, 83],
295
+ FLAC: [102, 76, 97, 67],
296
+ FTYP: [102, 116, 121, 112],
297
+ WEBM_EBML: [26, 69, 223, 163],
206
298
  };
207
299
  var MIN_DETECTION_BUFFER_SIZE = 12;
208
300
  function matchBytes(buffer, offset, expected) {
209
- for (let i = 0;i < expected.length; i++) {
210
- const expectedByte = expected[i];
211
- if (expectedByte === undefined || buffer[offset + i] !== expectedByte) {
212
- return false;
213
- }
214
- }
215
- return true;
301
+ for (let i = 0; i < expected.length; i++) {
302
+ const expectedByte = expected[i];
303
+ if (expectedByte === undefined || buffer[offset + i] !== expectedByte) {
304
+ return false;
305
+ }
306
+ }
307
+ return true;
216
308
  }
217
309
  function detectAudioMimeType(buffer) {
218
- if (buffer.length < MIN_DETECTION_BUFFER_SIZE) {
219
- return "application/octet-stream";
220
- }
221
- if (matchBytes(buffer, 0, MAGIC_BYTES.WAV.HEADER) && matchBytes(buffer, 8, MAGIC_BYTES.WAV.IDENTIFIER)) {
222
- return "audio/wav";
223
- }
224
- const firstByte = buffer[0];
225
- const secondByte = buffer[1];
226
- if (matchBytes(buffer, 0, MAGIC_BYTES.MP3_ID3) || firstByte === 255 && secondByte !== undefined && (secondByte & 224) === 224) {
227
- return "audio/mpeg";
228
- }
229
- if (matchBytes(buffer, 0, MAGIC_BYTES.OGG)) {
230
- return "audio/ogg";
231
- }
232
- if (matchBytes(buffer, 0, MAGIC_BYTES.FLAC)) {
233
- return "audio/flac";
234
- }
235
- if (matchBytes(buffer, 4, MAGIC_BYTES.FTYP)) {
236
- return "audio/mp4";
237
- }
238
- if (matchBytes(buffer, 0, MAGIC_BYTES.WEBM_EBML)) {
239
- return "audio/webm";
240
- }
241
- import_core3.logger.warn("Could not detect audio format from buffer, using generic binary type");
242
- return "application/octet-stream";
310
+ if (buffer.length < MIN_DETECTION_BUFFER_SIZE) {
311
+ return "application/octet-stream";
312
+ }
313
+ if (
314
+ matchBytes(buffer, 0, MAGIC_BYTES.WAV.HEADER) &&
315
+ matchBytes(buffer, 8, MAGIC_BYTES.WAV.IDENTIFIER)
316
+ ) {
317
+ return "audio/wav";
318
+ }
319
+ const firstByte = buffer[0];
320
+ const secondByte = buffer[1];
321
+ if (
322
+ matchBytes(buffer, 0, MAGIC_BYTES.MP3_ID3) ||
323
+ (firstByte === 255 &&
324
+ secondByte !== undefined &&
325
+ (secondByte & 224) === 224)
326
+ ) {
327
+ return "audio/mpeg";
328
+ }
329
+ if (matchBytes(buffer, 0, MAGIC_BYTES.OGG)) {
330
+ return "audio/ogg";
331
+ }
332
+ if (matchBytes(buffer, 0, MAGIC_BYTES.FLAC)) {
333
+ return "audio/flac";
334
+ }
335
+ if (matchBytes(buffer, 4, MAGIC_BYTES.FTYP)) {
336
+ return "audio/mp4";
337
+ }
338
+ if (matchBytes(buffer, 0, MAGIC_BYTES.WEBM_EBML)) {
339
+ return "audio/webm";
340
+ }
341
+ import_core3.logger.warn(
342
+ "Could not detect audio format from buffer, using generic binary type",
343
+ );
344
+ return "application/octet-stream";
243
345
  }
244
346
  function getExtensionForMimeType(mimeType) {
245
- switch (mimeType) {
246
- case "audio/wav":
247
- return "wav";
248
- case "audio/mpeg":
249
- return "mp3";
250
- case "audio/ogg":
251
- return "ogg";
252
- case "audio/flac":
253
- return "flac";
254
- case "audio/mp4":
255
- return "m4a";
256
- case "audio/webm":
257
- return "webm";
258
- case "application/octet-stream":
259
- return "bin";
260
- }
347
+ switch (mimeType) {
348
+ case "audio/wav":
349
+ return "wav";
350
+ case "audio/mpeg":
351
+ return "mp3";
352
+ case "audio/ogg":
353
+ return "ogg";
354
+ case "audio/flac":
355
+ return "flac";
356
+ case "audio/mp4":
357
+ return "m4a";
358
+ case "audio/webm":
359
+ return "webm";
360
+ case "application/octet-stream":
361
+ return "bin";
362
+ }
261
363
  }
262
364
  function getFilenameForMimeType(mimeType) {
263
- const ext = getExtensionForMimeType(mimeType);
264
- return `recording.${ext}`;
365
+ const ext = getExtensionForMimeType(mimeType);
366
+ return `recording.${ext}`;
265
367
  }
266
368
 
267
369
  // models/audio.ts
268
370
  function isBlobOrFile(value) {
269
- return value instanceof Blob || value instanceof File;
371
+ return value instanceof Blob || value instanceof File;
270
372
  }
271
373
  function isBuffer(value) {
272
- return Buffer.isBuffer(value);
374
+ return Buffer.isBuffer(value);
273
375
  }
274
376
  function isLocalTranscriptionParams(value) {
275
- return typeof value === "object" && value !== null && "audio" in value && (isBlobOrFile(value.audio) || isBuffer(value.audio));
377
+ return (
378
+ typeof value === "object" &&
379
+ value !== null &&
380
+ "audio" in value &&
381
+ (isBlobOrFile(value.audio) || isBuffer(value.audio))
382
+ );
276
383
  }
277
384
  function isCoreTranscriptionParams(value) {
278
- return typeof value === "object" && value !== null && "audioUrl" in value && typeof value.audioUrl === "string";
385
+ return (
386
+ typeof value === "object" &&
387
+ value !== null &&
388
+ "audioUrl" in value &&
389
+ typeof value.audioUrl === "string"
390
+ );
279
391
  }
280
392
  async function fetchAudioFromUrl(url) {
281
- const response = await fetch(url);
282
- if (!response.ok) {
283
- throw new Error(`Failed to fetch audio from URL: ${response.status}`);
284
- }
285
- return response.blob();
393
+ const response = await fetch(url);
394
+ if (!response.ok) {
395
+ throw new Error(`Failed to fetch audio from URL: ${response.status}`);
396
+ }
397
+ return response.blob();
286
398
  }
287
399
  async function handleTranscription(runtime, input) {
288
- let modelName = getTranscriptionModel(runtime);
289
- let blob;
290
- let extraParams = {};
291
- if (typeof input === "string") {
292
- import_core4.logger.debug(`[OpenAI] Fetching audio from URL: ${input}`);
293
- blob = await fetchAudioFromUrl(input);
294
- } else if (isBlobOrFile(input)) {
295
- blob = input;
296
- } else if (isBuffer(input)) {
297
- const mimeType2 = detectAudioMimeType(input);
298
- import_core4.logger.debug(`[OpenAI] Auto-detected audio MIME type: ${mimeType2}`);
299
- blob = new Blob([new Uint8Array(input)], { type: mimeType2 });
300
- } else if (isLocalTranscriptionParams(input)) {
301
- extraParams = input;
302
- if (input.model) {
303
- modelName = input.model;
304
- }
305
- if (isBuffer(input.audio)) {
306
- const mimeType2 = input.mimeType ?? detectAudioMimeType(input.audio);
307
- import_core4.logger.debug(`[OpenAI] Using MIME type: ${mimeType2}`);
308
- blob = new Blob([new Uint8Array(input.audio)], { type: mimeType2 });
309
- } else {
310
- blob = input.audio;
311
- }
312
- } else if (isCoreTranscriptionParams(input)) {
313
- import_core4.logger.debug(`[OpenAI] Fetching audio from URL: ${input.audioUrl}`);
314
- blob = await fetchAudioFromUrl(input.audioUrl);
315
- extraParams = { prompt: input.prompt };
316
- } else {
317
- throw new Error("TRANSCRIPTION expects Blob, File, Buffer, URL string, or TranscriptionParams object");
318
- }
319
- import_core4.logger.debug(`[OpenAI] Using TRANSCRIPTION model: ${modelName}`);
320
- const mimeType = blob.type || "audio/webm";
321
- const filename = blob.name || getFilenameForMimeType(mimeType.startsWith("audio/") ? mimeType : "audio/webm");
322
- const formData = new FormData;
323
- formData.append("file", blob, filename);
324
- formData.append("model", modelName);
325
- if (extraParams.language) {
326
- formData.append("language", extraParams.language);
327
- }
328
- if (extraParams.responseFormat) {
329
- formData.append("response_format", extraParams.responseFormat);
330
- }
331
- if (extraParams.prompt) {
332
- formData.append("prompt", extraParams.prompt);
333
- }
334
- if (extraParams.temperature !== undefined) {
335
- formData.append("temperature", String(extraParams.temperature));
336
- }
337
- if (extraParams.timestampGranularities) {
338
- for (const granularity of extraParams.timestampGranularities) {
339
- formData.append("timestamp_granularities[]", granularity);
340
- }
341
- }
342
- const baseURL = getBaseURL(runtime);
343
- const response = await fetch(`${baseURL}/audio/transcriptions`, {
344
- method: "POST",
345
- headers: getAuthHeader(runtime),
346
- body: formData
347
- });
348
- if (!response.ok) {
349
- const errorText = await response.text().catch(() => "Unknown error");
350
- throw new Error(`OpenAI transcription failed: ${response.status} ${response.statusText} - ${errorText}`);
351
- }
352
- const data = await response.json();
353
- return data.text;
400
+ let modelName = getTranscriptionModel(runtime);
401
+ let blob;
402
+ let extraParams = {};
403
+ if (typeof input === "string") {
404
+ import_core4.logger.debug(`[OpenAI] Fetching audio from URL: ${input}`);
405
+ blob = await fetchAudioFromUrl(input);
406
+ } else if (isBlobOrFile(input)) {
407
+ blob = input;
408
+ } else if (isBuffer(input)) {
409
+ const mimeType2 = detectAudioMimeType(input);
410
+ import_core4.logger.debug(
411
+ `[OpenAI] Auto-detected audio MIME type: ${mimeType2}`,
412
+ );
413
+ blob = new Blob([new Uint8Array(input)], { type: mimeType2 });
414
+ } else if (isLocalTranscriptionParams(input)) {
415
+ extraParams = input;
416
+ if (input.model) {
417
+ modelName = input.model;
418
+ }
419
+ if (isBuffer(input.audio)) {
420
+ const mimeType2 = input.mimeType ?? detectAudioMimeType(input.audio);
421
+ import_core4.logger.debug(`[OpenAI] Using MIME type: ${mimeType2}`);
422
+ blob = new Blob([new Uint8Array(input.audio)], { type: mimeType2 });
423
+ } else {
424
+ blob = input.audio;
425
+ }
426
+ } else if (isCoreTranscriptionParams(input)) {
427
+ import_core4.logger.debug(
428
+ `[OpenAI] Fetching audio from URL: ${input.audioUrl}`,
429
+ );
430
+ blob = await fetchAudioFromUrl(input.audioUrl);
431
+ extraParams = { prompt: input.prompt };
432
+ } else {
433
+ throw new Error(
434
+ "TRANSCRIPTION expects Blob, File, Buffer, URL string, or TranscriptionParams object",
435
+ );
436
+ }
437
+ import_core4.logger.debug(`[OpenAI] Using TRANSCRIPTION model: ${modelName}`);
438
+ const mimeType = blob.type || "audio/webm";
439
+ const filename =
440
+ blob.name ||
441
+ getFilenameForMimeType(
442
+ mimeType.startsWith("audio/") ? mimeType : "audio/webm",
443
+ );
444
+ const formData = new FormData();
445
+ formData.append("file", blob, filename);
446
+ formData.append("model", modelName);
447
+ if (extraParams.language) {
448
+ formData.append("language", extraParams.language);
449
+ }
450
+ if (extraParams.responseFormat) {
451
+ formData.append("response_format", extraParams.responseFormat);
452
+ }
453
+ if (extraParams.prompt) {
454
+ formData.append("prompt", extraParams.prompt);
455
+ }
456
+ if (extraParams.temperature !== undefined) {
457
+ formData.append("temperature", String(extraParams.temperature));
458
+ }
459
+ if (extraParams.timestampGranularities) {
460
+ for (const granularity of extraParams.timestampGranularities) {
461
+ formData.append("timestamp_granularities[]", granularity);
462
+ }
463
+ }
464
+ const baseURL = getBaseURL(runtime);
465
+ const response = await fetch(`${baseURL}/audio/transcriptions`, {
466
+ method: "POST",
467
+ headers: getAuthHeader(runtime),
468
+ body: formData,
469
+ });
470
+ if (!response.ok) {
471
+ const errorText = await response.text().catch(() => "Unknown error");
472
+ throw new Error(
473
+ `OpenAI transcription failed: ${response.status} ${response.statusText} - ${errorText}`,
474
+ );
475
+ }
476
+ const data = await response.json();
477
+ return data.text;
354
478
  }
355
479
  async function handleTextToSpeech(runtime, input) {
356
- let text;
357
- let voice;
358
- let format = "mp3";
359
- let model;
360
- let instructions;
361
- if (typeof input === "string") {
362
- text = input;
363
- voice = undefined;
364
- } else {
365
- text = input.text;
366
- voice = input.voice;
367
- if ("format" in input && input.format) {
368
- format = input.format;
369
- }
370
- if ("model" in input && input.model) {
371
- model = input.model;
372
- }
373
- if ("instructions" in input && input.instructions) {
374
- instructions = input.instructions;
375
- }
376
- }
377
- model = model ?? getTTSModel(runtime);
378
- voice = voice ?? getTTSVoice(runtime);
379
- instructions = instructions ?? getTTSInstructions(runtime);
380
- import_core4.logger.debug(`[OpenAI] Using TEXT_TO_SPEECH model: ${model}`);
381
- if (!text || text.trim().length === 0) {
382
- throw new Error("TEXT_TO_SPEECH requires non-empty text");
383
- }
384
- if (text.length > 4096) {
385
- throw new Error("TEXT_TO_SPEECH text exceeds 4096 character limit");
386
- }
387
- const validVoices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
388
- if (voice && !validVoices.includes(voice)) {
389
- throw new Error(`Invalid voice: ${voice}. Must be one of: ${validVoices.join(", ")}`);
390
- }
391
- const baseURL = getBaseURL(runtime);
392
- const requestBody = {
393
- model,
394
- voice,
395
- input: text,
396
- response_format: format
397
- };
398
- if (instructions && instructions.length > 0) {
399
- requestBody.instructions = instructions;
400
- }
401
- const response = await fetch(`${baseURL}/audio/speech`, {
402
- method: "POST",
403
- headers: {
404
- ...getAuthHeader(runtime),
405
- "Content-Type": "application/json",
406
- ...format === "mp3" ? { Accept: "audio/mpeg" } : {}
407
- },
408
- body: JSON.stringify(requestBody)
409
- });
410
- if (!response.ok) {
411
- const errorText = await response.text().catch(() => "Unknown error");
412
- throw new Error(`OpenAI TTS failed: ${response.status} ${response.statusText} - ${errorText}`);
413
- }
414
- return response.arrayBuffer();
480
+ let text;
481
+ let voice;
482
+ let format = "mp3";
483
+ let model;
484
+ let instructions;
485
+ if (typeof input === "string") {
486
+ text = input;
487
+ voice = undefined;
488
+ } else {
489
+ text = input.text;
490
+ voice = input.voice;
491
+ if ("format" in input && input.format) {
492
+ format = input.format;
493
+ }
494
+ if ("model" in input && input.model) {
495
+ model = input.model;
496
+ }
497
+ if ("instructions" in input && input.instructions) {
498
+ instructions = input.instructions;
499
+ }
500
+ }
501
+ model = model ?? getTTSModel(runtime);
502
+ voice = voice ?? getTTSVoice(runtime);
503
+ instructions = instructions ?? getTTSInstructions(runtime);
504
+ import_core4.logger.debug(`[OpenAI] Using TEXT_TO_SPEECH model: ${model}`);
505
+ if (!text || text.trim().length === 0) {
506
+ throw new Error("TEXT_TO_SPEECH requires non-empty text");
507
+ }
508
+ if (text.length > 4096) {
509
+ throw new Error("TEXT_TO_SPEECH text exceeds 4096 character limit");
510
+ }
511
+ const validVoices = ["alloy", "echo", "fable", "onyx", "nova", "shimmer"];
512
+ if (voice && !validVoices.includes(voice)) {
513
+ throw new Error(
514
+ `Invalid voice: ${voice}. Must be one of: ${validVoices.join(", ")}`,
515
+ );
516
+ }
517
+ const baseURL = getBaseURL(runtime);
518
+ const requestBody = {
519
+ model,
520
+ voice,
521
+ input: text,
522
+ response_format: format,
523
+ };
524
+ if (instructions && instructions.length > 0) {
525
+ requestBody.instructions = instructions;
526
+ }
527
+ const response = await fetch(`${baseURL}/audio/speech`, {
528
+ method: "POST",
529
+ headers: {
530
+ ...getAuthHeader(runtime),
531
+ "Content-Type": "application/json",
532
+ ...(format === "mp3" ? { Accept: "audio/mpeg" } : {}),
533
+ },
534
+ body: JSON.stringify(requestBody),
535
+ });
536
+ if (!response.ok) {
537
+ const errorText = await response.text().catch(() => "Unknown error");
538
+ throw new Error(
539
+ `OpenAI TTS failed: ${response.status} ${response.statusText} - ${errorText}`,
540
+ );
541
+ }
542
+ return response.arrayBuffer();
415
543
  }
416
544
  // models/embedding.ts
417
545
  var import_core6 = require("@elizaos/core");
@@ -420,249 +548,287 @@ var import_core6 = require("@elizaos/core");
420
548
  var import_core5 = require("@elizaos/core");
421
549
  var MAX_PROMPT_LENGTH = 200;
422
550
  function truncatePrompt(prompt) {
423
- if (prompt.length <= MAX_PROMPT_LENGTH) {
424
- return prompt;
425
- }
426
- return `${prompt.slice(0, MAX_PROMPT_LENGTH)}…`;
551
+ if (prompt.length <= MAX_PROMPT_LENGTH) {
552
+ return prompt;
553
+ }
554
+ return `${prompt.slice(0, MAX_PROMPT_LENGTH)}…`;
427
555
  }
428
556
  function normalizeUsage(usage) {
429
- if ("promptTokens" in usage) {
430
- const promptTokensDetails = "promptTokensDetails" in usage ? usage.promptTokensDetails : undefined;
431
- const cachedPromptTokens = usage.cachedPromptTokens ?? promptTokensDetails?.cachedTokens;
432
- return {
433
- promptTokens: usage.promptTokens ?? 0,
434
- completionTokens: usage.completionTokens ?? 0,
435
- totalTokens: usage.totalTokens ?? (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0),
436
- cachedPromptTokens
437
- };
438
- }
439
- if ("inputTokens" in usage || "outputTokens" in usage) {
440
- const input = usage.inputTokens ?? 0;
441
- const output = usage.outputTokens ?? 0;
442
- const total = usage.totalTokens ?? input + output;
443
- return {
444
- promptTokens: input,
445
- completionTokens: output,
446
- totalTokens: total,
447
- cachedPromptTokens: usage.cachedInputTokens
448
- };
449
- }
450
- return {
451
- promptTokens: 0,
452
- completionTokens: 0,
453
- totalTokens: 0
454
- };
557
+ if ("promptTokens" in usage) {
558
+ const promptTokensDetails =
559
+ "promptTokensDetails" in usage ? usage.promptTokensDetails : undefined;
560
+ const cachedPromptTokens =
561
+ usage.cachedPromptTokens ?? promptTokensDetails?.cachedTokens;
562
+ return {
563
+ promptTokens: usage.promptTokens ?? 0,
564
+ completionTokens: usage.completionTokens ?? 0,
565
+ totalTokens:
566
+ usage.totalTokens ??
567
+ (usage.promptTokens ?? 0) + (usage.completionTokens ?? 0),
568
+ cachedPromptTokens,
569
+ };
570
+ }
571
+ if ("inputTokens" in usage || "outputTokens" in usage) {
572
+ const input = usage.inputTokens ?? 0;
573
+ const output = usage.outputTokens ?? 0;
574
+ const total = usage.totalTokens ?? input + output;
575
+ return {
576
+ promptTokens: input,
577
+ completionTokens: output,
578
+ totalTokens: total,
579
+ cachedPromptTokens: usage.cachedInputTokens,
580
+ };
581
+ }
582
+ return {
583
+ promptTokens: 0,
584
+ completionTokens: 0,
585
+ totalTokens: 0,
586
+ };
455
587
  }
456
588
  function emitModelUsageEvent(runtime, type, prompt, usage) {
457
- const normalized = normalizeUsage(usage);
458
- const payload = {
459
- runtime,
460
- source: "openai",
461
- provider: "openai",
462
- type,
463
- prompt: truncatePrompt(prompt),
464
- tokens: {
465
- prompt: normalized.promptTokens,
466
- completion: normalized.completionTokens,
467
- total: normalized.totalTokens,
468
- ...normalized.cachedPromptTokens !== undefined ? { cached: normalized.cachedPromptTokens } : {}
469
- }
470
- };
471
- runtime.emitEvent(import_core5.EventType.MODEL_USED, payload);
589
+ const normalized = normalizeUsage(usage);
590
+ const payload = {
591
+ runtime,
592
+ source: "openai",
593
+ provider: "openai",
594
+ type,
595
+ prompt: truncatePrompt(prompt),
596
+ tokens: {
597
+ prompt: normalized.promptTokens,
598
+ completion: normalized.completionTokens,
599
+ total: normalized.totalTokens,
600
+ ...(normalized.cachedPromptTokens !== undefined
601
+ ? { cached: normalized.cachedPromptTokens }
602
+ : {}),
603
+ },
604
+ };
605
+ runtime.emitEvent(import_core5.EventType.MODEL_USED, payload);
472
606
  }
473
607
 
474
608
  // models/embedding.ts
475
- var MAX_EMBEDDING_TOKENS = 8000;
476
609
  function validateDimension(dimension) {
477
- const validDimensions = Object.values(import_core6.VECTOR_DIMS);
478
- if (!validDimensions.includes(dimension)) {
479
- throw new Error(`Invalid embedding dimension: ${dimension}. Must be one of: ${validDimensions.join(", ")}`);
480
- }
481
- return dimension;
610
+ const validDimensions = Object.values(import_core6.VECTOR_DIMS);
611
+ if (!validDimensions.includes(dimension)) {
612
+ throw new Error(
613
+ `Invalid embedding dimension: ${dimension}. Must be one of: ${validDimensions.join(", ")}`,
614
+ );
615
+ }
616
+ return dimension;
482
617
  }
483
618
  function extractText(params) {
484
- if (params === null) {
485
- return null;
486
- }
487
- if (typeof params === "string") {
488
- return params;
489
- }
490
- if (typeof params === "object" && typeof params.text === "string") {
491
- return params.text;
492
- }
493
- throw new Error("Invalid embedding params: expected string, { text: string }, or null");
619
+ if (params === null) {
620
+ return null;
621
+ }
622
+ if (typeof params === "string") {
623
+ return params;
624
+ }
625
+ if (typeof params === "object" && typeof params.text === "string") {
626
+ return params.text;
627
+ }
628
+ throw new Error(
629
+ "Invalid embedding params: expected string, { text: string }, or null",
630
+ );
494
631
  }
495
632
  async function handleTextEmbedding(runtime, params) {
496
- const embeddingModel = getEmbeddingModel(runtime);
497
- const embeddingDimension = validateDimension(getEmbeddingDimensions(runtime));
498
- const text = extractText(params);
499
- if (text === null) {
500
- import_core6.logger.debug("[OpenAI] Creating test embedding for initialization");
501
- const testVector = new Array(embeddingDimension).fill(0);
502
- testVector[0] = 0.1;
503
- return testVector;
504
- }
505
- let trimmedText = text.trim();
506
- if (trimmedText.length === 0) {
507
- throw new Error("Cannot generate embedding for empty text");
508
- }
509
- const maxChars = MAX_EMBEDDING_TOKENS * 4;
510
- if (trimmedText.length > maxChars) {
511
- import_core6.logger.warn(`[OpenAI] Embedding input too long (~${Math.ceil(trimmedText.length / 4)} tokens), truncating to ~${MAX_EMBEDDING_TOKENS} tokens`);
512
- trimmedText = trimmedText.slice(0, maxChars);
513
- }
514
- const baseURL = getEmbeddingBaseURL(runtime);
515
- const url = `${baseURL}/embeddings`;
516
- import_core6.logger.debug(`[OpenAI] Generating embedding with model: ${embeddingModel}`);
517
- const response = await fetch(url, {
518
- method: "POST",
519
- headers: {
520
- ...getAuthHeader(runtime, true),
521
- "Content-Type": "application/json"
522
- },
523
- body: JSON.stringify({
524
- model: embeddingModel,
525
- input: trimmedText
526
- })
527
- });
528
- if (!response.ok) {
529
- const errorText = await response.text().catch(() => "Unknown error");
530
- throw new Error(`OpenAI embedding API error: ${response.status} ${response.statusText} - ${errorText}`);
531
- }
532
- const data = await response.json();
533
- const firstResult = data?.data?.[0];
534
- if (!firstResult || !firstResult.embedding) {
535
- throw new Error("OpenAI API returned invalid embedding response structure");
536
- }
537
- const embedding = firstResult.embedding;
538
- if (embedding.length !== embeddingDimension) {
539
- throw new Error(`Embedding dimension mismatch: got ${embedding.length}, expected ${embeddingDimension}. ` + `Check OPENAI_EMBEDDING_DIMENSIONS setting.`);
540
- }
541
- if (data.usage) {
542
- emitModelUsageEvent(runtime, import_core6.ModelType.TEXT_EMBEDDING, trimmedText, {
543
- promptTokens: data.usage.prompt_tokens,
544
- completionTokens: 0,
545
- totalTokens: data.usage.total_tokens
546
- });
547
- }
548
- import_core6.logger.debug(`[OpenAI] Generated embedding with ${embedding.length} dimensions`);
549
- return embedding;
633
+ const embeddingModel = getEmbeddingModel(runtime);
634
+ const embeddingDimension = validateDimension(getEmbeddingDimensions(runtime));
635
+ const text = extractText(params);
636
+ if (text === null) {
637
+ import_core6.logger.debug(
638
+ "[OpenAI] Creating test embedding for initialization",
639
+ );
640
+ const testVector = new Array(embeddingDimension).fill(0);
641
+ testVector[0] = 0.1;
642
+ return testVector;
643
+ }
644
+ let trimmedText = text.trim();
645
+ if (trimmedText.length === 0) {
646
+ throw new Error("Cannot generate embedding for empty text");
647
+ }
648
+ const maxChars = 8000 * 4;
649
+ if (trimmedText.length > maxChars) {
650
+ import_core6.logger.warn(
651
+ `[OpenAI] Embedding input too long (~${Math.ceil(trimmedText.length / 4)} tokens), truncating to ~8000 tokens`,
652
+ );
653
+ trimmedText = trimmedText.slice(0, maxChars);
654
+ }
655
+ const baseURL = getEmbeddingBaseURL(runtime);
656
+ const url = `${baseURL}/embeddings`;
657
+ import_core6.logger.debug(
658
+ `[OpenAI] Generating embedding with model: ${embeddingModel}`,
659
+ );
660
+ const response = await fetch(url, {
661
+ method: "POST",
662
+ headers: {
663
+ ...getAuthHeader(runtime, true),
664
+ "Content-Type": "application/json",
665
+ },
666
+ body: JSON.stringify({
667
+ model: embeddingModel,
668
+ input: trimmedText,
669
+ }),
670
+ });
671
+ if (!response.ok) {
672
+ const errorText = await response.text().catch(() => "Unknown error");
673
+ throw new Error(
674
+ `OpenAI embedding API error: ${response.status} ${response.statusText} - ${errorText}`,
675
+ );
676
+ }
677
+ const data = await response.json();
678
+ const firstResult = data?.data?.[0];
679
+ if (!firstResult?.embedding) {
680
+ throw new Error("OpenAI API returned invalid embedding response structure");
681
+ }
682
+ const embedding = firstResult.embedding;
683
+ if (embedding.length !== embeddingDimension) {
684
+ throw new Error(
685
+ `Embedding dimension mismatch: got ${embedding.length}, expected ${embeddingDimension}. Check OPENAI_EMBEDDING_DIMENSIONS setting.`,
686
+ );
687
+ }
688
+ if (data.usage) {
689
+ emitModelUsageEvent(
690
+ runtime,
691
+ import_core6.ModelType.TEXT_EMBEDDING,
692
+ trimmedText,
693
+ {
694
+ promptTokens: data.usage.prompt_tokens,
695
+ completionTokens: 0,
696
+ totalTokens: data.usage.total_tokens,
697
+ },
698
+ );
699
+ }
700
+ import_core6.logger.debug(
701
+ `[OpenAI] Generated embedding with ${embedding.length} dimensions`,
702
+ );
703
+ return embedding;
550
704
  }
551
705
  // models/image.ts
552
706
  var import_core7 = require("@elizaos/core");
553
- var DEFAULT_IMAGE_DESCRIPTION_PROMPT = "Please analyze this image and provide a title and detailed description.";
707
+ var DEFAULT_IMAGE_DESCRIPTION_PROMPT =
708
+ "Please analyze this image and provide a title and detailed description.";
554
709
  async function handleImageGeneration(runtime, params) {
555
- const modelName = getImageModel(runtime);
556
- const count = params.count ?? 1;
557
- const size = params.size ?? "1024x1024";
558
- const extendedParams = params;
559
- import_core7.logger.debug(`[OpenAI] Using IMAGE model: ${modelName}`);
560
- if (!params.prompt || params.prompt.trim().length === 0) {
561
- throw new Error("IMAGE generation requires a non-empty prompt");
562
- }
563
- if (count < 1 || count > 10) {
564
- throw new Error("IMAGE count must be between 1 and 10");
565
- }
566
- const baseURL = getBaseURL(runtime);
567
- const requestBody = {
568
- model: modelName,
569
- prompt: params.prompt,
570
- n: count,
571
- size
572
- };
573
- if (extendedParams.quality) {
574
- requestBody.quality = extendedParams.quality;
575
- }
576
- if (extendedParams.style) {
577
- requestBody.style = extendedParams.style;
578
- }
579
- const response = await fetch(`${baseURL}/images/generations`, {
580
- method: "POST",
581
- headers: {
582
- ...getAuthHeader(runtime),
583
- "Content-Type": "application/json"
584
- },
585
- body: JSON.stringify(requestBody)
586
- });
587
- if (!response.ok) {
588
- const errorText = await response.text().catch(() => "Unknown error");
589
- throw new Error(`OpenAI image generation failed: ${response.status} ${response.statusText} - ${errorText}`);
590
- }
591
- const data = await response.json();
592
- if (!data.data || data.data.length === 0) {
593
- throw new Error("OpenAI API returned no images");
594
- }
595
- return data.data.map((item) => ({
596
- url: item.url,
597
- revisedPrompt: item.revised_prompt
598
- }));
710
+ const modelName = getImageModel(runtime);
711
+ const count = params.count ?? 1;
712
+ const size = params.size ?? "1024x1024";
713
+ const extendedParams = params;
714
+ import_core7.logger.debug(`[OpenAI] Using IMAGE model: ${modelName}`);
715
+ if (!params.prompt || params.prompt.trim().length === 0) {
716
+ throw new Error("IMAGE generation requires a non-empty prompt");
717
+ }
718
+ if (count < 1 || count > 10) {
719
+ throw new Error("IMAGE count must be between 1 and 10");
720
+ }
721
+ const baseURL = getBaseURL(runtime);
722
+ const requestBody = {
723
+ model: modelName,
724
+ prompt: params.prompt,
725
+ n: count,
726
+ size,
727
+ };
728
+ if (extendedParams.quality) {
729
+ requestBody.quality = extendedParams.quality;
730
+ }
731
+ if (extendedParams.style) {
732
+ requestBody.style = extendedParams.style;
733
+ }
734
+ const response = await fetch(`${baseURL}/images/generations`, {
735
+ method: "POST",
736
+ headers: {
737
+ ...getAuthHeader(runtime),
738
+ "Content-Type": "application/json",
739
+ },
740
+ body: JSON.stringify(requestBody),
741
+ });
742
+ if (!response.ok) {
743
+ const errorText = await response.text().catch(() => "Unknown error");
744
+ throw new Error(
745
+ `OpenAI image generation failed: ${response.status} ${response.statusText} - ${errorText}`,
746
+ );
747
+ }
748
+ const data = await response.json();
749
+ if (!data.data || data.data.length === 0) {
750
+ throw new Error("OpenAI API returned no images");
751
+ }
752
+ return data.data.map((item) => ({
753
+ url: item.url,
754
+ revisedPrompt: item.revised_prompt,
755
+ }));
599
756
  }
600
757
  function parseTitleFromResponse(content) {
601
- const titleMatch = content.match(/title[:\s]+(.+?)(?:\n|$)/i);
602
- return titleMatch?.[1]?.trim() ?? "Image Analysis";
758
+ const titleMatch = content.match(/title[:\s]+(.+?)(?:\n|$)/i);
759
+ return titleMatch?.[1]?.trim() ?? "Image Analysis";
603
760
  }
604
761
  function parseDescriptionFromResponse(content) {
605
- return content.replace(/title[:\s]+(.+?)(?:\n|$)/i, "").trim();
762
+ return content.replace(/title[:\s]+(.+?)(?:\n|$)/i, "").trim();
606
763
  }
607
764
  async function handleImageDescription(runtime, params) {
608
- const modelName = getImageDescriptionModel(runtime);
609
- const maxTokens = getImageDescriptionMaxTokens(runtime);
610
- import_core7.logger.debug(`[OpenAI] Using IMAGE_DESCRIPTION model: ${modelName}`);
611
- let imageUrl;
612
- let promptText;
613
- if (typeof params === "string") {
614
- imageUrl = params;
615
- promptText = DEFAULT_IMAGE_DESCRIPTION_PROMPT;
616
- } else {
617
- imageUrl = params.imageUrl;
618
- promptText = params.prompt ?? DEFAULT_IMAGE_DESCRIPTION_PROMPT;
619
- }
620
- if (!imageUrl || imageUrl.trim().length === 0) {
621
- throw new Error("IMAGE_DESCRIPTION requires a valid image URL");
622
- }
623
- const baseURL = getBaseURL(runtime);
624
- const requestBody = {
625
- model: modelName,
626
- messages: [
627
- {
628
- role: "user",
629
- content: [
630
- { type: "text", text: promptText },
631
- { type: "image_url", image_url: { url: imageUrl } }
632
- ]
633
- }
634
- ],
635
- max_tokens: maxTokens
636
- };
637
- const response = await fetch(`${baseURL}/chat/completions`, {
638
- method: "POST",
639
- headers: {
640
- ...getAuthHeader(runtime),
641
- "Content-Type": "application/json"
642
- },
643
- body: JSON.stringify(requestBody)
644
- });
645
- if (!response.ok) {
646
- const errorText = await response.text().catch(() => "Unknown error");
647
- throw new Error(`OpenAI image description failed: ${response.status} ${response.statusText} - ${errorText}`);
648
- }
649
- const data = await response.json();
650
- if (data.usage) {
651
- emitModelUsageEvent(runtime, import_core7.ModelType.IMAGE_DESCRIPTION, typeof params === "string" ? params : params.prompt ?? "", {
652
- promptTokens: data.usage.prompt_tokens,
653
- completionTokens: data.usage.completion_tokens,
654
- totalTokens: data.usage.total_tokens
655
- });
656
- }
657
- const firstChoice = data.choices?.[0];
658
- const content = firstChoice?.message?.content;
659
- if (!content) {
660
- throw new Error("OpenAI API returned empty image description");
661
- }
662
- return {
663
- title: parseTitleFromResponse(content),
664
- description: parseDescriptionFromResponse(content)
665
- };
765
+ const modelName = getImageDescriptionModel(runtime);
766
+ const maxTokens = getImageDescriptionMaxTokens(runtime);
767
+ import_core7.logger.debug(
768
+ `[OpenAI] Using IMAGE_DESCRIPTION model: ${modelName}`,
769
+ );
770
+ let imageUrl;
771
+ let promptText;
772
+ if (typeof params === "string") {
773
+ imageUrl = params;
774
+ promptText = DEFAULT_IMAGE_DESCRIPTION_PROMPT;
775
+ } else {
776
+ imageUrl = params.imageUrl;
777
+ promptText = params.prompt ?? DEFAULT_IMAGE_DESCRIPTION_PROMPT;
778
+ }
779
+ if (!imageUrl || imageUrl.trim().length === 0) {
780
+ throw new Error("IMAGE_DESCRIPTION requires a valid image URL");
781
+ }
782
+ const baseURL = getBaseURL(runtime);
783
+ const requestBody = {
784
+ model: modelName,
785
+ messages: [
786
+ {
787
+ role: "user",
788
+ content: [
789
+ { type: "text", text: promptText },
790
+ { type: "image_url", image_url: { url: imageUrl } },
791
+ ],
792
+ },
793
+ ],
794
+ max_tokens: maxTokens,
795
+ };
796
+ const response = await fetch(`${baseURL}/chat/completions`, {
797
+ method: "POST",
798
+ headers: {
799
+ ...getAuthHeader(runtime),
800
+ "Content-Type": "application/json",
801
+ },
802
+ body: JSON.stringify(requestBody),
803
+ });
804
+ if (!response.ok) {
805
+ const errorText = await response.text().catch(() => "Unknown error");
806
+ throw new Error(
807
+ `OpenAI image description failed: ${response.status} ${response.statusText} - ${errorText}`,
808
+ );
809
+ }
810
+ const data = await response.json();
811
+ if (data.usage) {
812
+ emitModelUsageEvent(
813
+ runtime,
814
+ import_core7.ModelType.IMAGE_DESCRIPTION,
815
+ typeof params === "string" ? params : (params.prompt ?? ""),
816
+ {
817
+ promptTokens: data.usage.prompt_tokens,
818
+ completionTokens: data.usage.completion_tokens,
819
+ totalTokens: data.usage.total_tokens,
820
+ },
821
+ );
822
+ }
823
+ const firstChoice = data.choices?.[0];
824
+ const content = firstChoice?.message?.content;
825
+ if (!content) {
826
+ throw new Error("OpenAI API returned empty image description");
827
+ }
828
+ return {
829
+ title: parseTitleFromResponse(content),
830
+ description: parseDescriptionFromResponse(content),
831
+ };
666
832
  }
667
833
  // models/object.ts
668
834
  var import_core9 = require("@elizaos/core");
@@ -672,353 +838,492 @@ var import_ai2 = require("ai");
672
838
  var import_openai = require("@ai-sdk/openai");
673
839
  var PROXY_API_KEY = "sk-proxy";
674
840
  function createOpenAIClient(runtime) {
675
- const baseURL = getBaseURL(runtime);
676
- const apiKey = getApiKey(runtime);
677
- if (!apiKey && isProxyMode(runtime)) {
678
- return import_openai.createOpenAI({
679
- apiKey: PROXY_API_KEY,
680
- baseURL
681
- });
682
- }
683
- if (!apiKey) {
684
- throw new Error("OPENAI_API_KEY is required. Set it in your environment variables or runtime settings.");
685
- }
686
- return import_openai.createOpenAI({
687
- apiKey,
688
- baseURL
689
- });
841
+ const baseURL = getBaseURL(runtime);
842
+ const apiKey = getApiKey(runtime);
843
+ if (!apiKey && isProxyMode(runtime)) {
844
+ return import_openai.createOpenAI({
845
+ apiKey: PROXY_API_KEY,
846
+ baseURL,
847
+ });
848
+ }
849
+ if (!apiKey) {
850
+ throw new Error(
851
+ "OPENAI_API_KEY is required. Set it in your environment variables or runtime settings.",
852
+ );
853
+ }
854
+ return import_openai.createOpenAI({
855
+ apiKey,
856
+ baseURL,
857
+ });
690
858
  }
691
859
  // utils/json.ts
692
860
  var import_core8 = require("@elizaos/core");
693
861
  var import_ai = require("ai");
694
862
  var JSON_CLEANUP_PATTERNS = {
695
- MARKDOWN_JSON: /```json\n|\n```|```/g,
696
- WHITESPACE: /^\s+|\s+$/g
863
+ MARKDOWN_JSON: /```json\n|\n```|```/g,
864
+ WHITESPACE: /^\s+|\s+$/g,
697
865
  };
698
866
  function getJsonRepairFunction() {
699
- return async ({ text, error }) => {
700
- if (!(error instanceof import_ai.JSONParseError)) {
701
- return null;
702
- }
703
- try {
704
- const cleanedText = text.replace(JSON_CLEANUP_PATTERNS.MARKDOWN_JSON, "");
705
- JSON.parse(cleanedText);
706
- import_core8.logger.debug("[JSON Repair] Successfully repaired JSON by removing markdown wrappers");
707
- return cleanedText;
708
- } catch {
709
- import_core8.logger.warn("[JSON Repair] Unable to repair JSON text");
710
- return null;
711
- }
712
- };
867
+ return async ({ text, error }) => {
868
+ if (!(error instanceof import_ai.JSONParseError)) {
869
+ return null;
870
+ }
871
+ try {
872
+ const cleanedText = text.replace(JSON_CLEANUP_PATTERNS.MARKDOWN_JSON, "");
873
+ JSON.parse(cleanedText);
874
+ import_core8.logger.debug(
875
+ "[JSON Repair] Successfully repaired JSON by removing markdown wrappers",
876
+ );
877
+ return cleanedText;
878
+ } catch {
879
+ import_core8.logger.warn("[JSON Repair] Unable to repair JSON text");
880
+ return null;
881
+ }
882
+ };
713
883
  }
714
884
 
715
885
  // models/object.ts
716
- async function generateObjectByModelType(runtime, params, modelType, getModelFn) {
717
- const openai = createOpenAIClient(runtime);
718
- const modelName = getModelFn(runtime);
719
- import_core9.logger.debug(`[OpenAI] Using ${modelType} model: ${modelName}`);
720
- if (!params.prompt || params.prompt.trim().length === 0) {
721
- throw new Error("Object generation requires a non-empty prompt");
722
- }
723
- if (params.schema) {
724
- import_core9.logger.debug("[OpenAI] Schema provided but using no-schema mode. " + "Structure is determined by prompt instructions.");
725
- }
726
- const model = openai.chat(modelName);
727
- const { object, usage } = await import_ai2.generateObject({
728
- model,
729
- output: "no-schema",
730
- prompt: params.prompt,
731
- experimental_repairText: getJsonRepairFunction()
732
- });
733
- if (usage) {
734
- emitModelUsageEvent(runtime, modelType, params.prompt, usage);
735
- }
736
- if (typeof object !== "object" || object === null) {
737
- throw new Error(`Object generation returned ${typeof object}, expected object`);
738
- }
739
- return object;
886
+ async function generateObjectByModelType(
887
+ runtime,
888
+ params,
889
+ modelType,
890
+ getModelFn,
891
+ ) {
892
+ const openai = createOpenAIClient(runtime);
893
+ const modelName = getModelFn(runtime);
894
+ import_core9.logger.debug(`[OpenAI] Using ${modelType} model: ${modelName}`);
895
+ if (!params.prompt || params.prompt.trim().length === 0) {
896
+ throw new Error("Object generation requires a non-empty prompt");
897
+ }
898
+ if (params.schema) {
899
+ import_core9.logger.debug(
900
+ "[OpenAI] Schema provided but using no-schema mode. " +
901
+ "Structure is determined by prompt instructions.",
902
+ );
903
+ }
904
+ const model = openai.chat(modelName);
905
+ const { object, usage } = await import_ai2.generateObject({
906
+ model,
907
+ output: "no-schema",
908
+ prompt: params.prompt,
909
+ experimental_repairText: getJsonRepairFunction(),
910
+ });
911
+ if (usage) {
912
+ emitModelUsageEvent(runtime, modelType, params.prompt, usage);
913
+ }
914
+ if (typeof object !== "object" || object === null) {
915
+ throw new Error(
916
+ `Object generation returned ${typeof object}, expected object`,
917
+ );
918
+ }
919
+ return object;
740
920
  }
741
921
  async function handleObjectSmall(runtime, params) {
742
- return generateObjectByModelType(runtime, params, import_core9.ModelType.OBJECT_SMALL, getSmallModel);
922
+ return generateObjectByModelType(
923
+ runtime,
924
+ params,
925
+ import_core9.ModelType.OBJECT_SMALL,
926
+ getSmallModel,
927
+ );
743
928
  }
744
929
  async function handleObjectLarge(runtime, params) {
745
- return generateObjectByModelType(runtime, params, import_core9.ModelType.OBJECT_LARGE, getLargeModel);
930
+ return generateObjectByModelType(
931
+ runtime,
932
+ params,
933
+ import_core9.ModelType.OBJECT_LARGE,
934
+ getLargeModel,
935
+ );
746
936
  }
747
937
  // models/research.ts
748
938
  var import_core10 = require("@elizaos/core");
749
939
  function convertToolToApi(tool) {
750
- switch (tool.type) {
751
- case "web_search_preview":
752
- return { type: "web_search_preview" };
753
- case "file_search":
754
- return {
755
- type: "file_search",
756
- vector_store_ids: tool.vectorStoreIds
757
- };
758
- case "code_interpreter":
759
- return {
760
- type: "code_interpreter",
761
- container: tool.container ?? { type: "auto" }
762
- };
763
- case "mcp":
764
- return {
765
- type: "mcp",
766
- server_label: tool.serverLabel,
767
- server_url: tool.serverUrl,
768
- require_approval: tool.requireApproval ?? "never"
769
- };
770
- default:
771
- throw new Error(`Unknown research tool type: ${tool.type}`);
772
- }
940
+ switch (tool.type) {
941
+ case "web_search_preview":
942
+ return { type: "web_search_preview" };
943
+ case "file_search":
944
+ return {
945
+ type: "file_search",
946
+ vector_store_ids: tool.vectorStoreIds,
947
+ };
948
+ case "code_interpreter":
949
+ return {
950
+ type: "code_interpreter",
951
+ container: tool.container ?? { type: "auto" },
952
+ };
953
+ case "mcp":
954
+ return {
955
+ type: "mcp",
956
+ server_label: tool.serverLabel,
957
+ server_url: tool.serverUrl,
958
+ require_approval: tool.requireApproval ?? "never",
959
+ };
960
+ default:
961
+ throw new Error(`Unknown research tool type: ${tool.type}`);
962
+ }
773
963
  }
774
964
  function convertOutputItem(item) {
775
- switch (item.type) {
776
- case "web_search_call":
777
- return {
778
- id: item.id ?? "",
779
- type: "web_search_call",
780
- status: item.status ?? "completed",
781
- action: {
782
- type: item.action?.type ?? "search",
783
- query: item.action?.query,
784
- url: item.action?.url
785
- }
786
- };
787
- case "file_search_call":
788
- return {
789
- id: item.id ?? "",
790
- type: "file_search_call",
791
- status: item.status ?? "completed",
792
- query: item.query ?? "",
793
- results: item.results?.map((r) => ({
794
- fileId: r.file_id,
795
- fileName: r.file_name,
796
- score: r.score
797
- }))
798
- };
799
- case "code_interpreter_call":
800
- return {
801
- id: item.id ?? "",
802
- type: "code_interpreter_call",
803
- status: item.status ?? "completed",
804
- code: item.code ?? "",
805
- output: item.output
806
- };
807
- case "mcp_tool_call":
808
- return {
809
- id: item.id ?? "",
810
- type: "mcp_tool_call",
811
- status: item.status ?? "completed",
812
- serverLabel: item.server_label ?? "",
813
- toolName: item.tool_name ?? "",
814
- arguments: item.arguments ?? {},
815
- result: item.result
816
- };
817
- case "message":
818
- return {
819
- type: "message",
820
- content: item.content?.map((c) => ({
821
- type: "output_text",
822
- text: c.text,
823
- annotations: c.annotations?.map((a) => ({
824
- url: a.url,
825
- title: a.title,
826
- startIndex: a.start_index,
827
- endIndex: a.end_index
828
- })) ?? []
829
- })) ?? []
830
- };
831
- default:
832
- return null;
833
- }
965
+ switch (item.type) {
966
+ case "web_search_call":
967
+ return {
968
+ id: item.id ?? "",
969
+ type: "web_search_call",
970
+ status: item.status ?? "completed",
971
+ action: {
972
+ type: item.action?.type ?? "search",
973
+ query: item.action?.query,
974
+ url: item.action?.url,
975
+ },
976
+ };
977
+ case "file_search_call":
978
+ return {
979
+ id: item.id ?? "",
980
+ type: "file_search_call",
981
+ status: item.status ?? "completed",
982
+ query: item.query ?? "",
983
+ results: item.results?.map((r) => ({
984
+ fileId: r.file_id,
985
+ fileName: r.file_name,
986
+ score: r.score,
987
+ })),
988
+ };
989
+ case "code_interpreter_call":
990
+ return {
991
+ id: item.id ?? "",
992
+ type: "code_interpreter_call",
993
+ status: item.status ?? "completed",
994
+ code: item.code ?? "",
995
+ output: item.output,
996
+ };
997
+ case "mcp_tool_call":
998
+ return {
999
+ id: item.id ?? "",
1000
+ type: "mcp_tool_call",
1001
+ status: item.status ?? "completed",
1002
+ serverLabel: item.server_label ?? "",
1003
+ toolName: item.tool_name ?? "",
1004
+ arguments: item.arguments ?? {},
1005
+ result: item.result,
1006
+ };
1007
+ case "message":
1008
+ return {
1009
+ type: "message",
1010
+ content:
1011
+ item.content?.map((c) => ({
1012
+ type: "output_text",
1013
+ text: c.text,
1014
+ annotations:
1015
+ c.annotations?.map((a) => ({
1016
+ url: a.url,
1017
+ title: a.title,
1018
+ startIndex: a.start_index,
1019
+ endIndex: a.end_index,
1020
+ })) ?? [],
1021
+ })) ?? [],
1022
+ };
1023
+ default:
1024
+ return null;
1025
+ }
834
1026
  }
835
1027
  function extractTextAndAnnotations(response) {
836
- if (response.output_text) {
837
- const annotations2 = [];
838
- if (response.output) {
839
- for (const item of response.output) {
840
- if (item.type === "message" && item.content) {
841
- for (const content of item.content) {
842
- if (content.annotations) {
843
- for (const ann of content.annotations) {
844
- annotations2.push({
845
- url: ann.url,
846
- title: ann.title,
847
- startIndex: ann.start_index,
848
- endIndex: ann.end_index
849
- });
850
- }
851
- }
852
- }
853
- }
854
- }
855
- }
856
- return { text: response.output_text, annotations: annotations2 };
857
- }
858
- let text = "";
859
- const annotations = [];
860
- if (response.output) {
861
- for (const item of response.output) {
862
- if (item.type === "message" && item.content) {
863
- for (const content of item.content) {
864
- text += content.text;
865
- if (content.annotations) {
866
- for (const ann of content.annotations) {
867
- annotations.push({
868
- url: ann.url,
869
- title: ann.title,
870
- startIndex: ann.start_index,
871
- endIndex: ann.end_index
872
- });
873
- }
874
- }
875
- }
876
- }
877
- }
878
- }
879
- return { text, annotations };
1028
+ if (response.output_text) {
1029
+ const annotations2 = [];
1030
+ if (response.output) {
1031
+ for (const item of response.output) {
1032
+ if (item.type === "message" && item.content) {
1033
+ for (const content of item.content) {
1034
+ if (content.annotations) {
1035
+ for (const ann of content.annotations) {
1036
+ annotations2.push({
1037
+ url: ann.url,
1038
+ title: ann.title,
1039
+ startIndex: ann.start_index,
1040
+ endIndex: ann.end_index,
1041
+ });
1042
+ }
1043
+ }
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+ return { text: response.output_text, annotations: annotations2 };
1049
+ }
1050
+ let text = "";
1051
+ const annotations = [];
1052
+ if (response.output) {
1053
+ for (const item of response.output) {
1054
+ if (item.type === "message" && item.content) {
1055
+ for (const content of item.content) {
1056
+ text += content.text;
1057
+ if (content.annotations) {
1058
+ for (const ann of content.annotations) {
1059
+ annotations.push({
1060
+ url: ann.url,
1061
+ title: ann.title,
1062
+ startIndex: ann.start_index,
1063
+ endIndex: ann.end_index,
1064
+ });
1065
+ }
1066
+ }
1067
+ }
1068
+ }
1069
+ }
1070
+ }
1071
+ return { text, annotations };
880
1072
  }
881
1073
  async function handleResearch(runtime, params) {
882
- const apiKey = getApiKey(runtime);
883
- if (!apiKey) {
884
- throw new Error("OPENAI_API_KEY is required for deep research. Set it in your environment variables or runtime settings.");
885
- }
886
- const baseURL = getBaseURL(runtime);
887
- const modelName = params.model ?? getResearchModel(runtime);
888
- const timeout = getResearchTimeout(runtime);
889
- import_core10.logger.debug(`[OpenAI] Starting deep research with model: ${modelName}`);
890
- import_core10.logger.debug(`[OpenAI] Research input: ${params.input.substring(0, 100)}...`);
891
- const dataSourceTools = params.tools?.filter((t) => t.type === "web_search_preview" || t.type === "file_search" || t.type === "mcp");
892
- if (!dataSourceTools || dataSourceTools.length === 0) {
893
- import_core10.logger.debug("[OpenAI] No data source tools specified, defaulting to web_search_preview");
894
- params.tools = [{ type: "web_search_preview" }, ...params.tools ?? []];
895
- }
896
- const requestBody = {
897
- model: modelName,
898
- input: params.input
899
- };
900
- if (params.instructions) {
901
- requestBody.instructions = params.instructions;
902
- }
903
- if (params.background !== undefined) {
904
- requestBody.background = params.background;
905
- }
906
- if (params.tools && params.tools.length > 0) {
907
- requestBody.tools = params.tools.map(convertToolToApi);
908
- }
909
- if (params.maxToolCalls !== undefined) {
910
- requestBody.max_tool_calls = params.maxToolCalls;
911
- }
912
- if (params.reasoningSummary) {
913
- requestBody.reasoning = { summary: params.reasoningSummary };
914
- }
915
- import_core10.logger.debug(`[OpenAI] Research request body: ${JSON.stringify(requestBody, null, 2)}`);
916
- const response = await fetch(`${baseURL}/responses`, {
917
- method: "POST",
918
- headers: {
919
- Authorization: `Bearer ${apiKey}`,
920
- "Content-Type": "application/json"
921
- },
922
- body: JSON.stringify(requestBody),
923
- signal: AbortSignal.timeout(timeout)
924
- });
925
- if (!response.ok) {
926
- const errorText = await response.text();
927
- import_core10.logger.error(`[OpenAI] Research request failed: ${response.status} ${errorText}`);
928
- throw new Error(`Deep research request failed: ${response.status} ${response.statusText}`);
929
- }
930
- const data = await response.json();
931
- if (data.error) {
932
- import_core10.logger.error(`[OpenAI] Research API error: ${data.error.message}`);
933
- throw new Error(`Deep research error: ${data.error.message}`);
934
- }
935
- import_core10.logger.debug(`[OpenAI] Research response received. Status: ${data.status ?? "completed"}`);
936
- const { text, annotations } = extractTextAndAnnotations(data);
937
- const outputItems = [];
938
- if (data.output) {
939
- for (const item of data.output) {
940
- const converted = convertOutputItem(item);
941
- if (converted) {
942
- outputItems.push(converted);
943
- }
944
- }
945
- }
946
- const result = {
947
- id: data.id,
948
- text,
949
- annotations,
950
- outputItems,
951
- status: data.status
952
- };
953
- import_core10.logger.info(`[OpenAI] Research completed. Text length: ${text.length}, Annotations: ${annotations.length}, Output items: ${outputItems.length}`);
954
- return result;
1074
+ const apiKey = getApiKey(runtime);
1075
+ if (!apiKey) {
1076
+ throw new Error(
1077
+ "OPENAI_API_KEY is required for deep research. Set it in your environment variables or runtime settings.",
1078
+ );
1079
+ }
1080
+ const baseURL = getBaseURL(runtime);
1081
+ const modelName = params.model ?? getResearchModel(runtime);
1082
+ const timeout = getResearchTimeout(runtime);
1083
+ import_core10.logger.debug(
1084
+ `[OpenAI] Starting deep research with model: ${modelName}`,
1085
+ );
1086
+ import_core10.logger.debug(
1087
+ `[OpenAI] Research input: ${params.input.substring(0, 100)}...`,
1088
+ );
1089
+ const dataSourceTools = params.tools?.filter(
1090
+ (t) =>
1091
+ t.type === "web_search_preview" ||
1092
+ t.type === "file_search" ||
1093
+ t.type === "mcp",
1094
+ );
1095
+ if (!dataSourceTools || dataSourceTools.length === 0) {
1096
+ import_core10.logger.debug(
1097
+ "[OpenAI] No data source tools specified, defaulting to web_search_preview",
1098
+ );
1099
+ params.tools = [{ type: "web_search_preview" }, ...(params.tools ?? [])];
1100
+ }
1101
+ const requestBody = {
1102
+ model: modelName,
1103
+ input: params.input,
1104
+ };
1105
+ if (params.instructions) {
1106
+ requestBody.instructions = params.instructions;
1107
+ }
1108
+ if (params.background !== undefined) {
1109
+ requestBody.background = params.background;
1110
+ }
1111
+ if (params.tools && params.tools.length > 0) {
1112
+ requestBody.tools = params.tools.map(convertToolToApi);
1113
+ }
1114
+ if (params.maxToolCalls !== undefined) {
1115
+ requestBody.max_tool_calls = params.maxToolCalls;
1116
+ }
1117
+ if (params.reasoningSummary) {
1118
+ requestBody.reasoning = { summary: params.reasoningSummary };
1119
+ }
1120
+ import_core10.logger.debug(
1121
+ `[OpenAI] Research request body: ${JSON.stringify(requestBody, null, 2)}`,
1122
+ );
1123
+ const response = await fetch(`${baseURL}/responses`, {
1124
+ method: "POST",
1125
+ headers: {
1126
+ Authorization: `Bearer ${apiKey}`,
1127
+ "Content-Type": "application/json",
1128
+ },
1129
+ body: JSON.stringify(requestBody),
1130
+ signal: AbortSignal.timeout(timeout),
1131
+ });
1132
+ if (!response.ok) {
1133
+ const errorText = await response.text();
1134
+ import_core10.logger.error(
1135
+ `[OpenAI] Research request failed: ${response.status} ${errorText}`,
1136
+ );
1137
+ throw new Error(
1138
+ `Deep research request failed: ${response.status} ${response.statusText}`,
1139
+ );
1140
+ }
1141
+ const data = await response.json();
1142
+ if (data.error) {
1143
+ import_core10.logger.error(
1144
+ `[OpenAI] Research API error: ${data.error.message}`,
1145
+ );
1146
+ throw new Error(`Deep research error: ${data.error.message}`);
1147
+ }
1148
+ import_core10.logger.debug(
1149
+ `[OpenAI] Research response received. Status: ${data.status ?? "completed"}`,
1150
+ );
1151
+ const { text, annotations } = extractTextAndAnnotations(data);
1152
+ const outputItems = [];
1153
+ if (data.output) {
1154
+ for (const item of data.output) {
1155
+ const converted = convertOutputItem(item);
1156
+ if (converted) {
1157
+ outputItems.push(converted);
1158
+ }
1159
+ }
1160
+ }
1161
+ const result = {
1162
+ id: data.id,
1163
+ text,
1164
+ annotations,
1165
+ outputItems,
1166
+ status: data.status,
1167
+ };
1168
+ import_core10.logger.info(
1169
+ `[OpenAI] Research completed. Text length: ${text.length}, Annotations: ${annotations.length}, Output items: ${outputItems.length}`,
1170
+ );
1171
+ return result;
955
1172
  }
956
1173
  // models/text.ts
957
1174
  var import_core11 = require("@elizaos/core");
958
1175
  var import_ai3 = require("ai");
1176
+ var TEXT_NANO_MODEL_TYPE = import_core11.ModelType.TEXT_NANO ?? "TEXT_NANO";
1177
+ var TEXT_MEDIUM_MODEL_TYPE =
1178
+ import_core11.ModelType.TEXT_MEDIUM ?? "TEXT_MEDIUM";
1179
+ var TEXT_MEGA_MODEL_TYPE = import_core11.ModelType.TEXT_MEGA ?? "TEXT_MEGA";
1180
+ var RESPONSE_HANDLER_MODEL_TYPE =
1181
+ import_core11.ModelType.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
1182
+ var ACTION_PLANNER_MODEL_TYPE =
1183
+ import_core11.ModelType.ACTION_PLANNER ?? "ACTION_PLANNER";
1184
+ function buildUserContent(params) {
1185
+ const content = [{ type: "text", text: params.prompt }];
1186
+ for (const attachment of params.attachments ?? []) {
1187
+ content.push({
1188
+ type: "file",
1189
+ data: attachment.data,
1190
+ mediaType: attachment.mediaType,
1191
+ ...(attachment.filename ? { filename: attachment.filename } : {}),
1192
+ });
1193
+ }
1194
+ return content;
1195
+ }
959
1196
  function convertUsage(usage) {
960
- if (!usage) {
961
- return;
962
- }
963
- const promptTokens = usage.inputTokens ?? 0;
964
- const completionTokens = usage.outputTokens ?? 0;
965
- const usageWithCache = usage;
966
- return {
967
- promptTokens,
968
- completionTokens,
969
- totalTokens: promptTokens + completionTokens,
970
- cachedPromptTokens: usageWithCache.cachedInputTokens
971
- };
1197
+ if (!usage) {
1198
+ return;
1199
+ }
1200
+ const promptTokens = usage.inputTokens ?? 0;
1201
+ const completionTokens = usage.outputTokens ?? 0;
1202
+ const usageWithCache = usage;
1203
+ return {
1204
+ promptTokens,
1205
+ completionTokens,
1206
+ totalTokens: promptTokens + completionTokens,
1207
+ cachedPromptTokens: usageWithCache.cachedInputTokens,
1208
+ };
972
1209
  }
973
1210
  function resolvePromptCacheOptions(params) {
974
- const withOpenAIOptions = params;
975
- return {
976
- promptCacheKey: withOpenAIOptions.providerOptions?.openai?.promptCacheKey,
977
- promptCacheRetention: withOpenAIOptions.providerOptions?.openai?.promptCacheRetention
978
- };
1211
+ const withOpenAIOptions = params;
1212
+ return {
1213
+ promptCacheKey: withOpenAIOptions.providerOptions?.openai?.promptCacheKey,
1214
+ promptCacheRetention:
1215
+ withOpenAIOptions.providerOptions?.openai?.promptCacheRetention,
1216
+ };
979
1217
  }
980
1218
  async function generateTextByModelType(runtime, params, modelType, getModelFn) {
981
- const openai = createOpenAIClient(runtime);
982
- const modelName = getModelFn(runtime);
983
- import_core11.logger.debug(`[OpenAI] Using ${modelType} model: ${modelName}`);
984
- const promptCacheOptions = resolvePromptCacheOptions(params);
985
- const systemPrompt = runtime.character.system ?? undefined;
986
- const model = openai.chat(modelName);
987
- const generateParams = {
988
- model,
989
- prompt: params.prompt,
990
- system: systemPrompt,
991
- maxOutputTokens: params.maxTokens ?? 8192,
992
- experimental_telemetry: { isEnabled: getExperimentalTelemetry(runtime) },
993
- ...promptCacheOptions.promptCacheKey || promptCacheOptions.promptCacheRetention ? {
994
- providerOptions: {
995
- openai: {
996
- ...promptCacheOptions.promptCacheKey ? { promptCacheKey: promptCacheOptions.promptCacheKey } : {},
997
- ...promptCacheOptions.promptCacheRetention ? { promptCacheRetention: promptCacheOptions.promptCacheRetention } : {}
998
- }
999
- }
1000
- } : {}
1001
- };
1002
- if (params.stream) {
1003
- const result = import_ai3.streamText(generateParams);
1004
- return {
1005
- textStream: result.textStream,
1006
- text: Promise.resolve(result.text),
1007
- usage: Promise.resolve(result.usage).then(convertUsage),
1008
- finishReason: Promise.resolve(result.finishReason).then((r) => r)
1009
- };
1010
- }
1011
- const { text, usage } = await import_ai3.generateText(generateParams);
1012
- if (usage) {
1013
- emitModelUsageEvent(runtime, modelType, params.prompt, usage);
1014
- }
1015
- return text;
1219
+ const paramsWithAttachments = params;
1220
+ const openai = createOpenAIClient(runtime);
1221
+ const modelName = getModelFn(runtime);
1222
+ import_core11.logger.debug(`[OpenAI] Using ${modelType} model: ${modelName}`);
1223
+ const promptCacheOptions = resolvePromptCacheOptions(params);
1224
+ const hasAttachments = (paramsWithAttachments.attachments?.length ?? 0) > 0;
1225
+ const userContent = hasAttachments
1226
+ ? buildUserContent(paramsWithAttachments)
1227
+ : undefined;
1228
+ const systemPrompt = runtime.character.system ?? undefined;
1229
+ const model = openai.chat(modelName);
1230
+ const generateParams = {
1231
+ model,
1232
+ ...(userContent
1233
+ ? { messages: [{ role: "user", content: userContent }] }
1234
+ : { prompt: params.prompt }),
1235
+ system: systemPrompt,
1236
+ maxOutputTokens: params.maxTokens ?? 8192,
1237
+ experimental_telemetry: { isEnabled: getExperimentalTelemetry(runtime) },
1238
+ ...(promptCacheOptions.promptCacheKey ||
1239
+ promptCacheOptions.promptCacheRetention
1240
+ ? {
1241
+ providerOptions: {
1242
+ openai: {
1243
+ ...(promptCacheOptions.promptCacheKey
1244
+ ? { promptCacheKey: promptCacheOptions.promptCacheKey }
1245
+ : {}),
1246
+ ...(promptCacheOptions.promptCacheRetention
1247
+ ? {
1248
+ promptCacheRetention:
1249
+ promptCacheOptions.promptCacheRetention,
1250
+ }
1251
+ : {}),
1252
+ },
1253
+ },
1254
+ }
1255
+ : {}),
1256
+ };
1257
+ if (params.stream) {
1258
+ const result = import_ai3.streamText(generateParams);
1259
+ return {
1260
+ textStream: result.textStream,
1261
+ text: Promise.resolve(result.text),
1262
+ usage: Promise.resolve(result.usage).then(convertUsage),
1263
+ finishReason: Promise.resolve(result.finishReason).then((r) => r),
1264
+ };
1265
+ }
1266
+ const { text, usage } = await import_ai3.generateText(generateParams);
1267
+ if (usage) {
1268
+ emitModelUsageEvent(runtime, modelType, params.prompt, usage);
1269
+ }
1270
+ return text;
1016
1271
  }
1017
1272
  async function handleTextSmall(runtime, params) {
1018
- return generateTextByModelType(runtime, params, import_core11.ModelType.TEXT_SMALL, getSmallModel);
1273
+ return generateTextByModelType(
1274
+ runtime,
1275
+ params,
1276
+ import_core11.ModelType.TEXT_SMALL,
1277
+ getSmallModel,
1278
+ );
1279
+ }
1280
+ async function handleTextNano(runtime, params) {
1281
+ return generateTextByModelType(
1282
+ runtime,
1283
+ params,
1284
+ TEXT_NANO_MODEL_TYPE,
1285
+ getNanoModel,
1286
+ );
1287
+ }
1288
+ async function handleTextMedium(runtime, params) {
1289
+ return generateTextByModelType(
1290
+ runtime,
1291
+ params,
1292
+ TEXT_MEDIUM_MODEL_TYPE,
1293
+ getMediumModel,
1294
+ );
1019
1295
  }
1020
1296
  async function handleTextLarge(runtime, params) {
1021
- return generateTextByModelType(runtime, params, import_core11.ModelType.TEXT_LARGE, getLargeModel);
1297
+ return generateTextByModelType(
1298
+ runtime,
1299
+ params,
1300
+ import_core11.ModelType.TEXT_LARGE,
1301
+ getLargeModel,
1302
+ );
1303
+ }
1304
+ async function handleTextMega(runtime, params) {
1305
+ return generateTextByModelType(
1306
+ runtime,
1307
+ params,
1308
+ TEXT_MEGA_MODEL_TYPE,
1309
+ getMegaModel,
1310
+ );
1311
+ }
1312
+ async function handleResponseHandler(runtime, params) {
1313
+ return generateTextByModelType(
1314
+ runtime,
1315
+ params,
1316
+ RESPONSE_HANDLER_MODEL_TYPE,
1317
+ getResponseHandlerModel,
1318
+ );
1319
+ }
1320
+ async function handleActionPlanner(runtime, params) {
1321
+ return generateTextByModelType(
1322
+ runtime,
1323
+ params,
1324
+ ACTION_PLANNER_MODEL_TYPE,
1325
+ getActionPlannerModel,
1326
+ );
1022
1327
  }
1023
1328
  // models/tokenizer.ts
1024
1329
  var import_core13 = require("@elizaos/core");
@@ -1027,289 +1332,417 @@ var import_core13 = require("@elizaos/core");
1027
1332
  var import_core12 = require("@elizaos/core");
1028
1333
  var import_js_tiktoken = require("js-tiktoken");
1029
1334
  function resolveTokenizerEncoding(modelName) {
1030
- const normalized = modelName.toLowerCase();
1031
- const fallbackEncoding = normalized.includes("4o") ? "o200k_base" : "cl100k_base";
1032
- try {
1033
- return import_js_tiktoken.encodingForModel(modelName);
1034
- } catch {
1035
- return import_js_tiktoken.getEncoding(fallbackEncoding);
1036
- }
1335
+ const normalized = modelName.toLowerCase();
1336
+ const fallbackEncoding = normalized.includes("4o")
1337
+ ? "o200k_base"
1338
+ : "cl100k_base";
1339
+ try {
1340
+ return import_js_tiktoken.encodingForModel(modelName);
1341
+ } catch {
1342
+ return import_js_tiktoken.getEncoding(fallbackEncoding);
1343
+ }
1037
1344
  }
1038
1345
  function getModelName(runtime, modelType) {
1039
- if (modelType === import_core12.ModelType.TEXT_SMALL) {
1040
- return getSmallModel(runtime);
1041
- }
1042
- return getLargeModel(runtime);
1346
+ if (modelType === import_core12.ModelType.TEXT_SMALL) {
1347
+ return getSmallModel(runtime);
1348
+ }
1349
+ return getLargeModel(runtime);
1043
1350
  }
1044
1351
  function tokenizeText(runtime, modelType, text) {
1045
- const modelName = getModelName(runtime, modelType);
1046
- const encoder = resolveTokenizerEncoding(modelName);
1047
- return encoder.encode(text);
1352
+ const modelName = getModelName(runtime, modelType);
1353
+ const encoder = resolveTokenizerEncoding(modelName);
1354
+ return encoder.encode(text);
1048
1355
  }
1049
1356
  function detokenizeText(runtime, modelType, tokens) {
1050
- const modelName = getModelName(runtime, modelType);
1051
- const encoder = resolveTokenizerEncoding(modelName);
1052
- return encoder.decode(tokens);
1357
+ const modelName = getModelName(runtime, modelType);
1358
+ const encoder = resolveTokenizerEncoding(modelName);
1359
+ return encoder.decode(tokens);
1053
1360
  }
1054
1361
 
1055
1362
  // models/tokenizer.ts
1056
1363
  async function handleTokenizerEncode(runtime, params) {
1057
- if (!params.prompt) {
1058
- throw new Error("Tokenization requires a non-empty prompt");
1059
- }
1060
- const modelType = params.modelType ?? import_core13.ModelType.TEXT_LARGE;
1061
- return tokenizeText(runtime, modelType, params.prompt);
1364
+ if (!params.prompt) {
1365
+ throw new Error("Tokenization requires a non-empty prompt");
1366
+ }
1367
+ const modelType = params.modelType ?? import_core13.ModelType.TEXT_LARGE;
1368
+ return tokenizeText(runtime, modelType, params.prompt);
1062
1369
  }
1063
1370
  async function handleTokenizerDecode(runtime, params) {
1064
- if (!params.tokens || !Array.isArray(params.tokens)) {
1065
- throw new Error("Detokenization requires a valid tokens array");
1066
- }
1067
- if (params.tokens.length === 0) {
1068
- return "";
1069
- }
1070
- for (let i = 0;i < params.tokens.length; i++) {
1071
- const token = params.tokens[i];
1072
- if (typeof token !== "number" || !Number.isFinite(token)) {
1073
- throw new Error(`Invalid token at index ${i}: expected number`);
1074
- }
1075
- }
1076
- const modelType = params.modelType ?? import_core13.ModelType.TEXT_LARGE;
1077
- return detokenizeText(runtime, modelType, params.tokens);
1371
+ if (!params.tokens || !Array.isArray(params.tokens)) {
1372
+ throw new Error("Detokenization requires a valid tokens array");
1373
+ }
1374
+ if (params.tokens.length === 0) {
1375
+ return "";
1376
+ }
1377
+ for (let i = 0; i < params.tokens.length; i++) {
1378
+ const token = params.tokens[i];
1379
+ if (typeof token !== "number" || !Number.isFinite(token)) {
1380
+ throw new Error(`Invalid token at index ${i}: expected number`);
1381
+ }
1382
+ }
1383
+ const modelType = params.modelType ?? import_core13.ModelType.TEXT_LARGE;
1384
+ return detokenizeText(runtime, modelType, params.tokens);
1078
1385
  }
1079
1386
  // index.ts
1080
1387
  function getProcessEnv() {
1081
- if (typeof process === "undefined") {
1082
- return {};
1083
- }
1084
- return process.env;
1388
+ if (typeof process === "undefined") {
1389
+ return {};
1390
+ }
1391
+ return process.env;
1085
1392
  }
1086
1393
  var env = getProcessEnv();
1394
+ var TEXT_NANO_MODEL_TYPE2 = import_core14.ModelType.TEXT_NANO ?? "TEXT_NANO";
1395
+ var TEXT_MEDIUM_MODEL_TYPE2 =
1396
+ import_core14.ModelType.TEXT_MEDIUM ?? "TEXT_MEDIUM";
1397
+ var TEXT_MEGA_MODEL_TYPE2 = import_core14.ModelType.TEXT_MEGA ?? "TEXT_MEGA";
1398
+ var RESPONSE_HANDLER_MODEL_TYPE2 =
1399
+ import_core14.ModelType.RESPONSE_HANDLER ?? "RESPONSE_HANDLER";
1400
+ var ACTION_PLANNER_MODEL_TYPE2 =
1401
+ import_core14.ModelType.ACTION_PLANNER ?? "ACTION_PLANNER";
1087
1402
  var openaiPlugin = {
1088
- name: "openai",
1089
- description: "OpenAI API integration for text, image, audio, and embedding models",
1090
- config: {
1091
- OPENAI_API_KEY: env.OPENAI_API_KEY ?? null,
1092
- OPENAI_BASE_URL: env.OPENAI_BASE_URL ?? null,
1093
- OPENAI_SMALL_MODEL: env.OPENAI_SMALL_MODEL ?? null,
1094
- OPENAI_LARGE_MODEL: env.OPENAI_LARGE_MODEL ?? null,
1095
- SMALL_MODEL: env.SMALL_MODEL ?? null,
1096
- LARGE_MODEL: env.LARGE_MODEL ?? null,
1097
- OPENAI_EMBEDDING_MODEL: env.OPENAI_EMBEDDING_MODEL ?? null,
1098
- OPENAI_EMBEDDING_API_KEY: env.OPENAI_EMBEDDING_API_KEY ?? null,
1099
- OPENAI_EMBEDDING_URL: env.OPENAI_EMBEDDING_URL ?? null,
1100
- OPENAI_EMBEDDING_DIMENSIONS: env.OPENAI_EMBEDDING_DIMENSIONS ?? null,
1101
- OPENAI_IMAGE_DESCRIPTION_MODEL: env.OPENAI_IMAGE_DESCRIPTION_MODEL ?? null,
1102
- OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS: env.OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS ?? null,
1103
- OPENAI_EXPERIMENTAL_TELEMETRY: env.OPENAI_EXPERIMENTAL_TELEMETRY ?? null,
1104
- OPENAI_RESEARCH_MODEL: env.OPENAI_RESEARCH_MODEL ?? null,
1105
- OPENAI_RESEARCH_TIMEOUT: env.OPENAI_RESEARCH_TIMEOUT ?? null
1106
- },
1107
- async init(config, runtime) {
1108
- initializeOpenAI(config, runtime);
1109
- },
1110
- models: {
1111
- [import_core14.ModelType.TEXT_EMBEDDING]: async (runtime, params) => {
1112
- return handleTextEmbedding(runtime, params);
1113
- },
1114
- [import_core14.ModelType.TEXT_TOKENIZER_ENCODE]: async (runtime, params) => {
1115
- return handleTokenizerEncode(runtime, params);
1116
- },
1117
- [import_core14.ModelType.TEXT_TOKENIZER_DECODE]: async (runtime, params) => {
1118
- return handleTokenizerDecode(runtime, params);
1119
- },
1120
- [import_core14.ModelType.TEXT_SMALL]: async (runtime, params) => {
1121
- return handleTextSmall(runtime, params);
1122
- },
1123
- [import_core14.ModelType.TEXT_LARGE]: async (runtime, params) => {
1124
- return handleTextLarge(runtime, params);
1125
- },
1126
- [import_core14.ModelType.IMAGE]: async (runtime, params) => {
1127
- return handleImageGeneration(runtime, params);
1128
- },
1129
- [import_core14.ModelType.IMAGE_DESCRIPTION]: async (runtime, params) => {
1130
- return handleImageDescription(runtime, params);
1131
- },
1132
- [import_core14.ModelType.TRANSCRIPTION]: async (runtime, input) => {
1133
- return handleTranscription(runtime, input);
1134
- },
1135
- [import_core14.ModelType.TEXT_TO_SPEECH]: async (runtime, input) => {
1136
- return handleTextToSpeech(runtime, input);
1137
- },
1138
- [import_core14.ModelType.OBJECT_SMALL]: async (runtime, params) => {
1139
- return handleObjectSmall(runtime, params);
1140
- },
1141
- [import_core14.ModelType.OBJECT_LARGE]: async (runtime, params) => {
1142
- return handleObjectLarge(runtime, params);
1143
- },
1144
- [import_core14.ModelType.RESEARCH]: async (runtime, params) => {
1145
- return handleResearch(runtime, params);
1146
- }
1147
- },
1148
- tests: [
1149
- {
1150
- name: "openai_plugin_tests",
1151
- tests: [
1152
- {
1153
- name: "openai_test_api_connectivity",
1154
- fn: async (runtime) => {
1155
- const baseURL = getBaseURL(runtime);
1156
- const response = await fetch(`${baseURL}/models`, {
1157
- headers: getAuthHeader(runtime)
1158
- });
1159
- if (!response.ok) {
1160
- throw new Error(`API connectivity test failed: ${response.status} ${response.statusText}`);
1161
- }
1162
- const data = await response.json();
1163
- import_core14.logger.info(`[OpenAI Test] API connected. ${data.data?.length ?? 0} models available.`);
1164
- }
1165
- },
1166
- {
1167
- name: "openai_test_text_embedding",
1168
- fn: async (runtime) => {
1169
- const embedding = await runtime.useModel(import_core14.ModelType.TEXT_EMBEDDING, {
1170
- text: "Hello, world!"
1171
- });
1172
- if (!Array.isArray(embedding) || embedding.length === 0) {
1173
- throw new Error("Embedding should return a non-empty array");
1174
- }
1175
- import_core14.logger.info(`[OpenAI Test] Generated embedding with ${embedding.length} dimensions`);
1176
- }
1177
- },
1178
- {
1179
- name: "openai_test_text_small",
1180
- fn: async (runtime) => {
1181
- const text = await runtime.useModel(import_core14.ModelType.TEXT_SMALL, {
1182
- prompt: "Say hello in exactly 5 words."
1183
- });
1184
- if (typeof text !== "string" || text.length === 0) {
1185
- throw new Error("TEXT_SMALL should return non-empty string");
1186
- }
1187
- import_core14.logger.info(`[OpenAI Test] TEXT_SMALL generated: "${text.substring(0, 50)}..."`);
1188
- }
1189
- },
1190
- {
1191
- name: "openai_test_text_large",
1192
- fn: async (runtime) => {
1193
- const text = await runtime.useModel(import_core14.ModelType.TEXT_LARGE, {
1194
- prompt: "Explain quantum computing in 2 sentences."
1195
- });
1196
- if (typeof text !== "string" || text.length === 0) {
1197
- throw new Error("TEXT_LARGE should return non-empty string");
1198
- }
1199
- import_core14.logger.info(`[OpenAI Test] TEXT_LARGE generated: "${text.substring(0, 50)}..."`);
1200
- }
1201
- },
1202
- {
1203
- name: "openai_test_tokenizer_roundtrip",
1204
- fn: async (runtime) => {
1205
- const originalText = "Hello, tokenizer test!";
1206
- const tokens = await runtime.useModel(import_core14.ModelType.TEXT_TOKENIZER_ENCODE, {
1207
- prompt: originalText,
1208
- modelType: import_core14.ModelType.TEXT_SMALL
1209
- });
1210
- if (!Array.isArray(tokens) || tokens.length === 0) {
1211
- throw new Error("Tokenization should return non-empty token array");
1212
- }
1213
- const decodedText = await runtime.useModel(import_core14.ModelType.TEXT_TOKENIZER_DECODE, {
1214
- tokens,
1215
- modelType: import_core14.ModelType.TEXT_SMALL
1216
- });
1217
- if (decodedText !== originalText) {
1218
- throw new Error(`Tokenizer roundtrip failed: expected "${originalText}", got "${decodedText}"`);
1219
- }
1220
- import_core14.logger.info(`[OpenAI Test] Tokenizer roundtrip successful (${tokens.length} tokens)`);
1221
- }
1222
- },
1223
- {
1224
- name: "openai_test_streaming",
1225
- fn: async (runtime) => {
1226
- const chunks = [];
1227
- const result = await runtime.useModel(import_core14.ModelType.TEXT_LARGE, {
1228
- prompt: "Count from 1 to 5, one number per line.",
1229
- stream: true,
1230
- onStreamChunk: (chunk) => {
1231
- chunks.push(chunk);
1232
- }
1233
- });
1234
- if (typeof result !== "string" || result.length === 0) {
1235
- throw new Error("Streaming should return non-empty result");
1236
- }
1237
- if (chunks.length === 0) {
1238
- throw new Error("No streaming chunks received");
1239
- }
1240
- import_core14.logger.info(`[OpenAI Test] Streaming test: ${chunks.length} chunks received`);
1241
- }
1242
- },
1243
- {
1244
- name: "openai_test_image_description",
1245
- fn: async (runtime) => {
1246
- const testImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/440px-Camponotus_flavomarginatus_ant.jpg";
1247
- const result = await runtime.useModel(import_core14.ModelType.IMAGE_DESCRIPTION, testImageUrl);
1248
- if (!result || typeof result !== "object" || !("title" in result) || !("description" in result)) {
1249
- throw new Error("Image description should return { title, description }");
1250
- }
1251
- import_core14.logger.info(`[OpenAI Test] Image described: "${result.title}"`);
1252
- }
1253
- },
1254
- {
1255
- name: "openai_test_transcription",
1256
- fn: async (runtime) => {
1257
- const audioUrl = "https://upload.wikimedia.org/wikipedia/commons/2/25/En-Open_Source.ogg";
1258
- const response = await fetch(audioUrl);
1259
- const arrayBuffer = await response.arrayBuffer();
1260
- const audioBuffer = Buffer.from(new Uint8Array(arrayBuffer));
1261
- const transcription = await runtime.useModel(import_core14.ModelType.TRANSCRIPTION, audioBuffer);
1262
- if (typeof transcription !== "string") {
1263
- throw new Error("Transcription should return a string");
1264
- }
1265
- import_core14.logger.info(`[OpenAI Test] Transcription: "${transcription.substring(0, 50)}..."`);
1266
- }
1267
- },
1268
- {
1269
- name: "openai_test_text_to_speech",
1270
- fn: async (runtime) => {
1271
- const audioData = await runtime.useModel(import_core14.ModelType.TEXT_TO_SPEECH, {
1272
- text: "Hello, this is a text-to-speech test."
1273
- });
1274
- if (!(audioData instanceof ArrayBuffer) || audioData.byteLength === 0) {
1275
- throw new Error("TTS should return non-empty ArrayBuffer");
1276
- }
1277
- import_core14.logger.info(`[OpenAI Test] TTS generated ${audioData.byteLength} bytes of audio`);
1278
- }
1279
- },
1280
- {
1281
- name: "openai_test_object_generation",
1282
- fn: async (runtime) => {
1283
- const result = await runtime.useModel(import_core14.ModelType.OBJECT_SMALL, {
1284
- prompt: "Return a JSON object with exactly these fields: name (string), age (number), active (boolean)"
1285
- });
1286
- if (!result || typeof result !== "object") {
1287
- throw new Error("Object generation should return an object");
1288
- }
1289
- import_core14.logger.info(`[OpenAI Test] Object generated: ${JSON.stringify(result).substring(0, 100)}`);
1290
- }
1291
- },
1292
- {
1293
- name: "openai_test_research",
1294
- fn: async (runtime) => {
1295
- const result = await runtime.useModel(import_core14.ModelType.RESEARCH, {
1296
- input: "What is the current date and time?",
1297
- tools: [{ type: "web_search_preview" }],
1298
- maxToolCalls: 3
1299
- });
1300
- if (!result || typeof result !== "object" || !("text" in result)) {
1301
- throw new Error("Research should return an object with text property");
1302
- }
1303
- if (typeof result.text !== "string" || result.text.length === 0) {
1304
- throw new Error("Research result text should be a non-empty string");
1305
- }
1306
- import_core14.logger.info(`[OpenAI Test] Research completed. Text length: ${result.text.length}, Annotations: ${result.annotations?.length ?? 0}`);
1307
- }
1308
- }
1309
- ]
1310
- }
1311
- ]
1403
+ name: "openai",
1404
+ description:
1405
+ "OpenAI API integration for text, image, audio, and embedding models",
1406
+ config: {
1407
+ OPENAI_API_KEY: env.OPENAI_API_KEY ?? null,
1408
+ OPENAI_BASE_URL: env.OPENAI_BASE_URL ?? null,
1409
+ OPENAI_NANO_MODEL: env.OPENAI_NANO_MODEL ?? null,
1410
+ OPENAI_MEDIUM_MODEL: env.OPENAI_MEDIUM_MODEL ?? null,
1411
+ OPENAI_SMALL_MODEL: env.OPENAI_SMALL_MODEL ?? null,
1412
+ OPENAI_LARGE_MODEL: env.OPENAI_LARGE_MODEL ?? null,
1413
+ OPENAI_MEGA_MODEL: env.OPENAI_MEGA_MODEL ?? null,
1414
+ OPENAI_RESPONSE_HANDLER_MODEL: env.OPENAI_RESPONSE_HANDLER_MODEL ?? null,
1415
+ OPENAI_SHOULD_RESPOND_MODEL: env.OPENAI_SHOULD_RESPOND_MODEL ?? null,
1416
+ OPENAI_ACTION_PLANNER_MODEL: env.OPENAI_ACTION_PLANNER_MODEL ?? null,
1417
+ OPENAI_PLANNER_MODEL: env.OPENAI_PLANNER_MODEL ?? null,
1418
+ NANO_MODEL: env.NANO_MODEL ?? null,
1419
+ MEDIUM_MODEL: env.MEDIUM_MODEL ?? null,
1420
+ SMALL_MODEL: env.SMALL_MODEL ?? null,
1421
+ LARGE_MODEL: env.LARGE_MODEL ?? null,
1422
+ MEGA_MODEL: env.MEGA_MODEL ?? null,
1423
+ RESPONSE_HANDLER_MODEL: env.RESPONSE_HANDLER_MODEL ?? null,
1424
+ SHOULD_RESPOND_MODEL: env.SHOULD_RESPOND_MODEL ?? null,
1425
+ ACTION_PLANNER_MODEL: env.ACTION_PLANNER_MODEL ?? null,
1426
+ PLANNER_MODEL: env.PLANNER_MODEL ?? null,
1427
+ OPENAI_EMBEDDING_MODEL: env.OPENAI_EMBEDDING_MODEL ?? null,
1428
+ OPENAI_EMBEDDING_API_KEY: env.OPENAI_EMBEDDING_API_KEY ?? null,
1429
+ OPENAI_EMBEDDING_URL: env.OPENAI_EMBEDDING_URL ?? null,
1430
+ OPENAI_EMBEDDING_DIMENSIONS: env.OPENAI_EMBEDDING_DIMENSIONS ?? null,
1431
+ OPENAI_IMAGE_DESCRIPTION_MODEL: env.OPENAI_IMAGE_DESCRIPTION_MODEL ?? null,
1432
+ OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS:
1433
+ env.OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS ?? null,
1434
+ OPENAI_EXPERIMENTAL_TELEMETRY: env.OPENAI_EXPERIMENTAL_TELEMETRY ?? null,
1435
+ OPENAI_RESEARCH_MODEL: env.OPENAI_RESEARCH_MODEL ?? null,
1436
+ OPENAI_RESEARCH_TIMEOUT: env.OPENAI_RESEARCH_TIMEOUT ?? null,
1437
+ },
1438
+ async init(config, runtime) {
1439
+ initializeOpenAI(config, runtime);
1440
+ },
1441
+ models: {
1442
+ [import_core14.ModelType.TEXT_EMBEDDING]: async (runtime, params) => {
1443
+ return handleTextEmbedding(runtime, params);
1444
+ },
1445
+ [import_core14.ModelType.TEXT_TOKENIZER_ENCODE]: async (
1446
+ runtime,
1447
+ params,
1448
+ ) => {
1449
+ return handleTokenizerEncode(runtime, params);
1450
+ },
1451
+ [import_core14.ModelType.TEXT_TOKENIZER_DECODE]: async (
1452
+ runtime,
1453
+ params,
1454
+ ) => {
1455
+ return handleTokenizerDecode(runtime, params);
1456
+ },
1457
+ [import_core14.ModelType.TEXT_SMALL]: async (runtime, params) => {
1458
+ return handleTextSmall(runtime, params);
1459
+ },
1460
+ [TEXT_NANO_MODEL_TYPE2]: async (runtime, params) => {
1461
+ return handleTextNano(runtime, params);
1462
+ },
1463
+ [TEXT_MEDIUM_MODEL_TYPE2]: async (runtime, params) => {
1464
+ return handleTextMedium(runtime, params);
1465
+ },
1466
+ [import_core14.ModelType.TEXT_LARGE]: async (runtime, params) => {
1467
+ return handleTextLarge(runtime, params);
1468
+ },
1469
+ [TEXT_MEGA_MODEL_TYPE2]: async (runtime, params) => {
1470
+ return handleTextMega(runtime, params);
1471
+ },
1472
+ [RESPONSE_HANDLER_MODEL_TYPE2]: async (runtime, params) => {
1473
+ return handleResponseHandler(runtime, params);
1474
+ },
1475
+ [ACTION_PLANNER_MODEL_TYPE2]: async (runtime, params) => {
1476
+ return handleActionPlanner(runtime, params);
1477
+ },
1478
+ [import_core14.ModelType.IMAGE]: async (runtime, params) => {
1479
+ return handleImageGeneration(runtime, params);
1480
+ },
1481
+ [import_core14.ModelType.IMAGE_DESCRIPTION]: async (runtime, params) => {
1482
+ return handleImageDescription(runtime, params);
1483
+ },
1484
+ [import_core14.ModelType.TRANSCRIPTION]: async (runtime, input) => {
1485
+ return handleTranscription(runtime, input);
1486
+ },
1487
+ [import_core14.ModelType.TEXT_TO_SPEECH]: async (runtime, input) => {
1488
+ return handleTextToSpeech(runtime, input);
1489
+ },
1490
+ [import_core14.ModelType.OBJECT_SMALL]: async (runtime, params) => {
1491
+ return handleObjectSmall(runtime, params);
1492
+ },
1493
+ [import_core14.ModelType.OBJECT_LARGE]: async (runtime, params) => {
1494
+ return handleObjectLarge(runtime, params);
1495
+ },
1496
+ [import_core14.ModelType.RESEARCH]: async (runtime, params) => {
1497
+ return handleResearch(runtime, params);
1498
+ },
1499
+ },
1500
+ tests: [
1501
+ {
1502
+ name: "openai_plugin_tests",
1503
+ tests: [
1504
+ {
1505
+ name: "openai_test_api_connectivity",
1506
+ fn: async (runtime) => {
1507
+ const baseURL = getBaseURL(runtime);
1508
+ const response = await fetch(`${baseURL}/models`, {
1509
+ headers: getAuthHeader(runtime),
1510
+ });
1511
+ if (!response.ok) {
1512
+ throw new Error(
1513
+ `API connectivity test failed: ${response.status} ${response.statusText}`,
1514
+ );
1515
+ }
1516
+ const data = await response.json();
1517
+ import_core14.logger.info(
1518
+ `[OpenAI Test] API connected. ${data.data?.length ?? 0} models available.`,
1519
+ );
1520
+ },
1521
+ },
1522
+ {
1523
+ name: "openai_test_text_embedding",
1524
+ fn: async (runtime) => {
1525
+ const embedding = await runtime.useModel(
1526
+ import_core14.ModelType.TEXT_EMBEDDING,
1527
+ {
1528
+ text: "Hello, world!",
1529
+ },
1530
+ );
1531
+ if (!Array.isArray(embedding) || embedding.length === 0) {
1532
+ throw new Error("Embedding should return a non-empty array");
1533
+ }
1534
+ import_core14.logger.info(
1535
+ `[OpenAI Test] Generated embedding with ${embedding.length} dimensions`,
1536
+ );
1537
+ },
1538
+ },
1539
+ {
1540
+ name: "openai_test_text_small",
1541
+ fn: async (runtime) => {
1542
+ const text = await runtime.useModel(
1543
+ import_core14.ModelType.TEXT_SMALL,
1544
+ {
1545
+ prompt: "Say hello in exactly 5 words.",
1546
+ },
1547
+ );
1548
+ if (typeof text !== "string" || text.length === 0) {
1549
+ throw new Error("TEXT_SMALL should return non-empty string");
1550
+ }
1551
+ import_core14.logger.info(
1552
+ `[OpenAI Test] TEXT_SMALL generated: "${text.substring(0, 50)}..."`,
1553
+ );
1554
+ },
1555
+ },
1556
+ {
1557
+ name: "openai_test_text_large",
1558
+ fn: async (runtime) => {
1559
+ const text = await runtime.useModel(
1560
+ import_core14.ModelType.TEXT_LARGE,
1561
+ {
1562
+ prompt: "Explain quantum computing in 2 sentences.",
1563
+ },
1564
+ );
1565
+ if (typeof text !== "string" || text.length === 0) {
1566
+ throw new Error("TEXT_LARGE should return non-empty string");
1567
+ }
1568
+ import_core14.logger.info(
1569
+ `[OpenAI Test] TEXT_LARGE generated: "${text.substring(0, 50)}..."`,
1570
+ );
1571
+ },
1572
+ },
1573
+ {
1574
+ name: "openai_test_tokenizer_roundtrip",
1575
+ fn: async (runtime) => {
1576
+ const originalText = "Hello, tokenizer test!";
1577
+ const tokens = await runtime.useModel(
1578
+ import_core14.ModelType.TEXT_TOKENIZER_ENCODE,
1579
+ {
1580
+ prompt: originalText,
1581
+ modelType: import_core14.ModelType.TEXT_SMALL,
1582
+ },
1583
+ );
1584
+ if (!Array.isArray(tokens) || tokens.length === 0) {
1585
+ throw new Error(
1586
+ "Tokenization should return non-empty token array",
1587
+ );
1588
+ }
1589
+ const decodedText = await runtime.useModel(
1590
+ import_core14.ModelType.TEXT_TOKENIZER_DECODE,
1591
+ {
1592
+ tokens,
1593
+ modelType: import_core14.ModelType.TEXT_SMALL,
1594
+ },
1595
+ );
1596
+ if (decodedText !== originalText) {
1597
+ throw new Error(
1598
+ `Tokenizer roundtrip failed: expected "${originalText}", got "${decodedText}"`,
1599
+ );
1600
+ }
1601
+ import_core14.logger.info(
1602
+ `[OpenAI Test] Tokenizer roundtrip successful (${tokens.length} tokens)`,
1603
+ );
1604
+ },
1605
+ },
1606
+ {
1607
+ name: "openai_test_streaming",
1608
+ fn: async (runtime) => {
1609
+ const chunks = [];
1610
+ const result = await runtime.useModel(
1611
+ import_core14.ModelType.TEXT_LARGE,
1612
+ {
1613
+ prompt: "Count from 1 to 5, one number per line.",
1614
+ stream: true,
1615
+ onStreamChunk: (chunk) => {
1616
+ chunks.push(chunk);
1617
+ },
1618
+ },
1619
+ );
1620
+ if (typeof result !== "string" || result.length === 0) {
1621
+ throw new Error("Streaming should return non-empty result");
1622
+ }
1623
+ if (chunks.length === 0) {
1624
+ throw new Error("No streaming chunks received");
1625
+ }
1626
+ import_core14.logger.info(
1627
+ `[OpenAI Test] Streaming test: ${chunks.length} chunks received`,
1628
+ );
1629
+ },
1630
+ },
1631
+ {
1632
+ name: "openai_test_image_description",
1633
+ fn: async (runtime) => {
1634
+ const testImageUrl =
1635
+ "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/440px-Camponotus_flavomarginatus_ant.jpg";
1636
+ const result = await runtime.useModel(
1637
+ import_core14.ModelType.IMAGE_DESCRIPTION,
1638
+ testImageUrl,
1639
+ );
1640
+ if (
1641
+ !result ||
1642
+ typeof result !== "object" ||
1643
+ !("title" in result) ||
1644
+ !("description" in result)
1645
+ ) {
1646
+ throw new Error(
1647
+ "Image description should return { title, description }",
1648
+ );
1649
+ }
1650
+ import_core14.logger.info(
1651
+ `[OpenAI Test] Image described: "${result.title}"`,
1652
+ );
1653
+ },
1654
+ },
1655
+ {
1656
+ name: "openai_test_transcription",
1657
+ fn: async (runtime) => {
1658
+ const audioUrl =
1659
+ "https://upload.wikimedia.org/wikipedia/commons/2/25/En-Open_Source.ogg";
1660
+ const response = await fetch(audioUrl);
1661
+ const arrayBuffer = await response.arrayBuffer();
1662
+ const audioBuffer = Buffer.from(new Uint8Array(arrayBuffer));
1663
+ const transcription = await runtime.useModel(
1664
+ import_core14.ModelType.TRANSCRIPTION,
1665
+ audioBuffer,
1666
+ );
1667
+ if (typeof transcription !== "string") {
1668
+ throw new Error("Transcription should return a string");
1669
+ }
1670
+ import_core14.logger.info(
1671
+ `[OpenAI Test] Transcription: "${transcription.substring(0, 50)}..."`,
1672
+ );
1673
+ },
1674
+ },
1675
+ {
1676
+ name: "openai_test_text_to_speech",
1677
+ fn: async (runtime) => {
1678
+ const audioData = await runtime.useModel(
1679
+ import_core14.ModelType.TEXT_TO_SPEECH,
1680
+ {
1681
+ text: "Hello, this is a text-to-speech test.",
1682
+ },
1683
+ );
1684
+ if (
1685
+ !(audioData instanceof ArrayBuffer) ||
1686
+ audioData.byteLength === 0
1687
+ ) {
1688
+ throw new Error("TTS should return non-empty ArrayBuffer");
1689
+ }
1690
+ import_core14.logger.info(
1691
+ `[OpenAI Test] TTS generated ${audioData.byteLength} bytes of audio`,
1692
+ );
1693
+ },
1694
+ },
1695
+ {
1696
+ name: "openai_test_object_generation",
1697
+ fn: async (runtime) => {
1698
+ const result = await runtime.useModel(
1699
+ import_core14.ModelType.OBJECT_SMALL,
1700
+ {
1701
+ prompt:
1702
+ "Return a JSON object with exactly these fields: name (string), age (number), active (boolean)",
1703
+ },
1704
+ );
1705
+ if (!result || typeof result !== "object") {
1706
+ throw new Error("Object generation should return an object");
1707
+ }
1708
+ import_core14.logger.info(
1709
+ `[OpenAI Test] Object generated: ${JSON.stringify(result).substring(0, 100)}`,
1710
+ );
1711
+ },
1712
+ },
1713
+ {
1714
+ name: "openai_test_research",
1715
+ fn: async (runtime) => {
1716
+ const result = await runtime.useModel(
1717
+ import_core14.ModelType.RESEARCH,
1718
+ {
1719
+ input: "What is the current date and time?",
1720
+ tools: [{ type: "web_search_preview" }],
1721
+ maxToolCalls: 3,
1722
+ },
1723
+ );
1724
+ if (!result || typeof result !== "object" || !("text" in result)) {
1725
+ throw new Error(
1726
+ "Research should return an object with text property",
1727
+ );
1728
+ }
1729
+ if (typeof result.text !== "string" || result.text.length === 0) {
1730
+ throw new Error(
1731
+ "Research result text should be a non-empty string",
1732
+ );
1733
+ }
1734
+ import_core14.logger.info(
1735
+ `[OpenAI Test] Research completed. Text length: ${result.text.length}, Annotations: ${result.annotations?.length ?? 0}`,
1736
+ );
1737
+ },
1738
+ },
1739
+ ],
1740
+ },
1741
+ ],
1312
1742
  };
1313
1743
  var typescript_default = openaiPlugin;
1314
1744
 
1315
- //# debugId=23E20BA2231FE80864756E2164756E21
1745
+ // index.node.ts
1746
+ var index_node_default = typescript_default;
1747
+
1748
+ //# debugId=236821D6733C981C64756E2164756E21