@juspay/neurolink 2.0.0 → 2.1.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 (43) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +31 -5
  3. package/dist/cli/commands/config.d.ts +6 -6
  4. package/dist/cli/index.js +29 -30
  5. package/dist/core/types.d.ts +2 -0
  6. package/dist/lib/core/types.d.ts +2 -0
  7. package/dist/lib/neurolink.d.ts +2 -0
  8. package/dist/lib/neurolink.js +23 -2
  9. package/dist/lib/providers/agent-enhanced-provider.d.ts +1 -0
  10. package/dist/lib/providers/agent-enhanced-provider.js +59 -3
  11. package/dist/lib/providers/amazonBedrock.js +70 -24
  12. package/dist/lib/providers/anthropic.js +77 -15
  13. package/dist/lib/providers/azureOpenAI.js +77 -15
  14. package/dist/lib/providers/googleAIStudio.js +70 -26
  15. package/dist/lib/providers/googleVertexAI.js +70 -24
  16. package/dist/lib/providers/huggingFace.js +70 -26
  17. package/dist/lib/providers/mistralAI.js +70 -26
  18. package/dist/lib/providers/ollama.d.ts +1 -1
  19. package/dist/lib/providers/ollama.js +24 -10
  20. package/dist/lib/providers/openAI.js +67 -23
  21. package/dist/lib/providers/timeout-wrapper.d.ts +40 -0
  22. package/dist/lib/providers/timeout-wrapper.js +100 -0
  23. package/dist/lib/utils/timeout.d.ts +69 -0
  24. package/dist/lib/utils/timeout.js +130 -0
  25. package/dist/neurolink.d.ts +2 -0
  26. package/dist/neurolink.js +23 -2
  27. package/dist/providers/agent-enhanced-provider.d.ts +1 -0
  28. package/dist/providers/agent-enhanced-provider.js +59 -3
  29. package/dist/providers/amazonBedrock.js +70 -24
  30. package/dist/providers/anthropic.js +77 -15
  31. package/dist/providers/azureOpenAI.js +77 -15
  32. package/dist/providers/googleAIStudio.js +70 -26
  33. package/dist/providers/googleVertexAI.js +70 -24
  34. package/dist/providers/huggingFace.js +70 -26
  35. package/dist/providers/mistralAI.js +70 -26
  36. package/dist/providers/ollama.d.ts +1 -1
  37. package/dist/providers/ollama.js +24 -10
  38. package/dist/providers/openAI.js +67 -23
  39. package/dist/providers/timeout-wrapper.d.ts +40 -0
  40. package/dist/providers/timeout-wrapper.js +100 -0
  41. package/dist/utils/timeout.d.ts +69 -0
  42. package/dist/utils/timeout.js +130 -0
  43. package/package.json +1 -1
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { streamText, generateText, Output } from "ai";
14
14
  import { logger } from "../utils/logger.js";
15
+ import { getDefaultTimeout } from "../utils/timeout.js";
15
16
  // Default system context
