@elizaos/plugin-openai 2.0.0-beta.1 → 2.0.11-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -117
- package/auto-enable.ts +2 -2
- package/package.json +45 -7
- package/dist/browser/index.browser.js +0 -3
- package/dist/browser/index.browser.js.map +0 -23
- package/dist/browser/index.d.ts +0 -2
- package/dist/build.d.ts +0 -13
- package/dist/build.d.ts.map +0 -1
- package/dist/cjs/index.d.ts +0 -2
- package/dist/cjs/index.node.cjs +0 -1748
- package/dist/cjs/index.node.js.map +0 -25
- package/dist/generated/specs/specs.d.ts +0 -55
- package/dist/generated/specs/specs.d.ts.map +0 -1
- package/dist/index.browser.d.ts +0 -4
- package/dist/index.browser.d.ts.map +0 -1
- package/dist/index.d.ts +0 -4
- package/dist/index.d.ts.map +0 -1
- package/dist/index.node.d.ts +0 -4
- package/dist/index.node.d.ts.map +0 -1
- package/dist/init.d.ts +0 -4
- package/dist/init.d.ts.map +0 -1
- package/dist/models/audio.d.ts +0 -9
- package/dist/models/audio.d.ts.map +0 -1
- package/dist/models/embedding.d.ts +0 -3
- package/dist/models/embedding.d.ts.map +0 -1
- package/dist/models/image.d.ts +0 -5
- package/dist/models/image.d.ts.map +0 -1
- package/dist/models/index.d.ts +0 -7
- package/dist/models/index.d.ts.map +0 -1
- package/dist/models/object.d.ts +0 -4
- package/dist/models/object.d.ts.map +0 -1
- package/dist/models/research.d.ts +0 -34
- package/dist/models/research.d.ts.map +0 -1
- package/dist/models/text.d.ts +0 -33
- package/dist/models/text.d.ts.map +0 -1
- package/dist/models/tokenizer.d.ts +0 -4
- package/dist/models/tokenizer.d.ts.map +0 -1
- package/dist/node/index.d.ts +0 -2
- package/dist/node/index.node.js +0 -1881
- package/dist/node/index.node.js.map +0 -23
- package/dist/providers/index.d.ts +0 -2
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/openai.d.ts +0 -4
- package/dist/providers/openai.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -363
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/audio.d.ts +0 -6
- package/dist/utils/audio.d.ts.map +0 -1
- package/dist/utils/config.d.ts +0 -38
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/events.d.ts +0 -21
- package/dist/utils/events.d.ts.map +0 -1
- package/dist/utils/index.d.ts +0 -6
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/json.d.ts +0 -10
- package/dist/utils/json.d.ts.map +0 -1
- package/dist/utils/tokenization.d.ts +0 -6
- package/dist/utils/tokenization.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,163 +1,221 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @elizaos/plugin-openai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
OpenAI model-provider plugin for [elizaOS](https://github.com/elizaos/eliza). Adds text generation, embeddings, image generation, image description, audio transcription, text-to-speech, and deep research to any Eliza agent by registering handlers for the standard `ModelType.*` slots on the `AgentRuntime`.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What this plugin does
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- **Text generation** — multiple tiers (nano, small, medium, large, mega), plus dedicated response-handler and action-planner slots. Supports streaming and structured JSON output.
|
|
8
|
+
- **Text embeddings** — `text-embedding-3-small` by default; dimension configurable.
|
|
9
|
+
- **Image generation** — DALL-E 3 by default (`dall-e-3`).
|
|
10
|
+
- **Image description** — vision model analyzes an image URL and returns `{ title, description }`.
|
|
11
|
+
- **Audio transcription** — speech-to-text (`gpt-5-mini-transcribe` by default); accepts `Buffer`, `Blob`, `File`, or a URL.
|
|
12
|
+
- **Text-to-speech** — returns an `ArrayBuffer` of audio. Six voices; mp3/wav/flac/opus/aac/pcm output.
|
|
13
|
+
- **Deep research** — `ModelType.RESEARCH` via the OpenAI Responses API (`o3-deep-research` by default); returns annotated, multi-source research reports.
|
|
14
|
+
- **Tokenizer** — encode/decode using js-tiktoken (browser-safe, no network calls).
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
"plugins": ["@elizaos/plugin-openai"]
|
|
11
|
-
```
|
|
16
|
+
Works with any OpenAI-compatible endpoint: OpenAI, Cerebras, EvoLink, OpenRouter, local servers, etc.
|
|
12
17
|
|
|
13
|
-
##
|
|
18
|
+
## Enabling the plugin
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
Add `@elizaos/plugin-openai` to your character's plugin list:
|
|
16
21
|
|
|
17
22
|
```json
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
"OPENAI_BASE_URL": "optional_custom_endpoint",
|
|
21
|
-
"OPENAI_SMALL_MODEL": "gpt-5-mini",
|
|
22
|
-
"OPENAI_LARGE_MODEL": "gpt-5",
|
|
23
|
-
"OPENAI_EMBEDDING_MODEL": "text-embedding-3-small",
|
|
24
|
-
"OPENAI_EMBEDDING_API_KEY": "your_openai_api_key_for_embedding",
|
|
25
|
-
"OPENAI_EMBEDDING_URL": "optional_custom_endpoint",
|
|
26
|
-
"OPENAI_EMBEDDING_DIMENSIONS": "1536",
|
|
27
|
-
"OPENAI_IMAGE_DESCRIPTION_MODEL": "gpt-5-mini",
|
|
28
|
-
"OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS": "8192",
|
|
29
|
-
"OPENAI_EXPERIMENTAL_TELEMETRY": "false",
|
|
30
|
-
"OPENAI_BROWSER_BASE_URL": "https://your-proxy.example.com/openai",
|
|
31
|
-
"OPENAI_BROWSER_EMBEDDING_URL": "https://your-proxy.example.com/openai"
|
|
23
|
+
{
|
|
24
|
+
"plugins": ["@elizaos/plugin-openai"]
|
|
32
25
|
}
|
|
33
26
|
```
|
|
34
27
|
|
|
35
|
-
|
|
28
|
+
The plugin auto-enables when `OPENAI_API_KEY`, `CEREBRAS_API_KEY`, or `EVOLINK_API_KEY` is present in the environment.
|
|
29
|
+
|
|
30
|
+
## Required configuration
|
|
36
31
|
|
|
37
32
|
```
|
|
38
|
-
OPENAI_API_KEY=
|
|
39
|
-
# Optional overrides:
|
|
40
|
-
OPENAI_BASE_URL=optional_custom_endpoint
|
|
41
|
-
OPENAI_SMALL_MODEL=gpt-5-mini
|
|
42
|
-
OPENAI_LARGE_MODEL=gpt-5
|
|
43
|
-
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
|
44
|
-
OPENAI_EMBEDDING_API_KEY=your_openai_api_key_for_embedding
|
|
45
|
-
OPENAI_EMBEDDING_URL=optional_custom_endpoint
|
|
46
|
-
OPENAI_EMBEDDING_DIMENSIONS=1536
|
|
47
|
-
OPENAI_IMAGE_DESCRIPTION_MODEL=gpt-5-mini
|
|
48
|
-
OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS=8192
|
|
49
|
-
OPENAI_EXPERIMENTAL_TELEMETRY=false
|
|
50
|
-
# Browser proxy (frontend builds only)
|
|
51
|
-
OPENAI_BROWSER_BASE_URL=https://your-proxy.example.com/openai
|
|
52
|
-
OPENAI_BROWSER_EMBEDDING_URL=https://your-proxy.example.com/openai
|
|
33
|
+
OPENAI_API_KEY=sk-...
|
|
53
34
|
```
|
|
54
35
|
|
|
55
|
-
|
|
36
|
+
That is the only required setting. All other settings are optional overrides.
|
|
56
37
|
|
|
57
|
-
|
|
58
|
-
- `OPENAI_BASE_URL`: Custom API endpoint (default: https://api.openai.com/v1)
|
|
59
|
-
- `OPENAI_SMALL_MODEL`: Defaults to GPT-4o Mini ("gpt-5-mini")
|
|
60
|
-
- `OPENAI_LARGE_MODEL`: Defaults to GPT-4o ("gpt-5")
|
|
61
|
-
- `OPENAI_EMBEDDING_MODEL`: Defaults to text-embedding-3-small ("text-embedding-3-small")
|
|
62
|
-
- `OPENAI_EMBEDDING_API_KEY`: Custom embedding api key (defaults to `OPENAI_API_KEY`)
|
|
63
|
-
- `OPENAI_EMBEDDING_URL`: Custom embedding endpoint (defaults to `OPENAI_BASE_URL`)
|
|
64
|
-
- `OPENAI_EMBEDDING_DIMENSIONS`: Defaults to 1536 (1536)
|
|
65
|
-
- `OPENAI_IMAGE_DESCRIPTION_MODEL`: Model used for image description (default: "gpt-5-mini")
|
|
66
|
-
- `OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS`: Maximum tokens for image descriptions (default: 8192)
|
|
67
|
-
- `OPENAI_EXPERIMENTAL_TELEMETRY`: Enable experimental telemetry features for enhanced debugging and usage analytics (default: false)
|
|
68
|
-
- `OPENAI_BROWSER_BASE_URL`: Browser-only base URL to a proxy endpoint that forwards requests to OpenAI without exposing keys
|
|
69
|
-
- `OPENAI_BROWSER_EMBEDDING_URL`: Browser-only embeddings endpoint base URL
|
|
38
|
+
## Full configuration reference
|
|
70
39
|
|
|
71
|
-
|
|
40
|
+
Set these as environment variables or in your character's `settings` object.
|
|
72
41
|
|
|
73
|
-
|
|
42
|
+
### API endpoint
|
|
74
43
|
|
|
75
|
-
|
|
44
|
+
| Variable | Default | Description |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `OPENAI_BASE_URL` | `https://api.openai.com/v1` | Override for compatible endpoints |
|
|
47
|
+
| `ELIZA_PROVIDER` | — | Set to `cerebras` or `evolink` to force a compatible-provider mode |
|
|
48
|
+
| `EVOLINK_API_KEY` | — | Auth for EvoLink's OpenAI-compatible endpoint |
|
|
49
|
+
| `EVOLINK_BASE_URL` | `https://direct.evolink.ai/v1` | EvoLink endpoint used when `EVOLINK_API_KEY` enables the plugin |
|
|
50
|
+
| `EVOLINK_MODEL` | `gpt-5.2` | Text model used by EvoLink mode |
|
|
76
51
|
|
|
77
|
-
|
|
78
|
-
import express from "express";
|
|
79
|
-
import fetch from "node-fetch";
|
|
52
|
+
### Text generation models
|
|
80
53
|
|
|
81
|
-
|
|
82
|
-
|
|
54
|
+
| Variable | Default | Description |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `OPENAI_SMALL_MODEL` / `SMALL_MODEL` | `gpt-5.4-mini` | Fast, cost-effective responses |
|
|
57
|
+
| `OPENAI_NANO_MODEL` / `NANO_MODEL` | falls back to small | Fastest tier |
|
|
58
|
+
| `OPENAI_MEDIUM_MODEL` / `MEDIUM_MODEL` | falls back to small | Mid-tier |
|
|
59
|
+
| `OPENAI_LARGE_MODEL` / `LARGE_MODEL` | `gpt-5` | Complex reasoning |
|
|
60
|
+
| `OPENAI_MEGA_MODEL` / `MEGA_MODEL` | falls back to large | Highest tier |
|
|
61
|
+
| `OPENAI_RESPONSE_HANDLER_MODEL` | falls back to small | Response-handler slot |
|
|
62
|
+
| `OPENAI_ACTION_PLANNER_MODEL` | falls back to medium | Action-planner slot |
|
|
63
|
+
| `OPENAI_REASONING_EFFORT` | — | `minimal`/`low`/`medium`/`high` for o-series models |
|
|
83
64
|
|
|
84
|
-
|
|
85
|
-
const url = `https://api.openai.com/v1/${req.params[0]}`;
|
|
86
|
-
const r = await fetch(url, {
|
|
87
|
-
method: "POST",
|
|
88
|
-
headers: {
|
|
89
|
-
Authorization: `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
90
|
-
"Content-Type": "application/json",
|
|
91
|
-
},
|
|
92
|
-
body: JSON.stringify(req.body),
|
|
93
|
-
});
|
|
94
|
-
res
|
|
95
|
-
.status(r.status)
|
|
96
|
-
.set(Object.fromEntries(r.headers))
|
|
97
|
-
.send(await r.text());
|
|
98
|
-
});
|
|
65
|
+
### Embeddings
|
|
99
66
|
|
|
100
|
-
|
|
101
|
-
|
|
67
|
+
| Variable | Default | Description |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| `OPENAI_EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model |
|
|
70
|
+
| `OPENAI_EMBEDDING_URL` | `OPENAI_BASE_URL` | Separate endpoint for embeddings |
|
|
71
|
+
| `OPENAI_EMBEDDING_API_KEY` | `OPENAI_API_KEY` | Separate key for embeddings |
|
|
72
|
+
| `OPENAI_EMBEDDING_DIMENSIONS` | `1536` | Vector dimensions (must match model) |
|
|
102
73
|
|
|
103
|
-
###
|
|
74
|
+
### Image generation and description
|
|
104
75
|
|
|
105
|
-
|
|
76
|
+
| Variable | Default | Description |
|
|
77
|
+
|---|---|---|
|
|
78
|
+
| `OPENAI_IMAGE_MODEL` | `dall-e-3` | Image generation model |
|
|
79
|
+
| `OPENAI_IMAGE_DESCRIPTION_MODEL` | `gpt-5-mini` | Vision/description model |
|
|
80
|
+
| `OPENAI_IMAGE_DESCRIPTION_BASE_URL` | `OPENAI_BASE_URL` | Separate endpoint for vision |
|
|
81
|
+
| `OPENAI_IMAGE_DESCRIPTION_API_KEY` | `OPENAI_API_KEY` | Separate key for vision |
|
|
82
|
+
| `OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS` | `8192` | Max tokens in description response |
|
|
106
83
|
|
|
107
|
-
|
|
108
|
-
- Detailed usage analytics for optimization
|
|
109
|
-
- Better observability into OpenAI API interactions
|
|
110
|
-
- Foundation for future monitoring and analytics features through Sentry or other frameworks
|
|
84
|
+
### Audio
|
|
111
85
|
|
|
112
|
-
|
|
86
|
+
| Variable | Default | Description |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `OPENAI_TRANSCRIPTION_MODEL` | `gpt-5-mini-transcribe` | Speech-to-text model |
|
|
89
|
+
| `OPENAI_TTS_MODEL` | `tts-1` | Text-to-speech model |
|
|
90
|
+
| `OPENAI_TTS_VOICE` | `nova` | Voice: alloy, echo, fable, onyx, nova, shimmer |
|
|
91
|
+
| `OPENAI_TTS_INSTRUCTIONS` | — | Style instructions for TTS |
|
|
113
92
|
|
|
114
|
-
|
|
93
|
+
### Deep research
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- `IMAGE_DESCRIPTION`: GPT-4o image analysis
|
|
121
|
-
- `TRANSCRIPTION`: Whisper audio transcription
|
|
122
|
-
- `TEXT_TOKENIZER_ENCODE`: Text tokenization
|
|
123
|
-
- `TEXT_TOKENIZER_DECODE`: Token decoding
|
|
95
|
+
| Variable | Default | Description |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `OPENAI_RESEARCH_MODEL` | `o3-deep-research` | Research model (o3 or o4-mini variants) |
|
|
98
|
+
| `OPENAI_RESEARCH_TIMEOUT` | `3600000` (1 hr) | Request timeout in milliseconds |
|
|
124
99
|
|
|
125
|
-
|
|
100
|
+
### Browser and proxy
|
|
126
101
|
|
|
127
|
-
|
|
102
|
+
| Variable | Default | Description |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `OPENAI_BROWSER_BASE_URL` | — | Proxy URL for browser builds (keeps key server-side) |
|
|
105
|
+
| `OPENAI_BROWSER_EMBEDDING_URL` | — | Proxy URL for browser embedding requests |
|
|
106
|
+
| `OPENAI_ALLOW_BROWSER_API_KEY` | `false` | Send auth header in browser builds (opt-in) |
|
|
128
107
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
108
|
+
### Other
|
|
109
|
+
|
|
110
|
+
| Variable | Default | Description |
|
|
111
|
+
|---|---|---|
|
|
112
|
+
| `OPENAI_EXPERIMENTAL_TELEMETRY` | `false` | Enable AI SDK experimental telemetry |
|
|
113
|
+
|
|
114
|
+
## Usage examples
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { ModelType } from "@elizaos/core";
|
|
118
|
+
|
|
119
|
+
// Text generation
|
|
120
|
+
const reply = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
121
|
+
prompt: "Explain quantum entanglement in plain English.",
|
|
134
122
|
});
|
|
135
|
-
```
|
|
136
123
|
|
|
137
|
-
|
|
124
|
+
// Streaming text
|
|
125
|
+
const result = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
126
|
+
prompt: "Count from 1 to 10.",
|
|
127
|
+
stream: true,
|
|
128
|
+
onStreamChunk: (chunk) => process.stdout.write(chunk),
|
|
129
|
+
});
|
|
138
130
|
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
131
|
+
// Structured JSON output
|
|
132
|
+
const data = await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
133
|
+
prompt: "Return a JSON object with name and age fields.",
|
|
134
|
+
responseSchema: {
|
|
135
|
+
type: "object",
|
|
136
|
+
properties: { name: { type: "string" }, age: { type: "number" } },
|
|
137
|
+
required: ["name", "age"],
|
|
138
|
+
},
|
|
139
|
+
});
|
|
145
140
|
|
|
146
|
-
|
|
141
|
+
// Embedding
|
|
142
|
+
const vector = await runtime.useModel(ModelType.TEXT_EMBEDDING, {
|
|
143
|
+
text: "text to embed",
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Image generation
|
|
147
|
+
const images = await runtime.useModel(ModelType.IMAGE, {
|
|
148
|
+
prompt: "A sunset over mountains",
|
|
149
|
+
count: 1,
|
|
150
|
+
size: "1024x1024",
|
|
151
|
+
});
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
// Image description
|
|
149
154
|
const { title, description } = await runtime.useModel(
|
|
150
155
|
ModelType.IMAGE_DESCRIPTION,
|
|
151
|
-
"https://example.com/
|
|
156
|
+
"https://example.com/photo.jpg"
|
|
152
157
|
);
|
|
158
|
+
|
|
159
|
+
// Audio transcription (Buffer, Blob, File, or URL string all accepted)
|
|
160
|
+
const transcript = await runtime.useModel(ModelType.TRANSCRIPTION, audioBuffer);
|
|
161
|
+
|
|
162
|
+
// Text-to-speech
|
|
163
|
+
const audio = await runtime.useModel(ModelType.TEXT_TO_SPEECH, {
|
|
164
|
+
text: "Hello, world.",
|
|
165
|
+
voice: "nova",
|
|
166
|
+
format: "mp3",
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Deep research (may take minutes)
|
|
170
|
+
const report = await runtime.useModel(ModelType.RESEARCH, {
|
|
171
|
+
input: "What are the latest advances in fusion energy?",
|
|
172
|
+
tools: [{ type: "web_search_preview" }],
|
|
173
|
+
});
|
|
174
|
+
console.log(report.text, report.annotations);
|
|
153
175
|
```
|
|
154
176
|
|
|
155
|
-
|
|
177
|
+
## Browser proxy setup
|
|
178
|
+
|
|
179
|
+
In browser builds this plugin does not send `Authorization` headers by default, to avoid exposing API keys in frontend bundles. Point `OPENAI_BROWSER_BASE_URL` at a server-side proxy that injects the key:
|
|
156
180
|
|
|
157
|
-
```
|
|
158
|
-
|
|
181
|
+
```ts
|
|
182
|
+
// Minimal Express proxy
|
|
183
|
+
import express from "express";
|
|
184
|
+
const app = express();
|
|
185
|
+
app.use(express.json());
|
|
186
|
+
|
|
187
|
+
app.all("/openai/*", async (req, res) => {
|
|
188
|
+
const url = `https://api.openai.com/v1/${req.params[0]}`;
|
|
189
|
+
const r = await fetch(url, {
|
|
190
|
+
method: req.method,
|
|
191
|
+
headers: { Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, "Content-Type": "application/json" },
|
|
192
|
+
body: req.method !== "GET" ? JSON.stringify(req.body) : undefined,
|
|
193
|
+
});
|
|
194
|
+
res.status(r.status).send(await r.text());
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
app.listen(3000);
|
|
159
198
|
```
|
|
160
199
|
|
|
161
|
-
|
|
200
|
+
Then set `OPENAI_BROWSER_BASE_URL=http://localhost:3000/openai`.
|
|
201
|
+
|
|
202
|
+
## Cerebras compatibility
|
|
203
|
+
|
|
204
|
+
Point `OPENAI_BASE_URL` at a Cerebras endpoint or set `ELIZA_PROVIDER=cerebras` and the plugin automatically adapts: structured output uses `json_object` mode, `reasoning_effort` defaults to `"low"` for reasoning-capable models (to prevent empty responses), and `CEREBRAS_API_KEY` is accepted as an alias for `OPENAI_API_KEY`. Embeddings fall back to a deterministic local hash when no explicit embedding URL is set, since Cerebras does not provide an embeddings endpoint.
|
|
205
|
+
|
|
206
|
+
## EvoLink compatibility
|
|
162
207
|
|
|
163
|
-
|
|
208
|
+
Set `EVOLINK_API_KEY` to use EvoLink through its OpenAI-compatible endpoint. The plugin defaults to `https://direct.evolink.ai/v1` and `gpt-5.2`; set `EVOLINK_BASE_URL` or `EVOLINK_MODEL` to override either value.
|
|
209
|
+
|
|
210
|
+
## Prompt caching
|
|
211
|
+
|
|
212
|
+
Pass `providerOptions.openai.promptCacheKey` and `promptCacheRetention` on any `GenerateTextParams` call to enable OpenAI prompt caching:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
await runtime.useModel(ModelType.TEXT_LARGE, {
|
|
216
|
+
prompt: "...",
|
|
217
|
+
providerOptions: {
|
|
218
|
+
openai: { promptCacheKey: "my-key", promptCacheRetention: "24h" },
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
```
|
package/auto-enable.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
// auto-enable engine loads dozens of these per boot.
|
|
7
7
|
import type { PluginAutoEnableContext } from "@elizaos/core";
|
|
8
8
|
|
|
9
|
-
const ENV_KEYS = ["OPENAI_API_KEY"] as const;
|
|
9
|
+
const ENV_KEYS = ["OPENAI_API_KEY", "CEREBRAS_API_KEY", "EVOLINK_API_KEY"] as const;
|
|
10
10
|
|
|
11
|
-
/** Enable when an OpenAI API key is present in the environment. */
|
|
11
|
+
/** Enable when an OpenAI-compatible API key is present in the environment. */
|
|
12
12
|
export function shouldEnable(ctx: PluginAutoEnableContext): boolean {
|
|
13
13
|
return ENV_KEYS.some((k) => {
|
|
14
14
|
const v = ctx.env[k];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-openai",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.11-beta.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/node/index.node.js",
|
|
6
6
|
"module": "dist/node/index.node.js",
|
|
@@ -26,6 +26,12 @@
|
|
|
26
26
|
"default": "./dist/node/index.node.js"
|
|
27
27
|
},
|
|
28
28
|
"default": "./dist/node/index.node.js"
|
|
29
|
+
},
|
|
30
|
+
"./*.css": "./dist/*.css",
|
|
31
|
+
"./*": {
|
|
32
|
+
"types": "./dist/*.d.ts",
|
|
33
|
+
"import": "./dist/*.js",
|
|
34
|
+
"default": "./dist/*.js"
|
|
29
35
|
}
|
|
30
36
|
},
|
|
31
37
|
"files": [
|
|
@@ -53,7 +59,7 @@
|
|
|
53
59
|
},
|
|
54
60
|
"devDependencies": {
|
|
55
61
|
"@biomejs/biome": "^2.4.14",
|
|
56
|
-
"@elizaos/core": "2.0.
|
|
62
|
+
"@elizaos/core": "2.0.11-beta.7",
|
|
57
63
|
"@types/bun": "^1.3.8",
|
|
58
64
|
"@types/json-schema": "^7.0.15",
|
|
59
65
|
"@types/node": "^25.0.3",
|
|
@@ -62,7 +68,7 @@
|
|
|
62
68
|
"vitest": "^4.0.0"
|
|
63
69
|
},
|
|
64
70
|
"peerDependencies": {
|
|
65
|
-
"@elizaos/core": "2.0.
|
|
71
|
+
"@elizaos/core": "2.0.11-beta.7",
|
|
66
72
|
"zod": "^4.4.3"
|
|
67
73
|
},
|
|
68
74
|
"scripts": {
|
|
@@ -73,9 +79,9 @@
|
|
|
73
79
|
"clean": "rm -rf dist .turbo",
|
|
74
80
|
"format": "bunx @biomejs/biome format --write .",
|
|
75
81
|
"format:check": "bunx @biomejs/biome format .",
|
|
76
|
-
"typecheck": "
|
|
77
|
-
"test": "vitest run --config ./vitest.config.ts
|
|
78
|
-
"test:ts": "vitest run --config ./vitest.config.ts
|
|
82
|
+
"typecheck": "tsgo --noEmit",
|
|
83
|
+
"test": "vitest run --config ./vitest.config.ts",
|
|
84
|
+
"test:ts": "vitest run --config ./vitest.config.ts",
|
|
79
85
|
"build": "bun run build.ts"
|
|
80
86
|
},
|
|
81
87
|
"publishConfig": {
|
|
@@ -97,6 +103,26 @@
|
|
|
97
103
|
"default": "https://api.openai.com/v1",
|
|
98
104
|
"sensitive": false
|
|
99
105
|
},
|
|
106
|
+
"EVOLINK_API_KEY": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"description": "API key used to authenticate requests to EvoLink's OpenAI-compatible API.",
|
|
109
|
+
"required": false,
|
|
110
|
+
"sensitive": true
|
|
111
|
+
},
|
|
112
|
+
"EVOLINK_BASE_URL": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"description": "Base URL for EvoLink's OpenAI-compatible API.",
|
|
115
|
+
"required": false,
|
|
116
|
+
"default": "https://direct.evolink.ai/v1",
|
|
117
|
+
"sensitive": false
|
|
118
|
+
},
|
|
119
|
+
"EVOLINK_MODEL": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"description": "Identifier of the EvoLink chat model to use when EVOLINK_API_KEY enables this plugin.",
|
|
122
|
+
"required": false,
|
|
123
|
+
"default": "gpt-5.2",
|
|
124
|
+
"sensitive": false
|
|
125
|
+
},
|
|
100
126
|
"OPENAI_SMALL_MODEL": {
|
|
101
127
|
"type": "string",
|
|
102
128
|
"description": "Identifier of the small language model to be used (overrides SMALL_MODEL).",
|
|
@@ -149,6 +175,18 @@
|
|
|
149
175
|
"default": 1536,
|
|
150
176
|
"sensitive": false
|
|
151
177
|
},
|
|
178
|
+
"OPENAI_IMAGE_DESCRIPTION_API_KEY": {
|
|
179
|
+
"type": "string",
|
|
180
|
+
"description": "Optional API key for image-description requests. Falls back to the general OpenAI API key when unset.",
|
|
181
|
+
"required": false,
|
|
182
|
+
"sensitive": true
|
|
183
|
+
},
|
|
184
|
+
"OPENAI_IMAGE_DESCRIPTION_BASE_URL": {
|
|
185
|
+
"type": "string",
|
|
186
|
+
"description": "Optional OpenAI-compatible base URL for image-description requests. Useful when text models use a non-vision-compatible endpoint.",
|
|
187
|
+
"required": false,
|
|
188
|
+
"sensitive": false
|
|
189
|
+
},
|
|
152
190
|
"OPENAI_IMAGE_DESCRIPTION_MODEL": {
|
|
153
191
|
"type": "string",
|
|
154
192
|
"description": "Identifier of the model used for describing images.",
|
|
@@ -204,7 +242,7 @@
|
|
|
204
242
|
}
|
|
205
243
|
}
|
|
206
244
|
},
|
|
207
|
-
"gitHead": "
|
|
245
|
+
"gitHead": "cdbc876f793d96073d7eb0d09715a031ce0cd32e",
|
|
208
246
|
"eliza": {
|
|
209
247
|
"platforms": [
|
|
210
248
|
"browser",
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{logger as W,ModelType as V}from"@elizaos/core";import{logger as u}from"@elizaos/core";import{logger as v}from"@elizaos/core";function J0(k){if(typeof process>"u"||!process.env)return;let I=process.env[k];return I===void 0?void 0:String(I)}function H(k,I,$){let K=k.getSetting(I);if(K!==void 0&&K!==null)return String(K);return J0(I)??$}function m(k,I,$){let K=H(k,I);if(K===void 0)return $;let X=Number.parseInt(K,10);if(!Number.isFinite(X))throw Error(`Setting '${I}' must be a valid integer, got: ${K}`);return X}function _k(k,I,$){let K=H(k,I);if(K===void 0)return $;let X=K.toLowerCase();return X==="true"||X==="1"||X==="yes"}function w(){return typeof globalThis<"u"&&typeof globalThis.document<"u"}function zk(k){return w()&&!!H(k,"OPENAI_BROWSER_BASE_URL")}function P(k){let I=H(k,"MILADY_PROVIDER");if(I&&I.toLowerCase()==="cerebras")return!0;let $=H(k,"OPENAI_BASE_URL");if($&&/(^|\.)cerebras\.ai(\/|$)/i.test($))return!0;return!1}function L(k){if(P(k)){let I=H(k,"CEREBRAS_API_KEY");if(I)return I}return H(k,"OPENAI_API_KEY")}function j0(k){let I=H(k,"OPENAI_EMBEDDING_API_KEY");if(I)return v.debug("[OpenAI] Using specific embedding API key"),I;return v.debug("[OpenAI] Falling back to general API key for embeddings"),L(k)}function F(k,I=!1){if(w()&&!_k(k,"OPENAI_ALLOW_BROWSER_API_KEY",!1))return{};let $=I?j0(k):L(k);return $?{Authorization:`Bearer ${$}`}:{}}function R(k){let I=H(k,"OPENAI_BROWSER_BASE_URL"),$=w()&&I?I:H(k,"OPENAI_BASE_URL")??"https://api.openai.com/v1";return v.debug(`[OpenAI] Base URL: ${$}`),$}function Rk(k){let I=w()?H(k,"OPENAI_BROWSER_EMBEDDING_URL")??H(k,"OPENAI_BROWSER_BASE_URL"):H(k,"OPENAI_EMBEDDING_URL");if(I)return v.debug(`[OpenAI] Using embedding base URL: ${I}`),I;return v.debug("[OpenAI] Falling back to general base URL for embeddings"),R(k)}function T(k){return H(k,"OPENAI_SMALL_MODEL")??H(k,"SMALL_MODEL")??"gpt-5.4-mini"}function Fk(k){return H(k,"OPENAI_NANO_MODEL")??H(k,"NANO_MODEL")??T(k)}function s(k){return H(k,"OPENAI_MEDIUM_MODEL")??H(k,"MEDIUM_MODEL")??T(k)}function g(k){return H(k,"OPENAI_LARGE_MODEL")??H(k,"LARGE_MODEL")??"gpt-5"}function Wk(k){return H(k,"OPENAI_MEGA_MODEL")??H(k,"MEGA_MODEL")??g(k)}function Dk(k){return H(k,"OPENAI_RESPONSE_HANDLER_MODEL")??H(k,"OPENAI_SHOULD_RESPOND_MODEL")??H(k,"RESPONSE_HANDLER_MODEL")??H(k,"SHOULD_RESPOND_MODEL")??T(k)}function Ek(k){return H(k,"OPENAI_ACTION_PLANNER_MODEL")??H(k,"OPENAI_PLANNER_MODEL")??H(k,"ACTION_PLANNER_MODEL")??H(k,"PLANNER_MODEL")??s(k)}function wk(k){return H(k,"OPENAI_EMBEDDING_MODEL")??"text-embedding-3-small"}function Lk(k){return H(k,"OPENAI_IMAGE_DESCRIPTION_MODEL")??"gpt-5-mini"}function Nk(k){return H(k,"OPENAI_TRANSCRIPTION_MODEL")??"gpt-5-mini-transcribe"}function Ok(k){return H(k,"OPENAI_TTS_MODEL")??"tts-1"}function Pk(k){return H(k,"OPENAI_TTS_VOICE")??"nova"}function Tk(k){return H(k,"OPENAI_TTS_INSTRUCTIONS")??""}function Bk(k){return H(k,"OPENAI_IMAGE_MODEL")??"dall-e-3"}function fk(k){return _k(k,"OPENAI_EXPERIMENTAL_TELEMETRY",!1)}function bk(k){return m(k,"OPENAI_EMBEDDING_DIMENSIONS",1536)}function xk(k){return m(k,"OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS",8192)}function Mk(k){return H(k,"OPENAI_RESEARCH_MODEL")??"o3-deep-research"}function Ck(k){return m(k,"OPENAI_RESEARCH_TIMEOUT",3600000)}globalThis.AI_SDK_LOG_WARNINGS??=!1;function yk(k,I){G0(I)}async function G0(k){if(w()){u.debug("[OpenAI] Skipping API validation in browser environment");return}if(!L(k)){u.warn("[OpenAI] OPENAI_API_KEY is not configured. OpenAI functionality will fail until a valid API key is provided.");return}try{let $=R(k),K=await fetch(`${$}/models`,{headers:F(k)});if(!K.ok){u.warn(`[OpenAI] API key validation failed: ${K.status} ${K.statusText}. Please verify your OPENAI_API_KEY is correct.`);return}}catch($){let K=$ instanceof Error?$.message:String($);u.warn(`[OpenAI] API validation error: ${K}. OpenAI functionality may be limited.`)}}import{logger as y,recordLlmCall as ck}from"@elizaos/core";import{logger as H0}from"@elizaos/core";var B={WAV:{HEADER:[82,73,70,70],IDENTIFIER:[87,65,86,69]},MP3_ID3:[73,68,51],OGG:[79,103,103,83],FLAC:[102,76,97,67],FTYP:[102,116,121,112],WEBM_EBML:[26,69,223,163]},Y0=12;function f(k,I,$){for(let K=0;K<$.length;K++){let X=$[K];if(X===void 0||k[I+K]!==X)return!1}return!0}function r(k){if(k.length<Y0)return"application/octet-stream";if(f(k,0,B.WAV.HEADER)&&f(k,8,B.WAV.IDENTIFIER))return"audio/wav";let I=k[0],$=k[1];if(f(k,0,B.MP3_ID3)||I===255&&$!==void 0&&($&224)===224)return"audio/mpeg";if(f(k,0,B.OGG))return"audio/ogg";if(f(k,0,B.FLAC))return"audio/flac";if(f(k,4,B.FTYP))return"audio/mp4";if(f(k,0,B.WEBM_EBML))return"audio/webm";return H0.warn("Could not detect audio format from buffer, using generic binary type"),"application/octet-stream"}function U0(k){switch(k){case"audio/wav":return"wav";case"audio/mpeg":return"mp3";case"audio/ogg":return"ogg";case"audio/flac":return"flac";case"audio/mp4":return"m4a";case"audio/webm":return"webm";case"application/octet-stream":return"bin"}}function Sk(k){return`recording.${U0(k)}`}function vk(k){return k instanceof Blob||k instanceof File}function t(k){return Buffer.isBuffer(k)}function q0(k){return typeof k==="object"&&k!==null&&"audio"in k&&(vk(k.audio)||t(k.audio))}function V0(k){return typeof k==="object"&&k!==null&&"audioUrl"in k&&typeof k.audioUrl==="string"}async function hk(k){let I=await fetch(k);if(!I.ok)throw Error(`Failed to fetch audio from URL: ${I.status}`);return I.blob()}async function a(k,I){let $=Nk(k),K,X={};if(typeof I==="string")y.debug(`[OpenAI] Fetching audio from URL: ${I}`),K=await hk(I);else if(vk(I))K=I;else if(t(I)){let G=r(I);y.debug(`[OpenAI] Auto-detected audio MIME type: ${G}`),K=new Blob([new Uint8Array(I)],{type:G})}else if(q0(I)){if(X=I,I.model)$=I.model;if(t(I.audio)){let G=I.mimeType??r(I.audio);y.debug(`[OpenAI] Using MIME type: ${G}`),K=new Blob([new Uint8Array(I.audio)],{type:G})}else K=I.audio}else if(V0(I))y.debug(`[OpenAI] Fetching audio from URL: ${I.audioUrl}`),K=await hk(I.audioUrl),X={prompt:I.prompt};else throw Error("TRANSCRIPTION expects Blob, File, Buffer, URL string, or TranscriptionParams object");y.debug(`[OpenAI] Using TRANSCRIPTION model: ${$}`);let A=K.type||"audio/webm",J=K.name||Sk(A.startsWith("audio/")?A:"audio/webm"),Q=new FormData;if(Q.append("file",K,J),Q.append("model",$),X.language)Q.append("language",X.language);if(X.responseFormat)Q.append("response_format",X.responseFormat);if(X.prompt)Q.append("prompt",X.prompt);if(X.temperature!==void 0)Q.append("temperature",String(X.temperature));if(X.timestampGranularities)for(let G of X.timestampGranularities)Q.append("timestamp_granularities[]",G);let Z=R(k),j={model:$,systemPrompt:X.prompt??"",userPrompt:[`audio transcription request: filename=${J}`,`mimeType=${A}`,X.language?`language=${X.language}`:"",X.responseFormat?`responseFormat=${X.responseFormat}`:""].filter(Boolean).join(" "),temperature:X.temperature??0,maxTokens:0,purpose:"external_llm",actionType:"openai.audio.transcriptions.create"};return(await ck(k,j,async()=>{let G=await fetch(`${Z}/audio/transcriptions`,{method:"POST",headers:F(k),body:Q});if(!G.ok){let _=await G.text().catch(()=>"Unknown error");throw Error(`OpenAI transcription failed: ${G.status} ${G.statusText} - ${_}`)}let U=await G.json();return j.response=U.text,U})).text}async function e(k,I){let $,K,X="mp3",A,J;if(typeof I==="string")$=I,K=void 0;else{if($=I.text,K=I.voice,"format"in I&&I.format)X=I.format;if("model"in I&&I.model)A=I.model;if("instructions"in I&&I.instructions)J=I.instructions}if(A=A??Ok(k),K=K??Pk(k),J=J??Tk(k),y.debug(`[OpenAI] Using TEXT_TO_SPEECH model: ${A}`),!$||$.trim().length===0)throw Error("TEXT_TO_SPEECH requires non-empty text");if($.length>4096)throw Error("TEXT_TO_SPEECH text exceeds 4096 character limit");let Q=["alloy","echo","fable","onyx","nova","shimmer"];if(K&&!Q.includes(K))throw Error(`Invalid voice: ${K}. Must be one of: ${Q.join(", ")}`);let Z=R(k),j={model:A,voice:K,input:$,response_format:X};if(J&&J.length>0)j.instructions=J;let Y={model:A,systemPrompt:J??"",userPrompt:$,temperature:0,maxTokens:0,purpose:"external_llm",actionType:"openai.audio.speech.create"};return ck(k,Y,async()=>{let G=await fetch(`${Z}/audio/speech`,{method:"POST",headers:{...F(k),"Content-Type":"application/json",...X==="mp3"?{Accept:"audio/mpeg"}:{}},body:JSON.stringify(j)});if(!G.ok){let _=await G.text().catch(()=>"Unknown error");throw Error(`OpenAI TTS failed: ${G.status} ${G.statusText} - ${_}`)}let U=await G.arrayBuffer();return Y.response=`[audio bytes=${U.byteLength} format=${X}]`,U})}import{logger as d,ModelType as F0,VECTOR_DIMS as W0}from"@elizaos/core";import{EventType as _0}from"@elizaos/core";var gk=200;function z0(k){if(k.length<=gk)return k;return`${k.slice(0,gk)}…`}function R0(k){if("promptTokens"in k){let I="promptTokensDetails"in k?k.promptTokensDetails:void 0,$=k.cachedPromptTokens??I?.cachedTokens;return{promptTokens:k.promptTokens??0,completionTokens:k.completionTokens??0,totalTokens:k.totalTokens??(k.promptTokens??0)+(k.completionTokens??0),cachedPromptTokens:$}}if("inputTokens"in k||"outputTokens"in k){let I=k.inputTokens??0,$=k.outputTokens??0,K=k.totalTokens??I+$;return{promptTokens:I,completionTokens:$,totalTokens:K,cachedPromptTokens:k.cachedInputTokens}}return{promptTokens:0,completionTokens:0,totalTokens:0}}function S(k,I,$,K){let X=R0(K),A={runtime:k,source:"openai",provider:"openai",type:I,prompt:z0($),tokens:{prompt:X.promptTokens,completion:X.completionTokens,total:X.totalTokens,...X.cachedPromptTokens!==void 0?{cached:X.cachedPromptTokens}:{}}};k.emitEvent(_0.MODEL_USED,A)}function D0(k){let I=Object.values(W0);if(!I.includes(k))throw Error(`Invalid embedding dimension: ${k}. Must be one of: ${I.join(", ")}`);return k}function E0(k){if(k===null)return null;if(typeof k==="string")return k;if(typeof k==="object"&&typeof k.text==="string")return k.text;throw Error("Invalid embedding params: expected string, { text: string }, or null")}function w0(k){let I=w()?"OPENAI_BROWSER_EMBEDDING_URL":"OPENAI_EMBEDDING_URL",$=H(k,I);return typeof $==="string"&&$.trim().length>0}function L0(k){return P(k)&&!w0(k)}function dk(k){let I=2166136261;for(let $=0;$<k.length;$+=1)I^=k.charCodeAt($),I=Math.imul(I,16777619);return I>>>0}function N0(k,I){let $=Array(I).fill(0),K=k.toLowerCase(),X=K.match(/[a-z0-9]+(?:[_-][a-z0-9]+)*/g)??[K],A=(Q,Z)=>{let j=dk(Q),Y=j%I,G=(j&1)===0?1:-1;$[Y]+=G*Z;let U=dk(`b:${Q}`),_=U%I,z=(U&1)===0?1:-1;$[_]+=z*Z*0.5};X.forEach((Q,Z)=>{if(A(Q,1),Z>0)A(`${X[Z-1]} ${Q}`,0.35)}),A(K.slice(0,512),0.15);let J=Math.sqrt($.reduce((Q,Z)=>Q+Z*Z,0));if(J===0)return $[0]=1,$;return $.map((Q)=>Q/J)}async function kk(k,I){let $=wk(k),K=D0(bk(k)),X=E0(I);if(X===null){d.debug("[OpenAI] Creating test embedding for initialization");let _=Array(K).fill(0);return _[0]=0.1,_}let A=X.trim();if(A.length===0)throw Error("Cannot generate embedding for empty text");let J=32000;if(A.length>J)d.warn(`[OpenAI] Embedding input too long (~${Math.ceil(A.length/4)} tokens), truncating to ~8000 tokens`),A=A.slice(0,J);if(L0(k))return d.debug("[OpenAI] Using deterministic local embedding fallback for Cerebras mode"),N0(A,K);let Z=`${Rk(k)}/embeddings`;d.debug(`[OpenAI] Generating embedding with model: ${$}`);let j=await fetch(Z,{method:"POST",headers:{...F(k,!0),"Content-Type":"application/json"},body:JSON.stringify({model:$,input:A})});if(!j.ok){let _=await j.text().catch(()=>"Unknown error");throw Error(`OpenAI embedding API error: ${j.status} ${j.statusText} - ${_}`)}let Y=await j.json(),G=Y?.data?.[0];if(!G?.embedding)throw Error("OpenAI API returned invalid embedding response structure");let U=G.embedding;if(U.length!==K)throw Error(`Embedding dimension mismatch: got ${U.length}, expected ${K}. Check OPENAI_EMBEDDING_DIMENSIONS setting.`);if(Y.usage)S(k,F0.TEXT_EMBEDDING,A,{promptTokens:Y.usage.prompt_tokens,completionTokens:0,totalTokens:Y.usage.total_tokens});return d.debug(`[OpenAI] Generated embedding with ${U.length} dimensions`),U}import{logger as pk,ModelType as O0,recordLlmCall as ok}from"@elizaos/core";var lk="Please analyze this image and provide a title and detailed description.";async function Ik(k,I){let $=Bk(k),K=I.count??1,X=I.size??"1024x1024",A=I;if(pk.debug(`[OpenAI] Using IMAGE model: ${$}`),I.prompt.trim().length===0)throw Error("IMAGE generation requires a non-empty prompt");if(K<1||K>10)throw Error("IMAGE count must be between 1 and 10");let J=R(k),Q={model:$,prompt:I.prompt,n:K,size:X};if(A.quality)Q.quality=A.quality;if(A.style)Q.style=A.style;let Z={model:$,systemPrompt:"",userPrompt:I.prompt,temperature:0,maxTokens:0,purpose:"external_llm",actionType:"openai.images.generate"},j=await ok(k,Z,async()=>{let Y=await fetch(`${J}/images/generations`,{method:"POST",headers:{...F(k),"Content-Type":"application/json"},body:JSON.stringify(Q)});if(!Y.ok){let U=await Y.text().catch(()=>"Unknown error");throw Error(`OpenAI image generation failed: ${Y.status} ${Y.statusText} - ${U}`)}let G=await Y.json();return Z.response=JSON.stringify(G.data),G});if(j.data.length===0)throw Error("OpenAI API returned no images");return j.data.map((Y)=>({url:Y.url,revisedPrompt:Y.revised_prompt}))}function P0(k){return k.match(/title[:\s]+(.+?)(?:\n|$)/i)?.[1]?.trim()??"Image Analysis"}function T0(k){return k.replace(/title[:\s]+(.+?)(?:\n|$)/i,"").trim()}async function $k(k,I){let $=Lk(k),K=xk(k);pk.debug(`[OpenAI] Using IMAGE_DESCRIPTION model: ${$}`);let X,A;if(typeof I==="string")X=I,A=lk;else X=I.imageUrl,A=I.prompt??lk;if(!X||X.trim().length===0)throw Error("IMAGE_DESCRIPTION requires a valid image URL");let J=R(k),Q={model:$,messages:[{role:"user",content:[{type:"text",text:A},{type:"image_url",image_url:{url:X}}]}],max_tokens:K},Z={model:$,systemPrompt:"",userPrompt:A,temperature:0,maxTokens:K,purpose:"external_llm",actionType:"openai.chat.completions.create"},j=await ok(k,Z,async()=>{let U=await fetch(`${J}/chat/completions`,{method:"POST",headers:{...F(k),"Content-Type":"application/json"},body:JSON.stringify(Q)});if(!U.ok){let z=await U.text().catch(()=>"Unknown error");throw Error(`OpenAI image description failed: ${U.status} ${U.statusText} - ${z}`)}let _=await U.json();if(Z.response=_.choices?.[0]?.message?.content??"",_.usage)Z.promptTokens=_.usage.prompt_tokens,Z.completionTokens=_.usage.completion_tokens;return _});if(j.usage)S(k,O0.IMAGE_DESCRIPTION,typeof I==="string"?I:I.prompt??"",{promptTokens:j.usage.prompt_tokens,completionTokens:j.usage.completion_tokens,totalTokens:j.usage.total_tokens});let G=j.choices?.[0]?.message?.content;if(!G)throw Error("OpenAI API returned empty image description");return{title:P0(G),description:T0(G)}}import{logger as N,recordLlmCall as B0}from"@elizaos/core";function f0(k){switch(k.type){case"web_search_preview":return{type:"web_search_preview"};case"file_search":return{type:"file_search",vector_store_ids:k.vectorStoreIds};case"code_interpreter":return{type:"code_interpreter",container:k.container??{type:"auto"}};case"mcp":return{type:"mcp",server_label:k.serverLabel,server_url:k.serverUrl,require_approval:k.requireApproval??"never"};default:throw Error(`Unknown research tool type: ${k.type}`)}}function b0(k){switch(k.type){case"web_search_call":return{id:k.id??"",type:"web_search_call",status:k.status??"completed",action:{type:k.action?.type??"search",query:k.action?.query,url:k.action?.url}};case"file_search_call":return{id:k.id??"",type:"file_search_call",status:k.status??"completed",query:k.query??"",results:k.results?.map((I)=>({fileId:I.file_id,fileName:I.file_name,score:I.score}))};case"code_interpreter_call":return{id:k.id??"",type:"code_interpreter_call",status:k.status??"completed",code:k.code??"",output:k.output};case"mcp_tool_call":return{id:k.id??"",type:"mcp_tool_call",status:k.status??"completed",serverLabel:k.server_label??"",toolName:k.tool_name??"",arguments:k.arguments??{},result:k.result};case"message":return{type:"message",content:k.content?.map((I)=>({type:"output_text",text:I.text,annotations:I.annotations?.map(($)=>({url:$.url,title:$.title,startIndex:$.start_index,endIndex:$.end_index}))??[]}))??[]};default:return null}}function x0(k){if(k.output_text){let K=[];if(k.output){for(let X of k.output)if(X.type==="message"&&X.content){for(let A of X.content)if(A.annotations)for(let J of A.annotations)K.push({url:J.url,title:J.title,startIndex:J.start_index,endIndex:J.end_index})}}return{text:k.output_text,annotations:K}}let I="",$=[];if(k.output){for(let K of k.output)if(K.type==="message"&&K.content){for(let X of K.content)if(I+=X.text,X.annotations)for(let A of X.annotations)$.push({url:A.url,title:A.title,startIndex:A.start_index,endIndex:A.end_index})}}return{text:I,annotations:$}}async function Kk(k,I){let $=L(k);if(!$)throw Error("OPENAI_API_KEY is required for deep research. Set it in your environment variables or runtime settings.");let K=R(k),X=I.model??Mk(k),A=Ck(k);N.debug(`[OpenAI] Starting deep research with model: ${X}`),N.debug(`[OpenAI] Research input: ${I.input.substring(0,100)}...`);let J=I.tools?.filter((z)=>z.type==="web_search_preview"||z.type==="file_search"||z.type==="mcp");if(!J||J.length===0)N.debug("[OpenAI] No data source tools specified, defaulting to web_search_preview"),I.tools=[{type:"web_search_preview"},...I.tools??[]];let Q={model:X,input:I.input};if(I.instructions)Q.instructions=I.instructions;if(I.background!==void 0)Q.background=I.background;if(I.tools&&I.tools.length>0)Q.tools=I.tools.map(f0);if(I.maxToolCalls!==void 0)Q.max_tool_calls=I.maxToolCalls;if(I.reasoningSummary)Q.reasoning={summary:I.reasoningSummary};N.debug(`[OpenAI] Research request body: ${JSON.stringify(Q,null,2)}`);let Z={model:X,systemPrompt:I.instructions??"",userPrompt:I.input,temperature:0,maxTokens:0,purpose:"external_llm",actionType:"openai.responses.create"},j=await B0(k,Z,async()=>{let z=await fetch(`${K}/responses`,{method:"POST",headers:{Authorization:`Bearer ${$}`,"Content-Type":"application/json"},body:JSON.stringify(Q),signal:AbortSignal.timeout(A)});if(!z.ok){let l=await z.text();throw N.error(`[OpenAI] Research request failed: ${z.status} ${l}`),Error(`Deep research request failed: ${z.status} ${z.statusText}`)}let O=await z.json();return Z.response=O.output_text??"",O});if(j.error)throw N.error(`[OpenAI] Research API error: ${j.error.message}`),Error(`Deep research error: ${j.error.message}`);N.debug(`[OpenAI] Research response received. Status: ${j.status??"completed"}`);let{text:Y,annotations:G}=x0(j),U=[];if(j.output)for(let z of j.output){let O=b0(z);if(O)U.push(O)}let _={id:j.id,text:Y,annotations:G,outputItems:U,status:j.status};return N.info(`[OpenAI] Research completed. Text length: ${Y.length}, Annotations: ${G.length}, Output items: ${U.length}`),_}import{buildCanonicalSystemPrompt as C0,dropDuplicateLeadingSystemMessage as y0,logger as S0,ModelType as x,normalizeSchemaForCerebras as h0,recordLlmCall as nk,resolveEffectiveSystemPrompt as c0,sanitizeFunctionNameForCerebras as v0}from"@elizaos/core";import{generateText as g0,jsonSchema as sk,Output as d0,streamText as l0}from"ai";import{createOpenAI as uk}from"@ai-sdk/openai";var M0="sk-proxy";function Xk(k){let I=R(k),$=L(k);if(!$&&zk(k))return uk({apiKey:M0,baseURL:I});if(!$)throw Error("OPENAI_API_KEY is required. Set it in your environment variables or runtime settings.");return uk({apiKey:$,baseURL:I})}var p0=x.TEXT_NANO??"TEXT_NANO",o0=x.TEXT_MEDIUM??"TEXT_MEDIUM",u0=x.TEXT_MEGA??"TEXT_MEGA",n0=x.RESPONSE_HANDLER??"RESPONSE_HANDLER",i0=x.ACTION_PLANNER??"ACTION_PLANNER";function m0(k){let I=[{type:"text",text:k.prompt}];for(let $ of k.attachments??[])I.push({type:"file",data:$.data,mediaType:$.mediaType,...$.filename?{filename:$.filename}:{}});return I}function rk(k){if(!k)return;let I=k.inputTokens??0,$=k.outputTokens??0,K=k,X=ik(K.cacheReadInputTokens,K.cachedInputTokens,K.inputTokenDetails?.cacheReadTokens,K.inputTokenDetails?.cachedInputTokens,K.input_tokens_details?.cache_read_input_tokens,K.input_tokens_details?.cached_tokens,K.prompt_tokens_details?.cached_tokens)??void 0,A=ik(K.cacheCreationInputTokens,K.cacheWriteInputTokens,K.inputTokenDetails?.cacheCreationInputTokens,K.inputTokenDetails?.cacheCreationTokens,K.inputTokenDetails?.cacheWriteTokens,K.input_tokens_details?.cache_creation_input_tokens);return{promptTokens:I,completionTokens:$,totalTokens:I+$,cachedPromptTokens:X,cacheReadInputTokens:X,cacheCreationInputTokens:A}}function ik(...k){for(let I of k){if(typeof I==="number"&&Number.isFinite(I))return I;if(typeof I==="string"&&I.trim().length>0){let $=Number(I);if(Number.isFinite($))return $}}return}function s0(k){let I=k;return{promptCacheKey:I.providerOptions?.openai?.promptCacheKey,promptCacheRetention:I.providerOptions?.openai?.promptCacheRetention}}function r0(k,I){let K=k.providerOptions,X=s0(k);if(!K&&!X.promptCacheKey&&!X.promptCacheRetention)return;let A=P(I),{agentName:J,openai:Q,...Z}=K??{},Y={...(()=>{if(!Q||typeof Q!=="object")return Q;if(!A)return Q;let{promptCacheRetention:U,..._}=Q;return _})()??{},...X.promptCacheKey?{promptCacheKey:X.promptCacheKey}:{},...!A&&X.promptCacheRetention?{promptCacheRetention:X.promptCacheRetention}:{}},G={...Z,...Object.keys(Y).length>0?{openai:Y}:{}};return Object.keys(G).length>0?G:void 0}function t0(k){if(k&&typeof k==="object"&&"responseFormat"in k&&"parseCompleteOutput"in k)return k;let I=k&&typeof k==="object"&&"schema"in k?k:{schema:k};return d0.object({schema:sk(h(I.schema,!0)),...I.name?{name:I.name}:{},...I.description?{description:I.description}:{}})}function a0(k,I={}){if(!k)return;if(!Array.isArray(k))return k;let $={};for(let K of k){let X=b(K),A=b(X.function),J=E(X.name,A.name);if(!J)throw Error("[OpenAI] Native tool definition is missing a name.");let Q=E(X.description,A.description),Z=X.parameters??A.parameters??{type:"object"},j=h(Z,!0);if(I.cerebrasMode)j=h0(j);let Y=I.cerebrasMode?v0(J):J;$[Y]={...Q?{description:Q}:{},inputSchema:sk(j)}}return Object.keys($).length>0?$:void 0}function e0(k){if(!Array.isArray(k))return;return k.map((I)=>kI(I))}function kI(k){let I=b(k),$=ZI(I.providerOptions);if(I.role==="system")return{role:"system",content:tk(I.content),...$?{providerOptions:$}:{}};if(I.role==="assistant")return{role:"assistant",content:II(I),...$?{providerOptions:$}:{}};if(I.role==="tool")return{role:"tool",content:$I(I),...$?{providerOptions:$}:{}};return{role:"user",content:KI(I.content),...$?{providerOptions:$}:{}}}function II(k){let I=Array.isArray(k.toolCalls)?k.toolCalls:[];if(I.length===0){if(Array.isArray(k.content)||typeof k.content==="string")return k.content;return""}let $=[];if(typeof k.content==="string"&&k.content.length>0)$.push({type:"text",text:k.content});else if(Array.isArray(k.content))$.push(...k.content);for(let K of I){let X=b(K),A=b(X.function),J=E(X.toolCallId,X.id),Q=E(X.toolName,X.name,A.name);if(!J||!Q)continue;$.push({type:"tool-call",toolCallId:J,toolName:Q,input:XI(X,A)})}return $}function $I(k){if(Array.isArray(k.content))return k.content;let I=E(k.toolCallId,k.id)??"tool-call",$=E(k.toolName,k.name)??"tool",K=ak(k.content);return[{type:"tool-result",toolCallId:I,toolName:$,output:typeof K==="string"?{type:"text",value:K}:{type:"json",value:K}}]}function KI(k){if(Array.isArray(k))return k;return tk(k)}function tk(k){if(typeof k==="string")return k;if(k==null)return"";return typeof k==="object"?JSON.stringify(k):String(k)}function XI(k,I){if("input"in k)return k.input;return ak(k.arguments??I.arguments??{})}function ak(k){if(typeof k!=="string")return k??"";try{return JSON.parse(k)}catch{return k}}function AI(k){if(!k)return;if(typeof k==="string"&&(k==="auto"||k==="none"||k==="required"))return k;let I=b(k);if(I.type==="tool"){if(typeof I.toolName==="string"&&I.toolName.length>0)return k;let K=E(I.toolName,I.name);if(K)return{type:"tool",toolName:K}}if(I.type==="function"){let K=b(I.function),X=E(K.name);if(X)return{type:"tool",toolName:X}}let $=E(I.name);if($)return{type:"tool",toolName:$};return k}function h(k,I=!1){if(!k||typeof k!=="object"||Array.isArray(k))return{type:"object"};let K={...k};if(typeof K.type!=="string"){let X=QI(K,I);if(X)K.type=X}if(K.properties&&typeof K.properties==="object"&&!Array.isArray(K.properties)){let X={};for(let[Q,Z]of Object.entries(K.properties))X[Q]=h(Z);K.properties=X;let A=Object.keys(X),J=Array.isArray(K.required)?K.required.filter((Q)=>typeof Q==="string"):[];K.required=[...new Set([...J,...A])]}if(K.type==="object"&&K.additionalProperties!==!1)K.additionalProperties=!1;if(K.items)K.items=Array.isArray(K.items)?K.items.map((X)=>h(X)):h(K.items);for(let X of["anyOf","oneOf","allOf"]){let A=K[X];if(Array.isArray(A))K[X]=A.map((J)=>h(J))}return K}function QI(k,I){if("properties"in k||"required"in k||"additionalProperties"in k||I)return"object";if("items"in k)return"array";if(Array.isArray(k.enum)&&k.enum.length>0){let $=new Set(k.enum.map((K)=>typeof K));if($.size===1){let[K]=[...$];if(K==="string"||K==="number"||K==="boolean")return K}}return}function b(k){return k&&typeof k==="object"&&!Array.isArray(k)?k:{}}function ZI(k){return k&&typeof k==="object"&&!Array.isArray(k)?k:void 0}function E(...k){for(let I of k)if(typeof I==="string"&&I.length>0)return I;return}function JI(k){return Boolean(k.messages||k.tools||k.toolChoice||k.responseSchema)}function jI(k,I){return{text:k.text,toolCalls:k.toolCalls??[],finishReason:k.finishReason,usage:rk(k.usage),providerMetadata:GI(k.providerMetadata,I)}}function GI(k,I){if(!I)return k;if(k&&typeof k==="object"&&!Array.isArray(k))return{...k,modelName:I};return{modelName:I}}function mk(k,I,$,K,X,A,J){let Q=I,Z=J,j=Z&&"prompt"in Z?Z.prompt:void 0,Y=Z&&"messages"in Z&&Array.isArray(Z.messages)?Z.messages:void 0,G=typeof Z?.system==="string"?Z.system:$;return{model:k,modelType:X,provider:"vercel-ai-sdk",systemPrompt:G??"",userPrompt:typeof j==="string"?j:typeof I.prompt==="string"?I.prompt:"",prompt:typeof j==="string"?j:void 0,messages:Y,tools:Z?.tools??Q.tools,toolChoice:Z?.toolChoice??Q.toolChoice,output:Z?.output!==void 0?HI(Q.responseSchema,Z.output):void 0,responseSchema:Q.responseSchema,providerOptions:A??Z?.providerOptions??Q.providerOptions,temperature:I.temperature??0,maxTokens:typeof Z?.maxOutputTokens==="number"?Z.maxOutputTokens:I.maxTokens??8192,purpose:"external_llm",actionType:K}}function HI(k,I){if(k!==void 0)return{type:"object",schema:k};return YI(I)}function YI(k){try{return JSON.parse(JSON.stringify(k,(I,$)=>{if(typeof $==="function")return;if(typeof $==="bigint")return $.toString();return $}))}catch{return String(k)}}function UI(k,I){if(!I)return;k.promptTokens=I.inputTokens??0,k.completionTokens=I.outputTokens??0}async function M(k,I,$,K){let X=I,A=Xk(k),J=K(k);S0.debug(`[OpenAI] Using ${$} model: ${J}`);let Q=r0(I,k),j=(X.attachments?.length??0)>0?m0(X):void 0,Y=JI(X),G=c0({params:X,fallback:C0({character:k.character})}),U=X.providerOptions?.agentName,_={isEnabled:fk(k),functionId:U?`agent:${U}`:void 0,metadata:U?{agentName:U}:void 0},z=A.chat(J),O=P(k),l=a0(X.tools,{cerebrasMode:O}),qk=AI(X.toolChoice),Vk=e0(X.messages),n=y0(Vk,G),i=n&&n.length>0?n:Vk,A0=typeof I.prompt==="string"&&I.prompt.length>0?I.prompt:"",Q0=i&&i.length>0?{messages:i}:j?{messages:[{role:"user",content:j}]}:{prompt:A0},p={model:z,...Q0,system:G,allowSystemInMessages:!0,maxOutputTokens:I.maxTokens??8192,experimental_telemetry:_,...l?{tools:l}:{},...qk?{toolChoice:qk}:{},...X.responseSchema&&!P(k)?{output:t0(X.responseSchema)}:{},...Q?{providerOptions:Q}:{}};if(I.stream){let D=mk(J,I,G,"ai.streamText",$,Q,p);D.response="";let c=await nk(k,D,()=>l0(p));return{textStream:c.textStream,text:Promise.resolve(c.text),...Y?{toolCalls:Promise.resolve(c.toolCalls)}:{},usage:Promise.resolve(c.usage).then(rk),finishReason:Promise.resolve(c.finishReason).then((Z0)=>Z0)}}let C=mk(J,I,G,"ai.generateText",$,Q,p),o=await nk(k,C,async()=>{let D=await g0(p);return C.response=D.text,C.toolCalls=D.toolCalls??[],C.finishReason=D.finishReason,C.providerMetadata=D.providerMetadata,UI(C,D.usage),D});if(o.usage)S(k,$,I.prompt??"",o.usage);if(Y)return jI(o,J);return o.text}async function Ak(k,I){return M(k,I,x.TEXT_SMALL,T)}async function Qk(k,I){return M(k,I,p0,Fk)}async function Zk(k,I){return M(k,I,o0,s)}async function Jk(k,I){return M(k,I,x.TEXT_LARGE,g)}async function jk(k,I){return M(k,I,u0,Wk)}async function Gk(k,I){return M(k,I,n0,Dk)}async function Hk(k,I){return M(k,I,i0,Ek)}import{ModelType as K0}from"@elizaos/core";import{ModelType as qI}from"@elizaos/core";import{encodingForModel as VI,getEncoding as _I}from"js-tiktoken";function ek(k){let $=k.toLowerCase().includes("4o")?"o200k_base":"cl100k_base";try{return VI(k)}catch{return _I($)}}function k0(k,I){if(I===qI.TEXT_SMALL)return T(k);return g(k)}function I0(k,I,$){let K=k0(k,I);return ek(K).encode($)}function $0(k,I,$){let K=k0(k,I);return ek(K).decode($)}async function Yk(k,I){if(!I.prompt)throw Error("Tokenization requires a non-empty prompt");let $=I.modelType??K0.TEXT_LARGE;return I0(k,$,I.prompt)}async function Uk(k,I){if(!I.tokens||!Array.isArray(I.tokens))throw Error("Detokenization requires a valid tokens array");if(I.tokens.length===0)return"";for(let K=0;K<I.tokens.length;K++){let X=I.tokens[K];if(typeof X!=="number"||!Number.isFinite(X))throw Error(`Invalid token at index ${K}: expected number`)}let $=I.modelType??K0.TEXT_LARGE;return $0(k,$,I.tokens)}function zI(){if(typeof process>"u")return{};return process.env}var q=zI(),RI=V.TEXT_NANO??"TEXT_NANO",FI=V.TEXT_MEDIUM??"TEXT_MEDIUM",WI=V.TEXT_MEGA??"TEXT_MEGA",DI=V.RESPONSE_HANDLER??"RESPONSE_HANDLER",EI=V.ACTION_PLANNER??"ACTION_PLANNER",wI={name:"openai",description:"OpenAI API integration for text, image, audio, and embedding models",autoEnable:{envKeys:["OPENAI_API_KEY"]},config:{OPENAI_API_KEY:q.OPENAI_API_KEY??null,OPENAI_BASE_URL:q.OPENAI_BASE_URL??null,OPENAI_NANO_MODEL:q.OPENAI_NANO_MODEL??null,OPENAI_MEDIUM_MODEL:q.OPENAI_MEDIUM_MODEL??null,OPENAI_SMALL_MODEL:q.OPENAI_SMALL_MODEL??null,OPENAI_LARGE_MODEL:q.OPENAI_LARGE_MODEL??null,OPENAI_MEGA_MODEL:q.OPENAI_MEGA_MODEL??null,OPENAI_RESPONSE_HANDLER_MODEL:q.OPENAI_RESPONSE_HANDLER_MODEL??null,OPENAI_SHOULD_RESPOND_MODEL:q.OPENAI_SHOULD_RESPOND_MODEL??null,OPENAI_ACTION_PLANNER_MODEL:q.OPENAI_ACTION_PLANNER_MODEL??null,OPENAI_PLANNER_MODEL:q.OPENAI_PLANNER_MODEL??null,NANO_MODEL:q.NANO_MODEL??null,MEDIUM_MODEL:q.MEDIUM_MODEL??null,SMALL_MODEL:q.SMALL_MODEL??null,LARGE_MODEL:q.LARGE_MODEL??null,MEGA_MODEL:q.MEGA_MODEL??null,RESPONSE_HANDLER_MODEL:q.RESPONSE_HANDLER_MODEL??null,SHOULD_RESPOND_MODEL:q.SHOULD_RESPOND_MODEL??null,ACTION_PLANNER_MODEL:q.ACTION_PLANNER_MODEL??null,PLANNER_MODEL:q.PLANNER_MODEL??null,OPENAI_EMBEDDING_MODEL:q.OPENAI_EMBEDDING_MODEL??null,OPENAI_EMBEDDING_API_KEY:q.OPENAI_EMBEDDING_API_KEY??null,OPENAI_EMBEDDING_URL:q.OPENAI_EMBEDDING_URL??null,OPENAI_EMBEDDING_DIMENSIONS:q.OPENAI_EMBEDDING_DIMENSIONS??null,OPENAI_IMAGE_DESCRIPTION_MODEL:q.OPENAI_IMAGE_DESCRIPTION_MODEL??null,OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS:q.OPENAI_IMAGE_DESCRIPTION_MAX_TOKENS??null,OPENAI_EXPERIMENTAL_TELEMETRY:q.OPENAI_EXPERIMENTAL_TELEMETRY??null,OPENAI_RESEARCH_MODEL:q.OPENAI_RESEARCH_MODEL??null,OPENAI_RESEARCH_TIMEOUT:q.OPENAI_RESEARCH_TIMEOUT??null},async init(k,I){yk(k,I)},models:{[V.TEXT_EMBEDDING]:async(k,I)=>{return kk(k,I)},[V.TEXT_TOKENIZER_ENCODE]:async(k,I)=>{return Yk(k,I)},[V.TEXT_TOKENIZER_DECODE]:async(k,I)=>{return Uk(k,I)},[V.TEXT_SMALL]:async(k,I)=>{return Ak(k,I)},[RI]:async(k,I)=>{return Qk(k,I)},[FI]:async(k,I)=>{return Zk(k,I)},[V.TEXT_LARGE]:async(k,I)=>{return Jk(k,I)},[WI]:async(k,I)=>{return jk(k,I)},[DI]:async(k,I)=>{return Gk(k,I)},[EI]:async(k,I)=>{return Hk(k,I)},[V.IMAGE]:async(k,I)=>{return Ik(k,I)},[V.IMAGE_DESCRIPTION]:async(k,I)=>{return $k(k,I)},[V.TRANSCRIPTION]:async(k,I)=>{return a(k,I)},[V.TEXT_TO_SPEECH]:async(k,I)=>{return e(k,I)},[V.RESEARCH]:async(k,I)=>{return Kk(k,I)}},tests:[{name:"openai_plugin_tests",tests:[{name:"openai_test_api_connectivity",fn:async(k)=>{let I=R(k),$=await fetch(`${I}/models`,{headers:F(k)});if(!$.ok)throw Error(`API connectivity test failed: ${$.status} ${$.statusText}`);let K=await $.json();W.info(`[OpenAI Test] API connected. ${K.data?.length??0} models available.`)}},{name:"openai_test_text_embedding",fn:async(k)=>{let I=await k.useModel(V.TEXT_EMBEDDING,{text:"Hello, world!"});if(!Array.isArray(I)||I.length===0)throw Error("Embedding should return a non-empty array");W.info(`[OpenAI Test] Generated embedding with ${I.length} dimensions`)}},{name:"openai_test_text_small",fn:async(k)=>{let I=await k.useModel(V.TEXT_SMALL,{prompt:"Say hello in exactly 5 words."});if(typeof I!=="string"||I.length===0)throw Error("TEXT_SMALL should return non-empty string");W.info(`[OpenAI Test] TEXT_SMALL generated: "${I.substring(0,50)}..."`)}},{name:"openai_test_text_large",fn:async(k)=>{let I=await k.useModel(V.TEXT_LARGE,{prompt:"Explain quantum computing in 2 sentences."});if(typeof I!=="string"||I.length===0)throw Error("TEXT_LARGE should return non-empty string");W.info(`[OpenAI Test] TEXT_LARGE generated: "${I.substring(0,50)}..."`)}},{name:"openai_test_tokenizer_roundtrip",fn:async(k)=>{let $=await k.useModel(V.TEXT_TOKENIZER_ENCODE,{prompt:"Hello, tokenizer test!",modelType:V.TEXT_SMALL});if(!Array.isArray($)||$.length===0)throw Error("Tokenization should return non-empty token array");let K=await k.useModel(V.TEXT_TOKENIZER_DECODE,{tokens:$,modelType:V.TEXT_SMALL});if(K!=="Hello, tokenizer test!")throw Error(`Tokenizer roundtrip failed: expected "Hello, tokenizer test!", got "${K}"`);W.info(`[OpenAI Test] Tokenizer roundtrip successful (${$.length} tokens)`)}},{name:"openai_test_streaming",fn:async(k)=>{let I=[],$=await k.useModel(V.TEXT_LARGE,{prompt:"Count from 1 to 5, one number per line.",stream:!0,onStreamChunk:(K)=>{I.push(K)}});if(typeof $!=="string"||$.length===0)throw Error("Streaming should return non-empty result");if(I.length===0)throw Error("No streaming chunks received");W.info(`[OpenAI Test] Streaming test: ${I.length} chunks received`)}},{name:"openai_test_image_description",fn:async(k)=>{let $=await k.useModel(V.IMAGE_DESCRIPTION,"https://upload.wikimedia.org/wikipedia/commons/thumb/a/a7/Camponotus_flavomarginatus_ant.jpg/440px-Camponotus_flavomarginatus_ant.jpg");if(!$||typeof $!=="object"||!("title"in $)||!("description"in $))throw Error("Image description should return { title, description }");W.info(`[OpenAI Test] Image described: "${$.title}"`)}},{name:"openai_test_transcription",fn:async(k)=>{let K=await(await fetch("https://upload.wikimedia.org/wikipedia/commons/2/25/En-Open_Source.ogg")).arrayBuffer(),X=Buffer.from(new Uint8Array(K)),A=await k.useModel(V.TRANSCRIPTION,X);if(typeof A!=="string")throw Error("Transcription should return a string");W.info(`[OpenAI Test] Transcription: "${A.substring(0,50)}..."`)}},{name:"openai_test_text_to_speech",fn:async(k)=>{let I=await k.useModel(V.TEXT_TO_SPEECH,{text:"Hello, this is a text-to-speech test."});if(!(I instanceof ArrayBuffer)||I.byteLength===0)throw Error("TTS should return non-empty ArrayBuffer");W.info(`[OpenAI Test] TTS generated ${I.byteLength} bytes of audio`)}},{name:"openai_test_structured_output_via_text_large",fn:async(k)=>{let I=await k.useModel(V.TEXT_LARGE,{prompt:"Return a JSON object with exactly these fields: name (string), age (number), active (boolean)",responseSchema:{type:"object",properties:{name:{type:"string"},age:{type:"number"},active:{type:"boolean"}},required:["name","age","active"]}});if(!I||typeof I!=="object"&&typeof I!=="string")throw Error("Structured output should return an object or text");W.info(`[OpenAI Test] Structured output: ${JSON.stringify(I).substring(0,100)}`)}},{name:"openai_test_research",fn:async(k)=>{let I=await k.useModel(V.RESEARCH,{input:"What is the current date and time?",tools:[{type:"web_search_preview"}],maxToolCalls:3});if(!I||typeof I!=="object"||!("text"in I))throw Error("Research should return an object with text property");if(typeof I.text!=="string"||I.text.length===0)throw Error("Research result text should be a non-empty string");W.info(`[OpenAI Test] Research completed. Text length: ${I.text.length}, Annotations: ${I.annotations?.length??0}`)}}]}]},X0=wI;var P$=X0;export{wI as openaiPlugin,P$ as default};
|
|
2
|
-
|
|
3
|
-
//# debugId=2301216548B15DFF64756E2164756E21
|