@falai/agent 0.3.30 → 0.4.0

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.
Files changed (72) hide show
  1. package/dist/cjs/core/Agent.d.ts +1 -0
  2. package/dist/cjs/core/Agent.d.ts.map +1 -1
  3. package/dist/cjs/core/Agent.js +45 -2
  4. package/dist/cjs/core/Agent.js.map +1 -1
  5. package/dist/cjs/core/ConditionEvaluator.d.ts +72 -0
  6. package/dist/cjs/core/ConditionEvaluator.d.ts.map +1 -0
  7. package/dist/cjs/core/ConditionEvaluator.js +272 -0
  8. package/dist/cjs/core/ConditionEvaluator.js.map +1 -0
  9. package/dist/cjs/core/PreparationEngine.d.ts +105 -0
  10. package/dist/cjs/core/PreparationEngine.d.ts.map +1 -0
  11. package/dist/cjs/core/PreparationEngine.js +320 -0
  12. package/dist/cjs/core/PreparationEngine.js.map +1 -0
  13. package/dist/cjs/core/Route.d.ts +6 -0
  14. package/dist/cjs/core/Route.d.ts.map +1 -1
  15. package/dist/cjs/core/Route.js +9 -0
  16. package/dist/cjs/core/Route.js.map +1 -1
  17. package/dist/cjs/providers/AnthropicProvider.d.ts +3 -3
  18. package/dist/cjs/providers/AnthropicProvider.d.ts.map +1 -1
  19. package/dist/cjs/providers/AnthropicProvider.js.map +1 -1
  20. package/dist/cjs/providers/GeminiProvider.d.ts +3 -3
  21. package/dist/cjs/providers/GeminiProvider.d.ts.map +1 -1
  22. package/dist/cjs/providers/GeminiProvider.js +43 -18
  23. package/dist/cjs/providers/GeminiProvider.js.map +1 -1
  24. package/dist/cjs/providers/OpenAIProvider.d.ts +3 -3
  25. package/dist/cjs/providers/OpenAIProvider.d.ts.map +1 -1
  26. package/dist/cjs/providers/OpenAIProvider.js.map +1 -1
  27. package/dist/cjs/providers/OpenRouterProvider.d.ts +3 -3
  28. package/dist/cjs/providers/OpenRouterProvider.d.ts.map +1 -1
  29. package/dist/cjs/providers/OpenRouterProvider.js.map +1 -1
  30. package/dist/cjs/types/ai.d.ts +6 -6
  31. package/dist/cjs/types/ai.d.ts.map +1 -1
  32. package/dist/core/Agent.d.ts +1 -0
  33. package/dist/core/Agent.d.ts.map +1 -1
  34. package/dist/core/Agent.js +45 -2
  35. package/dist/core/Agent.js.map +1 -1
  36. package/dist/core/ConditionEvaluator.d.ts +72 -0
  37. package/dist/core/ConditionEvaluator.d.ts.map +1 -0
  38. package/dist/core/ConditionEvaluator.js +268 -0
  39. package/dist/core/ConditionEvaluator.js.map +1 -0
  40. package/dist/core/PreparationEngine.d.ts +105 -0
  41. package/dist/core/PreparationEngine.d.ts.map +1 -0
  42. package/dist/core/PreparationEngine.js +316 -0
  43. package/dist/core/PreparationEngine.js.map +1 -0
  44. package/dist/core/Route.d.ts +6 -0
  45. package/dist/core/Route.d.ts.map +1 -1
  46. package/dist/core/Route.js +9 -0
  47. package/dist/core/Route.js.map +1 -1
  48. package/dist/providers/AnthropicProvider.d.ts +3 -3
  49. package/dist/providers/AnthropicProvider.d.ts.map +1 -1
  50. package/dist/providers/AnthropicProvider.js.map +1 -1
  51. package/dist/providers/GeminiProvider.d.ts +3 -3
  52. package/dist/providers/GeminiProvider.d.ts.map +1 -1
  53. package/dist/providers/GeminiProvider.js +43 -18
  54. package/dist/providers/GeminiProvider.js.map +1 -1
  55. package/dist/providers/OpenAIProvider.d.ts +3 -3
  56. package/dist/providers/OpenAIProvider.d.ts.map +1 -1
  57. package/dist/providers/OpenAIProvider.js.map +1 -1
  58. package/dist/providers/OpenRouterProvider.d.ts +3 -3
  59. package/dist/providers/OpenRouterProvider.d.ts.map +1 -1
  60. package/dist/providers/OpenRouterProvider.js.map +1 -1
  61. package/dist/types/ai.d.ts +6 -6
  62. package/dist/types/ai.d.ts.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/Agent.ts +57 -2
  65. package/src/core/ConditionEvaluator.ts +381 -0
  66. package/src/core/PreparationEngine.ts +500 -0
  67. package/src/core/Route.ts +10 -0
  68. package/src/providers/AnthropicProvider.ts +51 -21
  69. package/src/providers/GeminiProvider.ts +86 -40
  70. package/src/providers/OpenAIProvider.ts +48 -21
  71. package/src/providers/OpenRouterProvider.ts +36 -18
  72. package/src/types/ai.ts +13 -8