16
17
  const DEFAULT_SYSTEM_CONTEXT = {
17
18
  systemPrompt: "You are a helpful AI assistant.",
@@ -275,32 +276,35 @@ class OllamaLanguageModel {
275
276
  export class Ollama {
276
277
  baseUrl;
277
278
  modelName;
278
- timeout;
279
+ defaultTimeout;
279
280
  constructor(modelName) {
280
281
  this.baseUrl = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
281
282
  this.modelName = modelName || process.env.OLLAMA_MODEL || "llama2";
282
- this.timeout = parseInt(process.env.OLLAMA_TIMEOUT || "60000"); // 60 seconds default
283
+ // Use environment variable for backward compatibility, but convert to format used by other providers
284
+ const envTimeout = process.env.OLLAMA_TIMEOUT ? parseInt(process.env.OLLAMA_TIMEOUT) : undefined;
285
+ this.defaultTimeout = envTimeout || parseInt(getDefaultTimeout('ollama', 'generate').replace(/[^\d]/g, ''));
283
286
  logger.debug("[Ollama] Initialized", {
284
287
  baseUrl: this.baseUrl,
285
288
  modelName: this.modelName,
286
- timeout: this.timeout,
289
+ defaultTimeout: this.defaultTimeout,
287
290
  });
288
291
  }
289
292
  /**
290
293
  * Gets the appropriate model instance
291
294
  * @private
292
295
  */
293
- getModel() {
296
+ getModel(timeout) {
294
297
  logger.debug("Ollama.getModel - Ollama model selected", {
295
298
  modelName: this.modelName,
299
+ timeout: timeout || this.defaultTimeout,
296
300
  });
297
- return new OllamaLanguageModel(this.modelName, this.baseUrl, this.timeout);
301
+ return new OllamaLanguageModel(this.modelName, this.baseUrl, timeout || this.defaultTimeout);
298
302
  }
299
303
  /**
300
304
  * Health check - verify Ollama service is running and accessible
301
305
  */
302
306
  async checkHealth() {
303
- const model = new OllamaLanguageModel(this.modelName, this.baseUrl, this.timeout);
307
+ const model = new OllamaLanguageModel(this.modelName, this.baseUrl, this.defaultTimeout);
304
308
  return await model["checkHealth"]();
305
309
  }
306
310
  /**
@@ -388,17 +392,22 @@ export class Ollama {
388
392
  const options = typeof optionsOrPrompt === "string"
389
393
  ? { prompt: optionsOrPrompt }
390
394
  : optionsOrPrompt;
391
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
395
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout, } = options;
392
396
  // Use schema from options or fallback parameter
393
397
  const finalSchema = schema || analysisSchema;
398
+ // Convert timeout to milliseconds if provided as string
399
+ const timeoutMs = timeout
400
+ ? (typeof timeout === 'string' ? parseInt(getDefaultTimeout('ollama', 'generate').replace(/[^\d]/g, '')) : timeout)
401
+ : this.defaultTimeout;
394
402
  logger.debug(`[${functionTag}] Generate request started`, {
395
403
  provider,
396
404
  modelName: this.modelName,
397
405
  promptLength: prompt.length,
398
406
  temperature,
399
407
  maxTokens,
408
+ timeout: timeoutMs,
400
409
  });
401
- const model = this.getModel();
410
+ const model = this.getModel(timeoutMs);
402
411
  const generateOptions = {
403
412
  model: model,
404
413
  prompt: prompt,
@@ -446,9 +455,13 @@ export class Ollama {
446
455
  const options = typeof optionsOrPrompt === "string"
447
456
  ? { prompt: optionsOrPrompt }
448
457
  : optionsOrPrompt;
449
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
458
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout, } = options;
450
459
  // Use schema from options or fallback parameter
451
460
  const finalSchema = schema || analysisSchema;
461
+ // Convert timeout to milliseconds if provided as string
462
+ const timeoutMs = timeout
463
+ ? (typeof timeout === 'string' ? parseInt(getDefaultTimeout('ollama', 'stream').replace(/[^\d]/g, '')) : timeout)
464
+ : this.defaultTimeout;
452
465
  logger.debug(`[${functionTag}] Stream request started`, {
453
466
  provider,
454
467
  modelName: this.modelName,
@@ -456,8 +469,9 @@ export class Ollama {
456
469
  temperature,
457
470
  maxTokens,
458
471
  hasSchema: !!finalSchema,
472
+ timeout: timeoutMs,
459
473
  });
460
- const model = this.getModel();
474
+ const model = this.getModel(timeoutMs);
461
475
  const streamOptions = {
462
476
  model: model,
463
477
  prompt: prompt,
@@ -1,6 +1,7 @@
1
1
  import { openai } from "@ai-sdk/openai";
2
2
  import { streamText, generateText, Output, } from "ai";
3
3
  import { logger } from "../utils/logger.js";
4
+ import { createTimeoutController, getDefaultTimeout, TimeoutError } from "../utils/timeout.js";
4
5
  // Default system context
5
6
  const DEFAULT_SYSTEM_CONTEXT = {
6
7
  systemPrompt: "You are a helpful AI assistant.",
@@ -59,7 +60,7 @@ export class OpenAI {
59
60
  const options = typeof optionsOrPrompt === "string"
60
61
  ? { prompt: optionsOrPrompt }
61
62
  : optionsOrPrompt;
62
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
63
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout = getDefaultTimeout(provider, 'stream'), } = options;
63
64
  // Use schema from options or fallback parameter
64
65
  const finalSchema = schema || analysisSchema;
65
66
  logger.debug(`[${functionTag}] Stream text started`, {
@@ -68,13 +69,18 @@ export class OpenAI {
68
69
  promptLength: prompt.length,
69
70
  temperature,
70
71
  maxTokens,
72
+ timeout,
71
73
  });
74
+ // Create timeout controller if timeout is specified
75
+ const timeoutController = createTimeoutController(timeout, provider, 'stream');
72
76
  const streamOptions = {
73
77
  model: this.model,
74
78
  prompt: prompt,
75
79
  system: systemPrompt,
76
80
  temperature,
77
81
  maxTokens,
82
+ // Add abort signal if available
83
+ ...(timeoutController && { abortSignal: timeoutController.controller.signal }),
78
84
  onError: (event) => {
79
85
  const error = event.error;
80
86
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -116,15 +122,28 @@ export class OpenAI {
116
122
  });
117
123
  }
118
124
  const result = streamText(streamOptions);
125
+ // For streaming, we can't clean up immediately, but the timeout will auto-clean
126
+ // The user should handle the stream and any timeout errors
119
127
  return result;
120
128
  }
121
129
  catch (err) {
122
- logger.debug(`[${functionTag}] Exception`, {
123
- provider,
124
- modelName: this.modelName,
125
- message: "Error in streaming text",
126
- err: String(err),
127
- });
130
+ // Log timeout errors specifically
131
+ if (err instanceof TimeoutError) {
132
+ logger.debug(`[${functionTag}] Timeout error`, {
133
+ provider,
134
+ modelName: this.modelName,
135
+ timeout: err.timeout,
136
+ message: err.message,
137
+ });
138
+ }
139
+ else {
140
+ logger.debug(`[${functionTag}] Exception`, {
141
+ provider,
142
+ modelName: this.modelName,
143
+ message: "Error in streaming text",
144
+ err: String(err),
145
+ });
146
+ }
128
147
  throw err; // Re-throw error to trigger fallback
129
148
  }
130
149
  }
@@ -136,7 +155,7 @@ export class OpenAI {
136
155
  const options = typeof optionsOrPrompt === "string"
137
156
  ? { prompt: optionsOrPrompt }
138
157
  : optionsOrPrompt;
139
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
158
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout = getDefaultTimeout(provider, 'generate'), } = options;
140
159
  // Use schema from options or fallback parameter
141
160
  const finalSchema = schema || analysisSchema;
142
161
  logger.debug(`[${functionTag}] Generate text started`, {
@@ -145,36 +164,61 @@ export class OpenAI {
145
164
  promptLength: prompt.length,
146
165
  temperature,
147
166
  maxTokens,
167
+ timeout,
148
168
  });
169
+ // Create timeout controller if timeout is specified
170
+ const timeoutController = createTimeoutController(timeout, provider, 'generate');
149
171
  const generateOptions = {
150
172
  model: this.model,
151
173
  prompt: prompt,
152
174
  system: systemPrompt,
153
175
  temperature,
154
176
  maxTokens,
177
+ // Add abort signal if available
178
+ ...(timeoutController && { abortSignal: timeoutController.controller.signal }),
155
179
  };
156
180
  if (finalSchema) {
157
181
  generateOptions.experimental_output = Output.object({
158
182
  schema: finalSchema,
159
183
  });
160
184
  }
161
- const result = await generateText(generateOptions);
162
- logger.debug(`[${functionTag}] Generate text completed`, {
163
- provider,
164
- modelName: this.modelName,
165
- usage: result.usage,
166
- finishReason: result.finishReason,
167
- responseLength: result.text?.length || 0,
168
- });
169
- return result;
185
+ try {
186
+ const result = await generateText(generateOptions);
187
+ // Clean up timeout if successful
188
+ timeoutController?.cleanup();
189
+ logger.debug(`[${functionTag}] Generate text completed`, {
190
+ provider,
191
+ modelName: this.modelName,
192
+ usage: result.usage,
193
+ finishReason: result.finishReason,
194
+ responseLength: result.text?.length || 0,
195
+ timeout,
196
+ });
197
+ return result;
198
+ }
199
+ finally {
200
+ // Always cleanup timeout
201
+ timeoutController?.cleanup();
202
+ }
170
203
  }
171
204
  catch (err) {
172
- logger.debug(`[${functionTag}] Exception`, {
173
- provider,
174
- modelName: this.modelName,
175
- message: "Error in generating text",
176
- err: String(err),
177
- });
205
+ // Log timeout errors specifically
206
+ if (err instanceof TimeoutError) {
207
+ logger.debug(`[${functionTag}] Timeout error`, {
208
+ provider,
209
+ modelName: this.modelName,
210
+ timeout: err.timeout,
211
+ message: err.message,
212
+ });
213
+ }
214
+ else {
215
+ logger.debug(`[${functionTag}] Exception`, {
216
+ provider,
217
+ modelName: this.modelName,
218
+ message: "Error in generating text",
219
+ err: String(err),
220
+ });
221
+ }
178
222
  throw err; // Re-throw error to trigger fallback
179
223
  }
180
224
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Timeout wrapper for AI provider operations
3
+ *
4
+ * Provides a consistent way to add timeout functionality to any async operation.
5
+ */
6
+ /**
7
+ * Wrap an async operation with a timeout
8
+ * @param promise - The promise to wrap
9
+ * @param timeout - Timeout duration (number in ms or string with unit)
10
+ * @param provider - Provider name for error messages
11
+ * @param operation - Operation type (generate or stream)
12
+ * @returns The result of the promise or throws TimeoutError
13
+ */
14
+ export declare function withTimeout<T>(promise: Promise<T>, timeout: number | string | undefined, provider: string, operation: 'generate' | 'stream'): Promise<T>;
15
+ /**
16
+ * Wrap a streaming async generator with timeout
17
+ * @param generator - The async generator to wrap
18
+ * @param timeout - Timeout duration for the entire stream
19
+ * @param provider - Provider name for error messages
20
+ * @returns Wrapped async generator that respects timeout
21
+ */
22
+ export declare function withStreamingTimeout<T>(generator: AsyncGenerator<T>, timeout: number | string | undefined, provider: string): AsyncGenerator<T>;
23
+ /**
24
+ * Create an abort controller with timeout
25
+ * @param timeout - Timeout duration
26
+ * @param provider - Provider name for error messages
27
+ * @param operation - Operation type
28
+ * @returns AbortController and cleanup function
29
+ */
30
+ export declare function createTimeoutController(timeout: number | string | undefined, provider: string, operation: 'generate' | 'stream'): {
31
+ controller: AbortController;
32
+ cleanup: () => void;
33
+ timeoutMs: number;
34
+ } | null;
35
+ /**
36
+ * Merge abort signals (for combining user abort with timeout)
37
+ * @param signals - Array of abort signals to merge
38
+ * @returns Combined abort controller
39
+ */
40
+ export declare function mergeAbortSignals(signals: (AbortSignal | undefined)[]): AbortController;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Timeout wrapper for AI provider operations
3
+ *
4
+ * Provides a consistent way to add timeout functionality to any async operation.
5
+ */
6
+ import { parseTimeout, TimeoutError, createTimeoutPromise } from '../utils/timeout.js';
7
+ /**
8
+ * Wrap an async operation with a timeout
9
+ * @param promise - The promise to wrap
10
+ * @param timeout - Timeout duration (number in ms or string with unit)
11
+ * @param provider - Provider name for error messages
12
+ * @param operation - Operation type (generate or stream)
13
+ * @returns The result of the promise or throws TimeoutError
14
+ */
15
+ export async function withTimeout(promise, timeout, provider, operation) {
16
+ const timeoutPromise = createTimeoutPromise(timeout, provider, operation);
17
+ if (!timeoutPromise) {
18
+ // No timeout specified, return original promise
19
+ return promise;
20
+ }
21
+ // Race between the actual operation and timeout
22
+ return Promise.race([promise, timeoutPromise]);
23
+ }
24
+ /**
25
+ * Wrap a streaming async generator with timeout
26
+ * @param generator - The async generator to wrap
27
+ * @param timeout - Timeout duration for the entire stream
28
+ * @param provider - Provider name for error messages
29
+ * @returns Wrapped async generator that respects timeout
30
+ */
31
+ export async function* withStreamingTimeout(generator, timeout, provider) {
32
+ const timeoutMs = parseTimeout(timeout);
33
+ if (!timeoutMs) {
34
+ // No timeout, pass through original generator
35
+ yield* generator;
36
+ return;
37
+ }
38
+ const startTime = Date.now();
39
+ try {
40
+ for await (const chunk of generator) {
41
+ // Check if we've exceeded the timeout
42
+ if (Date.now() - startTime > timeoutMs) {
43
+ throw new TimeoutError(`${provider} streaming operation timed out after ${timeout}`, timeoutMs, provider, 'stream');
44
+ }
45
+ yield chunk;
46
+ }
47
+ }
48
+ finally {
49
+ // Ensure generator is properly closed
50
+ if (generator.return) {
51
+ await generator.return(undefined);
52
+ }
53
+ }
54
+ }
55
+ /**
56
+ * Create an abort controller with timeout
57
+ * @param timeout - Timeout duration
58
+ * @param provider - Provider name for error messages
59
+ * @param operation - Operation type
60
+ * @returns AbortController and cleanup function
61
+ */
62
+ export function createTimeoutController(timeout, provider, operation) {
63
+ const timeoutMs = parseTimeout(timeout);
64
+ if (!timeoutMs) {
65
+ return null;
66
+ }
67
+ const controller = new AbortController();
68
+ const timer = setTimeout(() => {
69
+ controller.abort(new TimeoutError(`${provider} ${operation} operation timed out after ${timeout}`, timeoutMs, provider, operation));
70
+ }, timeoutMs);
71
+ // Cleanup function to clear the timer
72
+ const cleanup = () => {
73
+ clearTimeout(timer);
74
+ };
75
+ return { controller, cleanup, timeoutMs };
76
+ }
77
+ /**
78
+ * Merge abort signals (for combining user abort with timeout)
79
+ * @param signals - Array of abort signals to merge
80
+ * @returns Combined abort controller
81
+ */
82
+ export function mergeAbortSignals(signals) {
83
+ const controller = new AbortController();
84
+ // Listen to all signals and abort when any fires
85
+ for (const signal of signals) {
86
+ if (signal && !signal.aborted) {
87
+ signal.addEventListener('abort', () => {
88
+ if (!controller.signal.aborted) {
89
+ controller.abort(signal.reason);
90
+ }
91
+ });
92
+ }
93
+ // If any signal is already aborted, abort immediately
94
+ if (signal?.aborted) {
95
+ controller.abort(signal.reason);
96
+ break;
97
+ }
98
+ }
99
+ return controller;
100
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Timeout utilities for NeuroLink
3
+ *
4
+ * Provides flexible timeout parsing and error handling for AI operations.
5
+ * Supports multiple time formats: milliseconds, seconds, minutes, hours.
6
+ */
7
+ /**
8
+ * Custom error class for timeout operations
9
+ */
10
+ export declare class TimeoutError extends Error {
11
+ readonly timeout: number;
12
+ readonly provider?: string | undefined;
13
+ readonly operation?: "generate" | "stream" | undefined;
14
+ constructor(message: string, timeout: number, provider?: string | undefined, operation?: "generate" | "stream" | undefined);
15
+ }
16
+ /**
17
+ * Parse timeout value from various formats
18
+ * @param timeout - Can be number (ms), string with unit, or undefined
19
+ * @returns Parsed timeout in milliseconds or undefined
20
+ * @throws Error if format is invalid
21
+ *
22
+ * Examples:
23
+ * - parseTimeout(5000) => 5000
24
+ * - parseTimeout('30s') => 30000
25
+ * - parseTimeout('2m') => 120000
26
+ * - parseTimeout('1.5h') => 5400000
27
+ * - parseTimeout(undefined) => undefined
28
+ */
29
+ export declare function parseTimeout(timeout: number | string | undefined): number | undefined;
30
+ /**
31
+ * Default timeout configurations for different providers and operations
32
+ */
33
+ export declare const DEFAULT_TIMEOUTS: {
34
+ global: string;
35
+ streaming: string;
36
+ providers: {
37
+ openai: string;
38
+ bedrock: string;
39
+ vertex: string;
40
+ anthropic: string;
41
+ azure: string;
42
+ 'google-ai': string;
43
+ huggingface: string;
44
+ ollama: string;
45
+ mistral: string;
46
+ };
47
+ tools: {
48
+ default: string;
49
+ filesystem: string;
50
+ network: string;
51
+ computation: string;
52
+ };
53
+ };
54
+ /**
55
+ * Get default timeout for a specific provider
56
+ * @param provider - Provider name
57
+ * @param operation - Operation type (generate or stream)
58
+ * @returns Default timeout string
59
+ */
60
+ export declare function getDefaultTimeout(provider: string, operation?: 'generate' | 'stream'): string;
61
+ /**
62
+ * Create a timeout promise that rejects after specified duration
63
+ * @param timeout - Timeout duration
64
+ * @param provider - Provider name for error message
65
+ * @param operation - Operation type for error message
66
+ * @returns Promise that rejects with TimeoutError
67
+ */
68
+ export declare function createTimeoutPromise(timeout: number | string | undefined, provider: string, operation: 'generate' | 'stream'): Promise<never> | null;
69
+ export { createTimeoutController } from '../providers/timeout-wrapper.js';
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Timeout utilities for NeuroLink
3
+ *
4
+ * Provides flexible timeout parsing and error handling for AI operations.
5
+ * Supports multiple time formats: milliseconds, seconds, minutes, hours.
6
+ */
7
+ /**
8
+ * Custom error class for timeout operations
9
+ */
10
+ export class TimeoutError extends Error {
11
+ timeout;
12
+ provider;
13
+ operation;
14
+ constructor(message, timeout, provider, operation) {
15
+ super(message);
16
+ this.timeout = timeout;
17
+ this.provider = provider;
18
+ this.operation = operation;
19
+ this.name = 'TimeoutError';
20
+ // Maintains proper stack trace for where error was thrown
21
+ if (typeof Error.captureStackTrace === 'function') {
22
+ Error.captureStackTrace(this, TimeoutError);
23
+ }
24
+ }
25
+ }
26
+ /**
27
+ * Parse timeout value from various formats
28
+ * @param timeout - Can be number (ms), string with unit, or undefined
29
+ * @returns Parsed timeout in milliseconds or undefined
30
+ * @throws Error if format is invalid
31
+ *
32
+ * Examples:
33
+ * - parseTimeout(5000) => 5000
34
+ * - parseTimeout('30s') => 30000
35
+ * - parseTimeout('2m') => 120000
36
+ * - parseTimeout('1.5h') => 5400000
37
+ * - parseTimeout(undefined) => undefined
38
+ */
39
+ export function parseTimeout(timeout) {
40
+ if (timeout === undefined)
41
+ return undefined;
42
+ if (typeof timeout === 'number') {
43
+ if (timeout <= 0) {
44
+ throw new Error(`Timeout must be positive, got: ${timeout}`);
45
+ }
46
+ return timeout; // Assume milliseconds
47
+ }
48
+ if (typeof timeout === 'string') {
49
+ // Match number (including decimals) followed by optional unit
50
+ const match = timeout.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)?$/);
51
+ if (!match) {
52
+ throw new Error(`Invalid timeout format: ${timeout}. Use formats like '30s', '2m', '500ms', or '1.5h'`);
53
+ }
54
+ const value = parseFloat(match[1]);
55
+ if (value <= 0) {
56
+ throw new Error(`Timeout must be positive, got: ${value}`);
57
+ }
58
+ const unit = match[2] || 'ms';
59
+ switch (unit) {
60
+ case 'ms': return value;
61
+ case 's': return value * 1000;
62
+ case 'm': return value * 60 * 1000;
63
+ case 'h': return value * 60 * 60 * 1000;
64
+ default: return value; // Should never reach here due to regex
65
+ }
66
+ }
67
+ throw new Error(`Invalid timeout type: ${typeof timeout}`);
68
+ }
69
+ /**
70
+ * Default timeout configurations for different providers and operations
71
+ */
72
+ export const DEFAULT_TIMEOUTS = {
73
+ global: '30s', // Default for all providers
74
+ streaming: '2m', // Longer timeout for streaming operations
75
+ providers: {
76
+ openai: '30s', // OpenAI typically responds quickly
77
+ bedrock: '45s', // AWS can be slower, especially for cold starts
78
+ vertex: '60s', // Google Cloud can be slower
79
+ anthropic: '30s', // Direct Anthropic API is fast
80
+ azure: '30s', // Azure OpenAI similar to OpenAI
81
+ 'google-ai': '30s', // Google AI Studio is fast
82
+ huggingface: '2m', // Open source models vary significantly
83
+ ollama: '5m', // Local models need more time, especially large ones
84
+ mistral: '45s' // Mistral AI moderate speed
85
+ },
86
+ tools: {
87
+ default: '10s', // Default timeout for MCP tool execution
88
+ filesystem: '5s', // File operations should be quick
89
+ network: '30s', // Network requests might take longer
90
+ computation: '2m' // Heavy computation tools need more time
91
+ }
92
+ };
93
+ /**
94
+ * Get default timeout for a specific provider
95
+ * @param provider - Provider name
96
+ * @param operation - Operation type (generate or stream)
97
+ * @returns Default timeout string
98
+ */
99
+ export function getDefaultTimeout(provider, operation = 'generate') {
100
+ if (operation === 'stream') {
101
+ return DEFAULT_TIMEOUTS.streaming;
102
+ }
103
+ const providerKey = provider.toLowerCase().replace('_', '-');
104
+ return DEFAULT_TIMEOUTS.providers[providerKey]
105
+ || DEFAULT_TIMEOUTS.global;
106
+ }
107
+ /**
108
+ * Create a timeout promise that rejects after specified duration
109
+ * @param timeout - Timeout duration
110
+ * @param provider - Provider name for error message
111
+ * @param operation - Operation type for error message
112
+ * @returns Promise that rejects with TimeoutError
113
+ */
114
+ export function createTimeoutPromise(timeout, provider, operation) {
115
+ const timeoutMs = parseTimeout(timeout);
116
+ if (!timeoutMs) {
117
+ return null; // No timeout
118
+ }
119
+ return new Promise((_, reject) => {
120
+ const timer = setTimeout(() => {
121
+ reject(new TimeoutError(`${provider} ${operation} operation timed out after ${timeout}`, timeoutMs, provider, operation));
122
+ }, timeoutMs);
123
+ // Unref the timer so it doesn't keep the process alive (Node.js only)
124
+ if (typeof timer === 'object' && timer && 'unref' in timer && typeof timer.unref === 'function') {
125
+ timer.unref();
126
+ }
127
+ });
128
+ }
129
+ // Re-export createTimeoutController from timeout-wrapper for convenience
130
+ export { createTimeoutController } from '../providers/timeout-wrapper.js';
@@ -12,6 +12,7 @@ export interface TextGenerationOptions {
12
12
  maxTokens?: number;
13
13
  systemPrompt?: string;
14
14
  schema?: any;
15
+ timeout?: number | string;
15
16
  disableTools?: boolean;
16
17
  }
17
18
  export interface StreamTextOptions {
@@ -20,6 +21,7 @@ export interface StreamTextOptions {
20
21
  temperature?: number;
21
22
  maxTokens?: number;
22
23
  systemPrompt?: string;
24
+ timeout?: number | string;
23
25
  }
24
26
  export interface TextGenerationResult {
25
27
  content: string;