@just-every/ensemble 0.2.212 → 0.2.213
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 +152 -91
- package/dist/cjs/core/ensemble_request.cjs +734 -333
- package/dist/cjs/core/ensemble_request.d.ts.map +1 -1
- package/dist/cjs/core/ensemble_request.js.map +1 -1
- package/dist/cjs/model_providers/base_provider.d.ts.map +1 -1
- package/dist/cjs/model_providers/base_provider.js.map +1 -1
- package/dist/cjs/model_providers/claude.cjs +72 -72
- package/dist/cjs/model_providers/claude.d.ts.map +1 -1
- package/dist/cjs/model_providers/claude.js.map +1 -1
- package/dist/cjs/model_providers/gemini.cjs +3 -0
- package/dist/cjs/model_providers/gemini.d.ts.map +1 -1
- package/dist/cjs/model_providers/gemini.js.map +1 -1
- package/dist/cjs/model_providers/openai.cjs +41 -112
- package/dist/cjs/model_providers/openai.d.ts.map +1 -1
- package/dist/cjs/model_providers/openai.js.map +1 -1
- package/dist/cjs/model_providers/openai_chat.cjs +55 -24
- package/dist/cjs/model_providers/openai_chat.d.ts.map +1 -1
- package/dist/cjs/model_providers/openai_chat.js.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/types/types.d.ts +20 -2
- package/dist/cjs/types/types.d.ts.map +1 -1
- package/dist/cjs/utils/agent.cjs +4 -6
- package/dist/cjs/utils/agent.d.ts.map +1 -1
- package/dist/cjs/utils/agent.js.map +1 -1
- package/dist/cjs/utils/ensemble_result.cjs +43 -4
- package/dist/cjs/utils/ensemble_result.d.ts +10 -1
- package/dist/cjs/utils/ensemble_result.d.ts.map +1 -1
- package/dist/cjs/utils/ensemble_result.js.map +1 -1
- package/dist/cjs/utils/failure_detection.cjs +292 -0
- package/dist/cjs/utils/failure_detection.d.ts +51 -0
- package/dist/cjs/utils/failure_detection.d.ts.map +1 -0
- package/dist/cjs/utils/failure_detection.js.map +1 -0
- package/dist/cjs/utils/json_schema.cjs +490 -0
- package/dist/cjs/utils/json_schema.d.ts +10 -0
- package/dist/cjs/utils/json_schema.d.ts.map +1 -0
- package/dist/cjs/utils/json_schema.js.map +1 -0
- package/dist/cjs/utils/tool_execution_manager.cjs +28 -4
- package/dist/cjs/utils/tool_execution_manager.d.ts +1 -1
- package/dist/cjs/utils/tool_execution_manager.d.ts.map +1 -1
- package/dist/cjs/utils/tool_execution_manager.js.map +1 -1
- package/dist/cjs/utils/verification.cjs +26 -13
- package/dist/cjs/utils/verification.d.ts.map +1 -1
- package/dist/cjs/utils/verification.js.map +1 -1
- package/dist/core/ensemble_request.d.ts.map +1 -1
- package/dist/core/ensemble_request.js +734 -333
- package/dist/core/ensemble_request.js.map +1 -1
- package/dist/model_providers/base_provider.d.ts.map +1 -1
- package/dist/model_providers/base_provider.js.map +1 -1
- package/dist/model_providers/claude.d.ts.map +1 -1
- package/dist/model_providers/claude.js +72 -72
- package/dist/model_providers/claude.js.map +1 -1
- package/dist/model_providers/gemini.d.ts.map +1 -1
- package/dist/model_providers/gemini.js +3 -0
- package/dist/model_providers/gemini.js.map +1 -1
- package/dist/model_providers/openai.d.ts.map +1 -1
- package/dist/model_providers/openai.js +41 -112
- package/dist/model_providers/openai.js.map +1 -1
- package/dist/model_providers/openai_chat.d.ts.map +1 -1
- package/dist/model_providers/openai_chat.js +55 -24
- package/dist/model_providers/openai_chat.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/types.d.ts +20 -2
- package/dist/types/types.d.ts.map +1 -1
- package/dist/utils/agent.d.ts.map +1 -1
- package/dist/utils/agent.js +4 -6
- package/dist/utils/agent.js.map +1 -1
- package/dist/utils/ensemble_result.d.ts +10 -1
- package/dist/utils/ensemble_result.d.ts.map +1 -1
- package/dist/utils/ensemble_result.js +43 -4
- package/dist/utils/ensemble_result.js.map +1 -1
- package/dist/utils/failure_detection.d.ts +51 -0
- package/dist/utils/failure_detection.d.ts.map +1 -0
- package/dist/utils/failure_detection.js +280 -0
- package/dist/utils/failure_detection.js.map +1 -0
- package/dist/utils/json_schema.d.ts +10 -0
- package/dist/utils/json_schema.d.ts.map +1 -0
- package/dist/utils/json_schema.js +486 -0
- package/dist/utils/json_schema.js.map +1 -0
- package/dist/utils/tool_execution_manager.d.ts +1 -1
- package/dist/utils/tool_execution_manager.d.ts.map +1 -1
- package/dist/utils/tool_execution_manager.js +28 -4
- package/dist/utils/tool_execution_manager.js.map +1 -1
- package/dist/utils/verification.d.ts.map +1 -1
- package/dist/utils/verification.js +26 -13
- package/dist/utils/verification.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ npm run demo
|
|
|
16
16
|
This opens a unified demo interface at http://localhost:3000 with all demos:
|
|
17
17
|
|
|
18
18
|
### Demo Interface
|
|
19
|
+
|
|
19
20
|