@@ -48,39 +48,67 @@ export interface GeminiProviderOptions {
48
48
  */
49
49
  interface ErrorWithStatus {
50
50
  status?: number;
51
- code?: number;
51
+ code?: string;
52
52
  message?: string;
53
+ type?: string;
54
+ }
55
+
56
+ /**
57
+ * Type guard to check if error is ErrorWithStatus
58
+ */
59
+ function isErrorWithStatus(error: unknown): error is ErrorWithStatus {
60
+ return (
61
+ typeof error === "object" &&
62
+ error !== null &&
63
+ ("status" in error || "code" in error || "message" in error)
64
+ );
65
+ }
66
+
67
+ /**
68
+ * Safely extract error message
69
+ */
70
+ function getErrorMessage(error: unknown): string {
71
+ if (error instanceof Error) {
72
+ return error.message;
73
+ }
74
+ if (isErrorWithStatus(error) && error.message) {
75
+ return error.message;
76
+ }
77
+ return String(error);
53
78
  }
54
79
 
55
80
  /**
56
81
  * Determines if an error should trigger backup model usage
57
82
  */
58
83
  const shouldUseBackupModel = (error: unknown): boolean => {
59
- const err = error as ErrorWithStatus;
84
+ if (!isErrorWithStatus(error)) {
85
+ return false;
86
+ }
60
87
 
61
- if (err?.status === 500 || err?.code === 500) {
88
+ // Server errors
89
+ if (error.status === 500 || error.status === 503) {
62
90
  return true;
63
91
  }
64
92
 
65
- const message = err?.message ?? String(error);
66
- if (
67
- message.includes("internal error") ||
68
- message.includes("Internal error") ||
69
- message.includes("INTERNAL")
70
- ) {
93
+ // Rate limiting
94
+ if (error.status === 429) {
71
95
  return true;
72
96
  }
73
97
 
74
- if (
75
- err?.status === 429 ||
76
- err?.code === 429 ||
77
- err?.status === 503 ||
78
- err?.code === 503
79
- ) {
98
+ // Model overloaded or unavailable
99
+ if (error.code === "overloaded") {
80
100
  return true;
81
101
  }
82
102
 
83
- if (message.includes("unavailable") || message.includes("not available")) {
103
+ const message = getErrorMessage(error);
104
+ if (
105
+ message.includes("overloaded") ||
106
+ message.includes("unavailable") ||
107
+ message.includes("not available") ||
108
+ message.includes("internal error") ||
109
+ message.includes("Internal error") ||
110
+ message.includes("INTERNAL")
111
+ ) {
84
112
  return true;
85
113
  }
86
114
 
@@ -119,26 +147,35 @@ export class GeminiProvider implements AiProvider {
119
147
  };
120
148
  }
121
149
 
122
- async generateMessage<TContext = unknown>(
150
+ async generateMessage<
151
+ TContext = unknown,
152
+ TStructured = AgentStructuredResponse
153
+ >(
123
154
  input: GenerateMessageInput<TContext>
124
- ): Promise<GenerateMessageOutput> {
125
- return this.generateWithBackup(input);
155
+ ): Promise<GenerateMessageOutput<TStructured>> {
156
+ return this.generateWithBackup<TContext, TStructured>(input);
126
157
  }
127
158
 
128
- async *generateMessageStream<TContext = unknown>(
159
+ async *generateMessageStream<
160
+ TContext = unknown,
161
+ TStructured = AgentStructuredResponse
162
+ >(
129
163
  input: GenerateMessageInput<TContext>
130
- ): AsyncGenerator<GenerateMessageStreamChunk> {
131
- yield* this.generateStreamWithBackup(input);
164
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
165
+ yield* this.generateStreamWithBackup<TContext, TStructured>(input);
132
166
  }
133
167
 
134
- private async generateWithBackup<TContext = unknown>(
168
+ private async generateWithBackup<
169
+ TContext = unknown,
170
+ TStructured = AgentStructuredResponse
171
+ >(
135
172
  input: GenerateMessageInput<TContext>
136
- ): Promise<GenerateMessageOutput> {
173
+ ): Promise<GenerateMessageOutput<TStructured>> {
137
174
  // Try primary model first
138
175
  try {
139
176
  return await this.generateWithModel(this.primaryModel, input);
140
177
  } catch (primaryError: unknown) {
141
- const primaryErrMsg = String(primaryError);
178
+ const primaryErrMsg = getErrorMessage(primaryError);
142
179
  console.warn(
143
180
  `[GEMINI] Primary model ${this.primaryModel} failed: ${primaryErrMsg}`
144
181
  );
@@ -162,9 +199,9 @@ export class GeminiProvider implements AiProvider {
162
199
  try {
163
200
  const result = await this.generateWithModel(backupModel, input);
164
201
  console.log(`[GEMINI] Backup model ${backupModel} succeeded`);
165
- return result;
202
+ return result as GenerateMessageOutput<TStructured>;
166
203
  } catch (backupError: unknown) {
167
- const backupErrMsg = String(backupError);
204
+ const backupErrMsg = getErrorMessage(backupError);
168
205
  console.warn(
169
206
  `[GEMINI] Backup model ${backupModel} failed: ${backupErrMsg}`
170
207
  );
@@ -182,7 +219,7 @@ export class GeminiProvider implements AiProvider {
182
219
  }
183
220
  }
184
221
 
185
- const lastBackupErrMsg = String(lastBackupError);
222
+ const lastBackupErrMsg = getErrorMessage(lastBackupError);
186
223
  console.error(
187
224
  `[GEMINI] All models failed. Primary: ${primaryErrMsg}, Last backup: ${lastBackupErrMsg}`
188
225
  );
@@ -190,10 +227,13 @@ export class GeminiProvider implements AiProvider {
190
227
  }
191
228
  }
192
229
 
193
- private async generateWithModel<TContext = unknown>(
230
+ private async generateWithModel<
231
+ TContext = unknown,
232
+ TStructured = AgentStructuredResponse
233
+ >(
194
234
  model: string,
195
235
  input: GenerateMessageInput<TContext>
196
- ): Promise<GenerateMessageOutput> {
236
+ ): Promise<GenerateMessageOutput<TStructured>> {
197
237
  const operation = async (): Promise<GenerateMessageOutput> => {
198
238
  // Enable JSON mode if requested
199
239
  const configOverride: Partial<GenerateContentConfig> = { ...this.config };
@@ -214,7 +254,7 @@ export class GeminiProvider implements AiProvider {
214
254
  }
215
255
 
216
256
  // Parse JSON response if JSON mode was enabled
217
- let structured;
257
+ let structured: AgentStructuredResponse | undefined;
218
258
  if (input.parameters?.jsonMode) {
219
259
  try {
220
260
  structured = JSON.parse(message) as AgentStructuredResponse;
@@ -241,17 +281,20 @@ export class GeminiProvider implements AiProvider {
241
281
  this.retryConfig.timeout,
242
282
  this.retryConfig.retries,
243
283
  `Gemini ${model}`
244
- );
284
+ ) as Promise<GenerateMessageOutput<TStructured>>;
245
285
  }
246
286
 
247
- private async *generateStreamWithBackup<TContext = unknown>(
287
+ private async *generateStreamWithBackup<
288
+ TContext = unknown,
289
+ TStructured = AgentStructuredResponse
290
+ >(
248
291
  input: GenerateMessageInput<TContext>
249
- ): AsyncGenerator<GenerateMessageStreamChunk> {
292
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
250
293
  // Try primary model first
251
294
  try {
252
295
  yield* this.generateStreamWithModel(this.primaryModel, input);
253
296
  } catch (primaryError: unknown) {
254
- const primaryErrMsg = String(primaryError);
297
+ const primaryErrMsg = getErrorMessage(primaryError);
255
298
  console.warn(
256
299
  `[GEMINI] Primary model ${this.primaryModel} failed: ${primaryErrMsg}`
257
300
  );
@@ -277,7 +320,7 @@ export class GeminiProvider implements AiProvider {
277
320
  console.log(`[GEMINI] Backup model ${backupModel} succeeded`);
278
321
  return;
279
322
  } catch (backupError: unknown) {
280
- const backupErrMsg = String(backupError);
323
+ const backupErrMsg = getErrorMessage(backupError);
281
324
  console.warn(
282
325
  `[GEMINI] Backup model ${backupModel} failed: ${backupErrMsg}`
283
326
  );
@@ -295,7 +338,7 @@ export class GeminiProvider implements AiProvider {
295
338
  }
296
339
  }
297
340
 
298
- const lastBackupErrMsg = String(lastBackupError);
341
+ const lastBackupErrMsg = getErrorMessage(lastBackupError);
299
342
  console.error(
300
343
  `[GEMINI] All models failed. Primary: ${primaryErrMsg}, Last backup: ${lastBackupErrMsg}`
301
344
  );
@@ -303,10 +346,13 @@ export class GeminiProvider implements AiProvider {
303
346
  }
304
347
  }
305
348
 
306
- private async *generateStreamWithModel<TContext = unknown>(
349
+ private async *generateStreamWithModel<
350
+ TContext = unknown,
351
+ TStructured = AgentStructuredResponse
352
+ >(
307
353
  model: string,
308
354
  input: GenerateMessageInput<TContext>
309
- ): AsyncGenerator<GenerateMessageStreamChunk> {
355
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
310
356
  // Enable JSON mode if requested
311
357
  const configOverride: Partial<GenerateContentConfig> = { ...this.config };
312
358
  if (input.parameters?.jsonMode) {
@@ -368,7 +414,7 @@ export class GeminiProvider implements AiProvider {
368
414
  promptTokens: promptTokenCount,
369
415
  completionTokens: candidatesTokenCount,
370
416
  },
371
- structured,
417
+ structured: structured as TStructured | undefined,
372
418
  };
373
419
  }
374
420
  }
@@ -159,24 +159,36 @@ export class OpenAIProvider implements AiProvider {
159
159
  };
160
160
  }
161
161
 
162
- async generateMessage<TContext = unknown>(
162
+ async generateMessage<
163
+ TContext = unknown,
164
+ TStructured = AgentStructuredResponse
165
+ >(
163
166
  input: GenerateMessageInput<TContext>
164
- ): Promise<GenerateMessageOutput> {
165
- return this.generateWithBackup(input);
167
+ ): Promise<GenerateMessageOutput<TStructured>> {
168
+ return this.generateWithBackup<TContext, TStructured>(input);
166
169
  }
167
170
 
168
- async *generateMessageStream<TContext = unknown>(
171
+ async *generateMessageStream<
172
+ TContext = unknown,
173
+ TStructured = AgentStructuredResponse
174
+ >(
169
175
  input: GenerateMessageInput<TContext>
170
- ): AsyncGenerator<GenerateMessageStreamChunk> {
171
- yield* this.generateStreamWithBackup(input);
176
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
177
+ yield* this.generateStreamWithBackup<TContext, TStructured>(input);
172
178
  }
173
179
 
174
- private async generateWithBackup<TContext = unknown>(
180
+ private async generateWithBackup<
181
+ TContext = unknown,
182
+ TStructured = AgentStructuredResponse
183
+ >(
175
184
  input: GenerateMessageInput<TContext>
176
- ): Promise<GenerateMessageOutput> {
185
+ ): Promise<GenerateMessageOutput<TStructured>> {
177
186
  // Try primary model first
178
187
  try {
179
- return await this.generateWithModel(this.primaryModel, input);
188
+ return await this.generateWithModel<TContext, TStructured>(
189
+ this.primaryModel,
190
+ input
191
+ );
180
192
  } catch (primaryError: unknown) {
181
193
  const primaryErrMsg = getErrorMessage(primaryError);
182
194
  console.warn(
@@ -202,7 +214,7 @@ export class OpenAIProvider implements AiProvider {
202
214
  try {
203
215
  const result = await this.generateWithModel(backupModel, input);
204
216
  console.log(`[OPENAI] Backup model ${backupModel} succeeded`);
205
- return result;
217
+ return result as GenerateMessageOutput<TStructured>;
206
218
  } catch (backupError: unknown) {
207
219
  const backupErrMsg = getErrorMessage(backupError);
208
220
  console.warn(
@@ -230,10 +242,13 @@ export class OpenAIProvider implements AiProvider {
230
242
  }
231
243
  }
232
244
 
233
- private async generateWithModel<TContext = unknown>(
245
+ private async generateWithModel<
246
+ TContext = unknown,
247
+ TStructured = AgentStructuredResponse
248
+ >(
234
249
  model: string,
235
250
  input: GenerateMessageInput<TContext>
236
- ): Promise<GenerateMessageOutput> {
251
+ ): Promise<GenerateMessageOutput<TStructured>> {
237
252
  const operation = async (): Promise<GenerateMessageOutput> => {
238
253
  const params: ChatCompletionCreateParamsNonStreaming = {
239
254
  model,
@@ -358,15 +373,21 @@ export class OpenAIProvider implements AiProvider {
358
373
  this.retryConfig.timeout,
359
374
  this.retryConfig.retries,
360
375
  `OpenAI ${model}`
361
- );
376
+ ) as Promise<GenerateMessageOutput<TStructured>>;
362
377
  }
363
378
 
364
- private async *generateStreamWithBackup<TContext = unknown>(
379
+ private async *generateStreamWithBackup<
380
+ TContext = unknown,
381
+ TStructured = AgentStructuredResponse
382
+ >(
365
383
  input: GenerateMessageInput<TContext>
366
- ): AsyncGenerator<GenerateMessageStreamChunk> {
384
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
367
385
  // Try primary model first
368
386
  try {
369
- yield* this.generateStreamWithModel(this.primaryModel, input);
387
+ yield* this.generateStreamWithModel<TContext, TStructured>(
388
+ this.primaryModel,
389
+ input
390
+ );
370
391
  } catch (primaryError: unknown) {
371
392
  const primaryErrMsg = getErrorMessage(primaryError);
372
393
  console.warn(
@@ -390,7 +411,10 @@ export class OpenAIProvider implements AiProvider {
390
411
  );
391
412
 
392
413
  try {
393
- yield* this.generateStreamWithModel(backupModel, input);
414
+ yield* this.generateStreamWithModel<TContext, TStructured>(
415
+ backupModel,
416
+ input
417
+ );
394
418
  console.log(`[OPENAI] Backup model ${backupModel} succeeded`);
395
419
  return;
396
420
  } catch (backupError: unknown) {
@@ -420,10 +444,13 @@ export class OpenAIProvider implements AiProvider {
420
444
  }
421
445
  }
422
446
 
423
- private async *generateStreamWithModel<TContext = unknown>(
447
+ private async *generateStreamWithModel<
448
+ TContext = unknown,
449
+ TStructured = AgentStructuredResponse
450
+ >(
424
451
  model: string,
425
452
  input: GenerateMessageInput<TContext>
426
- ): AsyncGenerator<GenerateMessageStreamChunk> {
453
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
427
454
  const params = {
428
455
  ...this.config,
429
456
  model,
@@ -483,10 +510,10 @@ export class OpenAIProvider implements AiProvider {
483
510
  }
484
511
 
485
512
  // Parse JSON response if JSON mode was enabled
486
- let structured: AgentStructuredResponse | undefined;
513
+ let structured: TStructured | undefined;
487
514
  if (input.parameters?.jsonMode && accumulated) {
488
515
  try {
489
- structured = JSON.parse(accumulated) as AgentStructuredResponse;
516
+ structured = JSON.parse(accumulated) as TStructured;
490
517
  } catch (error) {
491
518
  console.warn(
492
519
  "[OPENAI] Failed to parse JSON response in stream:",
@@ -169,21 +169,30 @@ export class OpenRouterProvider implements AiProvider {
169
169
  };
170
170
  }
171
171
 
172
- async generateMessage<TContext = unknown>(
172
+ async generateMessage<
173
+ TContext = unknown,
174
+ TStructured = AgentStructuredResponse
175
+ >(
173
176
  input: GenerateMessageInput<TContext>
174
- ): Promise<GenerateMessageOutput> {
175
- return this.generateWithBackup(input);
177
+ ): Promise<GenerateMessageOutput<TStructured>> {
178
+ return this.generateWithBackup<TContext, TStructured>(input);
176
179
  }
177
180
 
178
- async *generateMessageStream<TContext = unknown>(
181
+ async *generateMessageStream<
182
+ TContext = unknown,
183
+ TStructured = AgentStructuredResponse
184
+ >(
179
185
  input: GenerateMessageInput<TContext>
180
- ): AsyncGenerator<GenerateMessageStreamChunk> {
181
- yield* this.generateStreamWithBackup(input);
186
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
187
+ yield* this.generateStreamWithBackup<TContext, TStructured>(input);
182
188
  }
183
189
 
184
- private async generateWithBackup<TContext = unknown>(
190
+ private async generateWithBackup<
191
+ TContext = unknown,
192
+ TStructured = AgentStructuredResponse
193
+ >(
185
194
  input: GenerateMessageInput<TContext>
186
- ): Promise<GenerateMessageOutput> {
195
+ ): Promise<GenerateMessageOutput<TStructured>> {
187
196
  // Try primary model first
188
197
  try {
189
198
  return await this.generateWithModel(this.primaryModel, input);
@@ -212,7 +221,7 @@ export class OpenRouterProvider implements AiProvider {
212
221
  try {
213
222
  const result = await this.generateWithModel(backupModel, input);
214
223
  console.log(`[OPENROUTER] Backup model ${backupModel} succeeded`);
215
- return result;
224
+ return result as GenerateMessageOutput<TStructured>;
216
225
  } catch (backupError: unknown) {
217
226
  const backupErrMsg = getErrorMessage(backupError);
218
227
  console.warn(
@@ -240,10 +249,13 @@ export class OpenRouterProvider implements AiProvider {
240
249
  }
241
250
  }
242
251
 
243
- private async generateWithModel<TContext = unknown>(
252
+ private async generateWithModel<
253
+ TContext = unknown,
254
+ TStructured = AgentStructuredResponse
255
+ >(
244
256
  model: string,
245
257
  input: GenerateMessageInput<TContext>
246
- ): Promise<GenerateMessageOutput> {
258
+ ): Promise<GenerateMessageOutput<TStructured>> {
247
259
  const operation = async (): Promise<GenerateMessageOutput> => {
248
260
  const params: ChatCompletionCreateParamsNonStreaming = {
249
261
  model,
@@ -368,12 +380,15 @@ export class OpenRouterProvider implements AiProvider {
368
380
  this.retryConfig.timeout,
369
381
  this.retryConfig.retries,
370
382
  `OpenRouter ${model}`
371
- );
383
+ ) as Promise<GenerateMessageOutput<TStructured>>;
372
384
  }
373
385
 
374
- private async *generateStreamWithBackup<TContext = unknown>(
386
+ private async *generateStreamWithBackup<
387
+ TContext = unknown,
388
+ TStructured = AgentStructuredResponse
389
+ >(
375
390
  input: GenerateMessageInput<TContext>
376
- ): AsyncGenerator<GenerateMessageStreamChunk> {
391
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
377
392
  // Try primary model first
378
393
  try {
379
394
  yield* this.generateStreamWithModel(this.primaryModel, input);
@@ -430,10 +445,13 @@ export class OpenRouterProvider implements AiProvider {
430
445
  }
431
446
  }
432
447
 
433
- private async *generateStreamWithModel<TContext = unknown>(
448
+ private async *generateStreamWithModel<
449
+ TContext = unknown,
450
+ TStructured = AgentStructuredResponse
451
+ >(
434
452
  model: string,
435
453
  input: GenerateMessageInput<TContext>
436
- ): AsyncGenerator<GenerateMessageStreamChunk> {
454
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>> {
437
455
  const params = {
438
456
  ...this.config,
439
457
  model,
@@ -493,10 +511,10 @@ export class OpenRouterProvider implements AiProvider {
493
511
  }
494
512
 
495
513
  // Parse JSON response if JSON mode was enabled
496
- let structured: AgentStructuredResponse | undefined;
514
+ let structured: TStructured | undefined;
497
515
  if (input.parameters?.jsonMode && accumulated) {
498
516
  try {
499
- structured = JSON.parse(accumulated) as AgentStructuredResponse;
517
+ structured = JSON.parse(accumulated) as TStructured;
500
518
  } catch (error) {
501
519
  console.warn(
502
520
  "[OPENROUTER] Failed to parse JSON response in stream:",
package/src/types/ai.ts CHANGED
@@ -76,7 +76,7 @@ export interface AgentStructuredResponse {
76
76
  /**
77
77
  * Output from AI message generation
78
78
  */
79
- export interface GenerateMessageOutput {
79
+ export interface GenerateMessageOutput<TStructured = AgentStructuredResponse> {
80
80
  /** The generated message */
81
81
  message: string;
82
82
  /** Optional metadata about generation */
@@ -91,13 +91,15 @@ export interface GenerateMessageOutput {
91
91
  [key: string]: unknown;
92
92
  };
93
93
  /** Structured response data (when JSON mode is enabled) */
94
- structured?: AgentStructuredResponse;
94
+ structured?: TStructured;
95
95
  }
96
96
 
97
97
  /**
98
98
  * Stream chunk from AI message generation
99
99
  */
100
- export interface GenerateMessageStreamChunk {
100
+ export interface GenerateMessageStreamChunk<
101
+ TStructured = AgentStructuredResponse
102
+ > {
101
103
  /** The delta/chunk of the message */
102
104
  delta: string;
103
105
  /** Accumulated message so far */
@@ -116,7 +118,7 @@ export interface GenerateMessageStreamChunk {
116
118
  [key: string]: unknown;
117
119
  };
118
120
  /** Structured response data (only available when done=true and JSON mode is enabled) */
119
- structured?: AgentStructuredResponse;
121
+ structured?: TStructured;
120
122
  }
121
123
 
122
124
  /**
@@ -129,14 +131,17 @@ export interface AiProvider {
129
131
  /**
130
132
  * Generate a message based on prompt and context
131
133
  */
132
- generateMessage<TContext = unknown>(
134
+ generateMessage<TContext = unknown, TStructured = AgentStructuredResponse>(
133
135
  input: GenerateMessageInput<TContext>
134
- ): Promise<GenerateMessageOutput>;
136
+ ): Promise<GenerateMessageOutput<TStructured>>;
135
137
 
136
138
  /**
137
139
  * Generate a message as a stream based on prompt and context
138
140
  */
139
- generateMessageStream<TContext = unknown>(
141
+ generateMessageStream<
142
+ TContext = unknown,
143
+ TStructured = AgentStructuredResponse
144
+ >(
140
145
  input: GenerateMessageInput<TContext>
141
- ): AsyncGenerator<GenerateMessageStreamChunk>;
146
+ ): AsyncGenerator<GenerateMessageStreamChunk<TStructured>>;
142
147
  }