|
|
20
21
|
|
|
21
22
|
Navigate to http://localhost:3000 to access all demos through a unified interface.
|
|
@@ -35,12 +36,13 @@ See the [demo README](demo/README.md) for detailed information about each demo.
|
|
|
35
36
|
- 🎯 **Smart Result Processing** - Automatic summarization and truncation for long outputs
|
|
36
37
|
|
|
37
38
|
## Model Updates (Dec 2025)
|
|
39
|
+
|
|
38
40
|
- OpenAI: Added GPT-5.2 (base + chat-latest + pro) and refreshed GPT-5.1/GPT-5/Codex pricing
|
|
39
41
|
- Anthropic: Claude 4.5 (Sonnet/Haiku, incl. 1M context) and Claude Opus 4.1
|
|
40
42
|
- Google: Gemini 3 (Pro/Flash/Ultra) and refreshed Gemini 2.5 pricing incl. image/TTS/native-audio
|
|
41
43
|
- xAI: Grok 4.1 Fast and Grok 4 Fast with tiered pricing; updated Grok 4/3/mini variants plus Grok Imagine image generation/editing support (`grok-imagine-image`, `grok-imagine-image-pro`)
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
\*Codex-Max pricing reflects current published rates and may change if OpenAI updates pricing.
|
|
44
46
|
|
|
45
47
|
## Installation
|
|
46
48
|
|
|
@@ -61,13 +63,13 @@ Available API keys (add only the ones you need):
|
|
|
61
63
|
```bash
|
|
62
64
|
# LLM Providers
|
|
63
65
|
OPENAI_API_KEY=your-openai-key
|
|
64
|
-
ANTHROPIC_API_KEY=your-anthropic-key
|
|
66
|
+
ANTHROPIC_API_KEY=your-anthropic-key
|
|
65
67
|
GOOGLE_API_KEY=your-google-key
|
|
66
68
|
XAI_API_KEY=your-xai-key
|
|
67
69
|
DEEPSEEK_API_KEY=your-deepseek-key
|
|
68
70
|
OPENROUTER_API_KEY=your-openrouter-key
|
|
69
71
|
|
|
70
|
-
# Voice & Audio Providers
|
|
72
|
+
# Voice & Audio Providers
|
|
71
73
|
ELEVENLABS_API_KEY=your-elevenlabs-key
|
|
72
74
|
|
|
73
75
|
# Search Providers
|
|
@@ -81,9 +83,7 @@ BRAVE_API_KEY=your-brave-key
|
|
|
81
83
|
```typescript
|
|
82
84
|
import { ensembleRequest, ensembleResult } from '@just-every/ensemble';
|
|
83
85
|
|
|
84
|
-
const messages = [
|
|
85
|
-
{ type: 'message', role: 'user', content: 'How many of the letter "e" is there in "Ensemble"?' }
|
|
86
|
-
];
|
|
86
|
+
const messages = [{ type: 'message', role: 'user', content: 'How many of the letter "e" is there in "Ensemble"?' }];
|
|
87
87
|
|
|
88
88
|
// Perform initial request
|
|
89
89
|
for await (const event of ensembleRequest(messages)) {
|
|
@@ -104,21 +104,22 @@ const stream = ensembleRequest(messages, validatorAgent);
|
|
|
104
104
|
const result = await ensembleResult(stream);
|
|
105
105
|
console.log('Validation Result:', {
|
|
106
106
|
message: result.message,
|
|
107
|
+
requestStatus: result.requestStatus,
|
|
108
|
+
failure: result.failure,
|
|
107
109
|
cost: result.cost,
|
|
108
110
|
completed: result.completed,
|
|
109
|
-
duration: result.endTime
|
|
110
|
-
? result.endTime.getTime() - result.startTime.getTime()
|
|
111
|
-
: 0,
|
|
111
|
+
duration: result.endTime ? result.endTime.getTime() - result.startTime.getTime() : 0,
|
|
112
112
|
messageIds: Array.from(result.messageIds),
|
|
113
113
|
});
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
## Documentation
|
|
117
117
|
|
|
118
|
+
- [Request Lifecycle & Failures](docs/retry-behavior.md) - Outer request status, retries, timeouts, verification, and failure handling
|
|
118
119
|
- [Tool Execution Guide](docs/tool-execution.md) - Advanced tool calling features
|
|
119
120
|
- [Interactive Demos](demo/) - Web-based demos for core features
|
|
120
121
|
- Generated [API Reference](docs/api) with `npm run docs`
|
|
121
|
-
|
|
122
|
+
|
|
122
123
|
Run `npm run docs` to regenerate the HTML documentation.
|
|
123
124
|
|
|
124
125
|
## Core Concepts
|
|
@@ -130,25 +131,27 @@ Define tools that LLMs can call:
|
|
|
130
131
|
```typescript
|
|
131
132
|
const agent = {
|
|
132
133
|
model: 'o3',
|
|
133
|
-
tools: [
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
134
|
+
tools: [
|
|
135
|
+
{
|
|
136
|
+
definition: {
|
|
137
|
+
type: 'function',
|
|
138
|
+
function: {
|
|
139
|
+
name: 'get_weather',
|
|
140
|
+
description: 'Get weather for a location',
|
|
141
|
+
parameters: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
properties: {
|
|
144
|
+
location: { type: 'string' },
|
|
145
|
+
},
|
|
146
|
+
required: ['location'],
|
|
143
147
|
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
function: async (location: string) => {
|
|
151
|
+
return `Weather in ${location}: Sunny, 72°F`;
|
|
152
|
+
},
|
|
147
153
|
},
|
|
148
|
-
|
|
149
|
-
return `Weather in ${location}: Sunny, 72°F`;
|
|
150
|
-
}
|
|
151
|
-
}]
|
|
154
|
+
],
|
|
152
155
|
};
|
|
153
156
|
```
|
|
154
157
|
|
|
@@ -159,7 +162,8 @@ All providers emit standardized events:
|
|
|
159
162
|
- `message_start` / `message_delta` / `message_complete` - Message streaming
|
|
160
163
|
- `tool_start` / `tool_delta` / `tool_done` - Tool execution
|
|
161
164
|
- `cost_update` - Token usage and cost tracking
|
|
162
|
-
- `
|
|
165
|
+
- `operation_status` - Authoritative outer request lifecycle (`started`, `retrying`, `completed`, `failed`)
|
|
166
|
+
- `error` - Failure details for the current request round (`error`, `code`, `details`, `recoverable`)
|
|
163
167
|
|
|
164
168
|
### Agent Configuration
|
|
165
169
|
|
|
@@ -179,15 +183,19 @@ const agent = {
|
|
|
179
183
|
```
|
|
180
184
|
|
|
181
185
|
Key configuration options:
|
|
186
|
+
|
|
182
187
|
- `maxToolCalls` - Limits the total number of tool calls across all rounds
|
|
183
188
|
- `maxToolCallRoundsPerTurn` - Limits sequential rounds where each round can have multiple parallel tool calls
|
|
184
189
|
- `modelSettings` - Provider-specific parameters like temperature, max_tokens, etc.
|
|
190
|
+
- `modelSettings.timeout_ms` - Whole outer-request timeout budget shared across retries and tool completion
|
|
191
|
+
- `retryOptions` - Outer retry/backoff policy for recoverable request failures
|
|
185
192
|
|
|
186
193
|
### Multimodal Input (Images)
|
|
187
194
|
|
|
188
195
|
For multimodal models, pass content as an array of typed parts. In addition to `input_text` and `input_image`, Ensemble now accepts a simpler `image` part that can take base64 data or a URL.
|
|
189
196
|
|
|
190
197
|
Supported image fields:
|
|
198
|
+
|
|
191
199
|
- `type: 'image'`
|
|
192
200
|
- `data`: base64 string **or** full `data:<mime>;base64,...` URL
|
|
193
201
|
- `url`: http(s) URL
|
|
@@ -199,27 +207,29 @@ Supported image fields:
|
|
|
199
207
|
import { ensembleRequest } from '@just-every/ensemble';
|
|
200
208
|
|
|
201
209
|
const messages = [
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
210
|
+
{
|
|
211
|
+
type: 'message',
|
|
212
|
+
role: 'user',
|
|
213
|
+
content: [
|
|
214
|
+
{ type: 'input_text', text: 'Describe this image.' },
|
|
215
|
+
{ type: 'image', data: myPngBase64, mime_type: 'image/png' },
|
|
216
|
+
// or: { type: 'image', url: 'https://example.com/cat.png' }
|
|
217
|
+
],
|
|
218
|
+
},
|
|
211
219
|
];
|
|
212
220
|
|
|
213
221
|
for await (const event of ensembleRequest(messages, { model: 'gemini-3-flash-preview' })) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
222
|
+
if (event.type === 'message_complete' && 'content' in event) {
|
|
223
|
+
console.log(event.content);
|
|
224
|
+
}
|
|
217
225
|
}
|
|
218
226
|
```
|
|
219
227
|
|
|
220
228
|
### Structured JSON Output
|
|
221
229
|
|
|
222
|
-
Use `modelSettings.json_schema` to request a JSON-only response.
|
|
230
|
+
Use `modelSettings.json_schema` to request a JSON-only response. Ensemble treats this as the authoritative structured-output contract. When `strict: true` is enabled, the final response is validated by the outer request lifecycle and invalid JSON/schema violations fail the request instead of silently completing.
|
|
231
|
+
|
|
232
|
+
Prefer `modelSettings.json_schema`. The older `jsonSchema` agent property is still accepted as a compatibility alias and is mapped onto `modelSettings.json_schema`.
|
|
223
233
|
|
|
224
234
|
The example below combines **image input** with **JSON output**:
|
|
225
235
|
|
|
@@ -227,40 +237,74 @@ The example below combines **image input** with **JSON output**:
|
|
|
227
237
|
import { ensembleRequest, ensembleResult } from '@just-every/ensemble';
|
|
228
238
|
|
|
229
239
|
const agent = {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
240
|
+
model: 'gemini-3-flash-preview',
|
|
241
|
+
modelSettings: {
|
|
242
|
+
temperature: 0.2,
|
|
243
|
+
json_schema: {
|
|
244
|
+
name: 'image_analysis',
|
|
245
|
+
type: 'json_schema',
|
|
246
|
+
schema: {
|
|
247
|
+
type: 'object',
|
|
248
|
+
properties: {
|
|
249
|
+
dominant_color: { type: 'string' },
|
|
250
|
+
confidence: { type: 'number' },
|
|
251
|
+
},
|
|
252
|
+
required: ['dominant_color', 'confidence'],
|
|
253
|
+
},
|
|
241
254
|
},
|
|
242
|
-
required: ['dominant_color', 'confidence'],
|
|
243
|
-
},
|
|
244
255
|
},
|
|
245
|
-
},
|
|
246
256
|
};
|
|
247
257
|
|
|
248
258
|
const messages = [
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
{
|
|
260
|
+
type: 'message',
|
|
261
|
+
role: 'user',
|
|
262
|
+
content: [
|
|
263
|
+
{ type: 'input_text', text: 'Analyze this image and return JSON.' },
|
|
264
|
+
{ type: 'image', data: myPngBase64, mime_type: 'image/png' },
|
|
265
|
+
],
|
|
266
|
+
},
|
|
257
267
|
];
|
|
258
268
|
|
|
259
269
|
const result = await ensembleResult(ensembleRequest(messages, agent));
|
|
270
|
+
if (!result.completed) {
|
|
271
|
+
throw new Error(result.failure?.error || 'Request failed');
|
|
272
|
+
}
|
|
260
273
|
const parsed = JSON.parse(result.message);
|
|
261
274
|
console.log(parsed.dominant_color, parsed.confidence);
|
|
262
275
|
```
|
|
263
276
|
|
|
277
|
+
`ensembleResult(...)` also exposes `requestStatus` and `failure`, so callers can distinguish the final outer request outcome from earlier recoverable errors in the stream.
|
|
278
|
+
|
|
279
|
+
### Request Lifecycle
|
|
280
|
+
|
|
281
|
+
`operation_status` is the authoritative outer lifecycle for one `ensembleRequest(...)` call:
|
|
282
|
+
|
|
283
|
+
- `started` is emitted once when the outer request begins
|
|
284
|
+
- `retrying` is emitted after a recoverable failure and before the next attempt
|
|
285
|
+
- `completed` is the only authoritative success outcome
|
|
286
|
+
- `failed` is the only authoritative terminal failure outcome
|
|
287
|
+
|
|
288
|
+
Recoverable `error` events can appear before a successful `completed` status. Callers that want the final outcome should prefer `operation_status` or `ensembleResult(...)` over treating the first `error` event as terminal.
|
|
289
|
+
|
|
290
|
+
```ts
|
|
291
|
+
const stream = ensembleRequest(messages, {
|
|
292
|
+
model: 'claude-4-sonnet',
|
|
293
|
+
modelSettings: { timeout_ms: 15_000 },
|
|
294
|
+
retryOptions: { maxRetries: 1 },
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
for await (const event of stream) {
|
|
298
|
+
if (event.type === 'operation_status') {
|
|
299
|
+
console.log(event.status, event.reason, event.attempt, event.max_attempts);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (event.type === 'error') {
|
|
303
|
+
console.log(event.recoverable ? 'recoverable' : 'terminal', event.code, event.error);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
264
308
|
### Advanced Features
|
|
265
309
|
|
|
266
310
|
- **Parallel Tool Execution** - Tools run concurrently by default within each round
|
|
@@ -278,21 +322,25 @@ import { ensembleVoice, ensembleVoice } from '@just-every/ensemble';
|
|
|
278
322
|
|
|
279
323
|
// Simple voice generation
|
|
280
324
|
const audioData = await ensembleVoice('Hello, world!', {
|
|
281
|
-
model: 'tts-1' // or 'gemini-2.5-flash-preview-tts'
|
|
325
|
+
model: 'tts-1', // or 'gemini-2.5-flash-preview-tts'
|
|
282
326
|
});
|
|
283
327
|
|
|
284
328
|
// Voice generation with options
|
|
285
|
-
const audioData = await ensembleVoice(
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
329
|
+
const audioData = await ensembleVoice(
|
|
330
|
+
'Welcome to our service',
|
|
331
|
+
{
|
|
332
|
+
model: 'tts-1-hd',
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
voice: 'nova', // Voice selection
|
|
336
|
+
speed: 1.2, // Speech speed (0.25-4.0)
|
|
337
|
+
response_format: 'mp3', // Audio format
|
|
338
|
+
}
|
|
339
|
+
);
|
|
292
340
|
|
|
293
341
|
// Streaming voice generation
|
|
294
342
|
for await (const event of ensembleVoice('Long text...', {
|
|
295
|
-
model: 'gemini-2.5-pro-preview-tts'
|
|
343
|
+
model: 'gemini-2.5-pro-preview-tts',
|
|
296
344
|
})) {
|
|
297
345
|
if (event.type === 'audio_stream') {
|
|
298
346
|
// Process audio chunk
|
|
@@ -302,6 +350,7 @@ for await (const event of ensembleVoice('Long text...', {
|
|
|
302
350
|
```
|
|
303
351
|
|
|
304
352
|
**Supported Voice Models:**
|
|
353
|
+
|
|
305
354
|
- OpenAI: `tts-1`, `tts-1-hd`
|
|
306
355
|
- Google Gemini: `gemini-2.5-flash-preview-tts`, `gemini-2.5-pro-preview-tts`
|
|
307
356
|
|
|
@@ -312,30 +361,35 @@ Use OpenAI GPT-Image-1 (or the new cost-efficient GPT-Image-1 Mini) or Google Ge
|
|
|
312
361
|
```ts
|
|
313
362
|
import { ensembleImage } from '@just-every/ensemble';
|
|
314
363
|
|
|
315
|
-
const images = await ensembleImage(
|
|
364
|
+
const images = await ensembleImage(
|
|
365
|
+
'A serene lake at dawn',
|
|
366
|
+
{ model: 'gemini-2.5-flash-image-preview' },
|
|
367
|
+
{ size: 'portrait' }
|
|
368
|
+
);
|
|
316
369
|
|
|
317
370
|
// Gemini 3.1 Flash Image: grounded generation + thinking controls + metadata callback
|
|
318
371
|
const grounded = await ensembleImage(
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
372
|
+
'A detailed painting of a Timareta butterfly resting on a flower',
|
|
373
|
+
{ model: 'gemini-3.1-flash-image-preview' },
|
|
374
|
+
{
|
|
375
|
+
size: '16:9',
|
|
376
|
+
quality: 'high', // 4K
|
|
377
|
+
grounding: {
|
|
378
|
+
web_search: true,
|
|
379
|
+
image_search: true,
|
|
380
|
+
},
|
|
381
|
+
thinking: {
|
|
382
|
+
level: 'high',
|
|
383
|
+
include_thoughts: true,
|
|
384
|
+
},
|
|
385
|
+
on_metadata: metadata => {
|
|
386
|
+
// metadata.citations includes containing-page URLs for attribution compliance
|
|
387
|
+
console.log(metadata.citations);
|
|
388
|
+
},
|
|
389
|
+
}
|
|
337
390
|
);
|
|
338
391
|
```
|
|
392
|
+
|
|
339
393
|
- ElevenLabs: `eleven_multilingual_v2`, `eleven_turbo_v2_5`
|
|
340
394
|
|
|
341
395
|
## Development
|
|
@@ -360,12 +414,14 @@ npm run lint
|
|
|
360
414
|
Additional image providers
|
|
361
415
|
|
|
362
416
|
New providers added
|
|
417
|
+
|
|
363
418
|
- Fireworks AI (FLUX family: Kontext/Pro/Schnell) – async APIs with result polling. Docs: Fireworks Image API.
|
|
364
419
|
- Stability AI (Stable Image Ultra/SDXL) – REST v2beta endpoints supporting text-to-image and image-to-image.
|
|
365
420
|
- Runway Gen-4 Image – via FAL.ai.
|
|
366
421
|
- Recraft v3 – via FAL.ai (supports text-to-vector and vector-style outputs).
|
|
367
422
|
|
|
368
423
|
Environment
|
|
424
|
+
|
|
369
425
|
```
|
|
370
426
|
FIREWORKS_API_KEY=your_key
|
|
371
427
|
STABILITY_API_KEY=your_key
|
|
@@ -373,6 +429,7 @@ FAL_KEY=your_key
|
|
|
373
429
|
```
|
|
374
430
|
|
|
375
431
|
Fallbacks
|
|
432
|
+
|
|
376
433
|
- If Fireworks returns 401/403 or is not configured, requests for Flux-family models automatically fall back to FAL.ai equivalents when `FAL_KEY` is set.
|
|
377
434
|
|
|
378
435
|
- Luma Photon (official): set `LUMA_API_KEY` and use `luma-photon-1` or `luma-photon-flash-1`.
|
|
@@ -380,6 +437,7 @@ Fallbacks
|
|
|
380
437
|
- Midjourney v7 (3rd-party): set `MIDJOURNEY_API_KEY` (or `KIE_API_KEY`) and optional `MJ_API_BASE`; use `midjourney-v7`.
|
|
381
438
|
|
|
382
439
|
Notes
|
|
440
|
+
|
|
383
441
|
- Gemini 3.1 Flash Image supports 0.5K/1K/2K/4K tiers, explicit aspect ratios, Google Image Search grounding, and thinking controls.
|
|
384
442
|
- Gemini 3 Pro Image supports explicit 1K/2K/4K resolution presets mapped to official aspect-ratio tables.
|
|
385
443
|
- Luma Photon and Ideogram return URLs; we pass them through without altering pixels.
|
|
@@ -406,16 +464,19 @@ Contributions are welcome! Please:
|
|
|
406
464
|
## Troubleshooting
|
|
407
465
|
|
|
408
466
|
### Provider Issues
|
|
467
|
+
|
|
409
468
|
- Ensure API keys are set correctly
|
|
410
469
|
- Check rate limits for your provider
|
|
411
470
|
- Verify model names match provider expectations
|
|
412
471
|
|
|
413
472
|
### Tool Calling
|
|
473
|
+
|
|
414
474
|
- Tools must follow the OpenAI function schema
|
|
415
475
|
- Ensure tool functions are async
|
|
416
476
|
- Check timeout settings for long-running tools
|
|
417
477
|
|
|
418
478
|
### Streaming Issues
|
|
479
|
+
|
|
419
480
|
- Verify network connectivity
|
|
420
481
|
- Check for provider-specific errors in events
|
|
421
482
|
- Enable debug logging with `DEBUG=ensemble:*`
|