@juspay/neurolink 2.0.0 → 3.0.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 (50) hide show
  1. package/CHANGELOG.md +34 -7
  2. package/README.md +38 -34
  3. package/dist/cli/commands/config.d.ts +6 -6
  4. package/dist/cli/index.js +46 -35
  5. package/dist/core/types.d.ts +2 -0
  6. package/dist/lib/core/types.d.ts +2 -0
  7. package/dist/lib/mcp/plugins/filesystem-mcp.d.ts +1 -1
  8. package/dist/lib/neurolink.d.ts +2 -0
  9. package/dist/lib/neurolink.js +23 -2
  10. package/dist/lib/providers/agent-enhanced-provider.d.ts +1 -0
  11. package/dist/lib/providers/agent-enhanced-provider.js +115 -51
  12. package/dist/lib/providers/amazonBedrock.js +74 -24
  13. package/dist/lib/providers/anthropic.js +80 -16
  14. package/dist/lib/providers/azureOpenAI.js +77 -15
  15. package/dist/lib/providers/googleAIStudio.js +77 -26
  16. package/dist/lib/providers/googleVertexAI.js +77 -24
  17. package/dist/lib/providers/huggingFace.js +74 -26
  18. package/dist/lib/providers/mistralAI.js +74 -26
  19. package/dist/lib/providers/ollama.d.ts +1 -1
  20. package/dist/lib/providers/ollama.js +32 -10
  21. package/dist/lib/providers/openAI.js +71 -23
  22. package/dist/lib/providers/timeout-wrapper.d.ts +40 -0
  23. package/dist/lib/providers/timeout-wrapper.js +100 -0
  24. package/dist/lib/proxy/proxy-fetch.d.ts +18 -0
  25. package/dist/lib/proxy/proxy-fetch.js +64 -0
  26. package/dist/lib/utils/timeout.d.ts +69 -0
  27. package/dist/lib/utils/timeout.js +138 -0
  28. package/dist/mcp/plugins/filesystem-mcp.d.ts +1 -1
  29. package/dist/mcp/plugins/filesystem-mcp.js +1 -1
  30. package/dist/neurolink.d.ts +2 -0
  31. package/dist/neurolink.js +23 -2
  32. package/dist/providers/agent-enhanced-provider.d.ts +1 -0
  33. package/dist/providers/agent-enhanced-provider.js +115 -51
  34. package/dist/providers/amazonBedrock.js +74 -24
  35. package/dist/providers/anthropic.js +80 -16
  36. package/dist/providers/azureOpenAI.js +77 -15
  37. package/dist/providers/googleAIStudio.js +77 -26
  38. package/dist/providers/googleVertexAI.js +77 -24
  39. package/dist/providers/huggingFace.js +74 -26
  40. package/dist/providers/mistralAI.js +74 -26
  41. package/dist/providers/ollama.d.ts +1 -1
  42. package/dist/providers/ollama.js +32 -10
  43. package/dist/providers/openAI.js +71 -23
  44. package/dist/providers/timeout-wrapper.d.ts +40 -0
  45. package/dist/providers/timeout-wrapper.js +100 -0
  46. package/dist/proxy/proxy-fetch.d.ts +18 -0
  47. package/dist/proxy/proxy-fetch.js +64 -0
  48. package/dist/utils/timeout.d.ts +69 -0
  49. package/dist/utils/timeout.js +138 -0
  50. package/package.json +2 -1
@@ -6,6 +6,8 @@
6
6
  */
7
7
  import { AIProviderName } from "../core/types.js";
8
8
  import { logger } from "../utils/logger.js";
9
+ import { createTimeoutController, TimeoutError, getDefaultTimeout, } from "../utils/timeout.js";
10
+ import { createProxyFetch } from "../proxy/proxy-fetch.js";
9
11
  export class AnthropicProvider {
10
12
  name = AIProviderName.ANTHROPIC;
11
13
  apiKey;
@@ -29,7 +31,7 @@ export class AnthropicProvider {
29
31
  getModel() {
30
32
  return this.defaultModel;
31
33
  }
32
- async makeRequest(endpoint, body, stream = false) {
34
+ async makeRequest(endpoint, body, stream = false, signal) {
33
35
  const url = `${this.baseURL}/v1/${endpoint}`;
34
36
  const headers = {
35
37
  "Content-Type": "application/json",
@@ -39,10 +41,12 @@ export class AnthropicProvider {
39
41
  };
40
42
  logger.debug(`[AnthropicProvider.makeRequest] ${stream ? "Streaming" : "Non-streaming"} request to ${url}`);
41
43
  logger.debug(`[AnthropicProvider.makeRequest] Model: ${body.model}, Max tokens: ${body.max_tokens}`);
42
- const response = await fetch(url, {
44
+ const proxyFetch = createProxyFetch();
45
+ const response = await proxyFetch(url, {
43
46
  method: "POST",
44
47
  headers,
45
48
  body: JSON.stringify(body),
49
+ signal, // Add abort signal for timeout support
46
50
  });
47
51
  if (!response.ok) {
48
52
  const errorText = await response.text();
@@ -52,13 +56,15 @@ export class AnthropicProvider {
52
56
  return response;
53
57
  }
54
58
  async generateText(optionsOrPrompt, schema) {
55
- logger.debug("[AnthropicProvider.generateText] Starting text generation");
59
+ const functionTag = "AnthropicProvider.generateText";
60
+ const provider = "anthropic";
61
+ logger.debug(`[${functionTag}] Starting text generation`);
56
62
  // Parse parameters with backward compatibility
57
63
  const options = typeof optionsOrPrompt === "string"
58
64
  ? { prompt: optionsOrPrompt }
59
65
  : optionsOrPrompt;
60
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are Claude, an AI assistant created by Anthropic. You are helpful, harmless, and honest.", } = options;
61
- logger.debug(`[AnthropicProvider.generateText] Prompt: "${prompt.substring(0, 100)}...", Temperature: ${temperature}, Max tokens: ${maxTokens}`);
66
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are Claude, an AI assistant created by Anthropic. You are helpful, harmless, and honest.", timeout = getDefaultTimeout(provider, "generate"), } = options;
67
+ logger.debug(`[${functionTag}] Prompt: "${prompt.substring(0, 100)}...", Temperature: ${temperature}, Max tokens: ${maxTokens}, Timeout: ${timeout}`);
62
68
  const requestBody = {
63
69
  model: this.getModel(),
64
70
  max_tokens: maxTokens,
@@ -71,10 +77,14 @@ export class AnthropicProvider {
71
77
  temperature,
72
78
  system: systemPrompt,
73
79
  };
80
+ // Create timeout controller if timeout is specified
81
+ const timeoutController = createTimeoutController(timeout, provider, "generate");
74
82
  try {
75
- const response = await this.makeRequest("messages", requestBody);
83
+ const response = await this.makeRequest("messages", requestBody, false, timeoutController?.controller.signal);
76
84
  const data = await response.json();
77
- logger.debug(`[AnthropicProvider.generateText] Success. Generated ${data.usage.output_tokens} tokens`);
85
+ // Clean up timeout if successful
86
+ timeoutController?.cleanup();
87
+ logger.debug(`[${functionTag}] Success. Generated ${data.usage.output_tokens} tokens`);
78
88
  const content = data.content.map((block) => block.text).join("");
79
89
  return {
80
90
  content,
@@ -89,18 +99,42 @@ export class AnthropicProvider {
89
99
  };
90
100
  }
91
101
  catch (error) {
92
- logger.error("[AnthropicProvider.generateText] Error:", error);
102
+ // Always cleanup timeout
103
+ timeoutController?.cleanup();
104
+ // Log timeout errors specifically
105
+ if (error instanceof TimeoutError) {
106
+ logger.error(`[${functionTag}] Timeout error`, {
107
+ provider,
108
+ timeout: error.timeout,
109
+ message: error.message,
110
+ });
111
+ }
112
+ else if (error?.name === "AbortError") {
113
+ // Convert AbortError to TimeoutError
114
+ const timeoutError = new TimeoutError(`${provider} generate operation timed out after ${timeout}`, timeoutController?.timeoutMs || 0, provider, "generate");
115
+ logger.error(`[${functionTag}] Timeout error`, {
116
+ provider,
117
+ timeout: timeoutController?.timeoutMs,
118
+ message: timeoutError.message,
119
+ });
120
+ throw timeoutError;
121
+ }
122
+ else {
123
+ logger.error(`[${functionTag}] Error:`, error);
124
+ }
93
125
  throw error;
94
126
  }
95
127
  }
96
128
  async streamText(optionsOrPrompt, schema) {
97
- logger.debug("[AnthropicProvider.streamText] Starting text streaming");
129
+ const functionTag = "AnthropicProvider.streamText";
130
+ const provider = "anthropic";
131
+ logger.debug(`[${functionTag}] Starting text streaming`);
98
132
  // Parse parameters with backward compatibility
99
133
  const options = typeof optionsOrPrompt === "string"
100
134
  ? { prompt: optionsOrPrompt }
101
135
  : optionsOrPrompt;
102
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are Claude, an AI assistant created by Anthropic. You are helpful, harmless, and honest.", } = options;
103
- logger.debug(`[AnthropicProvider.streamText] Streaming prompt: "${prompt.substring(0, 100)}..."`);
136
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are Claude, an AI assistant created by Anthropic. You are helpful, harmless, and honest.", timeout = getDefaultTimeout(provider, "stream"), } = options;
137
+ logger.debug(`[${functionTag}] Streaming prompt: "${prompt.substring(0, 100)}...", Timeout: ${timeout}`);
104
138
  const requestBody = {
105
139
  model: this.getModel(),
106
140
  max_tokens: maxTokens,
@@ -114,30 +148,60 @@ export class AnthropicProvider {
114
148
  system: systemPrompt,
115
149
  stream: true,
116
150
  };
151
+ // Create timeout controller if timeout is specified
152
+ const timeoutController = createTimeoutController(timeout, provider, "stream");
117
153
  try {
118
- const response = await this.makeRequest("messages", requestBody, true);
154
+ const response = await this.makeRequest("messages", requestBody, true, timeoutController?.controller.signal);
119
155
  if (!response.body) {
120
156
  throw new Error("No response body received");
121
157
  }
122
- // Return a StreamTextResult-like object
158
+ // Return a StreamTextResult-like object with timeout signal
123
159
  return {
124
- textStream: this.createAsyncIterable(response.body),
160
+ textStream: this.createAsyncIterable(response.body, timeoutController?.controller.signal),
125
161
  text: "",
126
162
  usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
127
163
  finishReason: "end_turn",
164
+ // Store timeout controller for external cleanup if needed
165
+ _timeoutController: timeoutController,
128
166
  };
129
167
  }
130
168
  catch (error) {
131
- logger.error("[AnthropicProvider.streamText] Error:", error);
169
+ // Cleanup timeout on error
170
+ timeoutController?.cleanup();
171
+ // Log timeout errors specifically
172
+ if (error instanceof TimeoutError) {
173
+ logger.error(`[${functionTag}] Timeout error`, {
174
+ provider,
175
+ timeout: error.timeout,
176
+ message: error.message,
177
+ });
178
+ }
179
+ else if (error?.name === "AbortError") {
180
+ // Convert AbortError to TimeoutError
181
+ const timeoutError = new TimeoutError(`${provider} stream operation timed out after ${timeout}`, timeoutController?.timeoutMs || 0, provider, "stream");
182
+ logger.error(`[${functionTag}] Timeout error`, {
183
+ provider,
184
+ timeout: timeoutController?.timeoutMs,
185
+ message: timeoutError.message,
186
+ });
187
+ throw timeoutError;
188
+ }
189
+ else {
190
+ logger.error(`[${functionTag}] Error:`, error);
191
+ }
132
192
  throw error;
133
193
  }
134
194
  }
135
- async *createAsyncIterable(body) {
195
+ async *createAsyncIterable(body, signal) {
136
196
  const reader = body.getReader();
137
197
  const decoder = new TextDecoder();
138
198
  let buffer = "";
139
199
  try {
140
200
  while (true) {
201
+ // Check if aborted
202
+ if (signal?.aborted) {
203
+ throw new Error("AbortError");
204
+ }
141
205
  const { done, value } = await reader.read();
142
206
  if (done) {
143
207
  break;
@@ -6,6 +6,7 @@
6
6
  */
7
7
  import { AIProviderName } from "../core/types.js";
8
8
  import { logger } from "../utils/logger.js";
9
+ import { createTimeoutController, TimeoutError, getDefaultTimeout, } from "../utils/timeout.js";
9
10
  export class AzureOpenAIProvider {
10
11
  name = AIProviderName.AZURE;
11
12
  apiKey;
@@ -44,7 +45,7 @@ export class AzureOpenAIProvider {
44
45
  getApiUrl(stream = false) {
45
46
  return `${this.endpoint}/openai/deployments/${this.deploymentId}/chat/completions?api-version=${this.apiVersion}`;
46
47
  }
47
- async makeRequest(body, stream = false) {
48
+ async makeRequest(body, stream = false, signal) {
48
49
  const url = this.getApiUrl(stream);
49
50
  const headers = {
50
51
  "Content-Type": "application/json",
@@ -56,6 +57,7 @@ export class AzureOpenAIProvider {
56
57
  method: "POST",
57
58
  headers,
58
59
  body: JSON.stringify(body),
60
+ signal, // Add abort signal for timeout support
59
61
  });
60
62
  if (!response.ok) {
61
63
  const errorText = await response.text();
@@ -65,13 +67,15 @@ export class AzureOpenAIProvider {
65
67
  return response;
66
68
  }
67
69
  async generateText(optionsOrPrompt, schema) {
68
- logger.debug("[AzureOpenAIProvider.generateText] Starting text generation");
70
+ const functionTag = "AzureOpenAIProvider.generateText";
71
+ const provider = "azure";
72
+ logger.debug(`[${functionTag}] Starting text generation`);
69
73
  // Parse parameters with backward compatibility
70
74
  const options = typeof optionsOrPrompt === "string"
71
75
  ? { prompt: optionsOrPrompt }
72
76
  : optionsOrPrompt;
73
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are a helpful AI assistant.", } = options;
74
- logger.debug(`[AzureOpenAIProvider.generateText] Prompt: "${prompt.substring(0, 100)}...", Temperature: ${temperature}, Max tokens: ${maxTokens}`);
77
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are a helpful AI assistant.", timeout = getDefaultTimeout(provider, "generate"), } = options;
78
+ logger.debug(`[${functionTag}] Prompt: "${prompt.substring(0, 100)}...", Temperature: ${temperature}, Max tokens: ${maxTokens}, Timeout: ${timeout}`);
75
79
  const messages = [];
76
80
  if (systemPrompt) {
77
81
  messages.push({
@@ -88,10 +92,14 @@ export class AzureOpenAIProvider {
88
92
  temperature,
89
93
  max_tokens: maxTokens,
90
94
  };
95
+ // Create timeout controller if timeout is specified
96
+ const timeoutController = createTimeoutController(timeout, provider, "generate");
91
97
  try {
92
- const response = await this.makeRequest(requestBody);
98
+ const response = await this.makeRequest(requestBody, false, timeoutController?.controller.signal);
93
99
  const data = await response.json();
94
- logger.debug(`[AzureOpenAIProvider.generateText] Success. Generated ${data.usage.completion_tokens} tokens`);
100
+ // Clean up timeout if successful
101
+ timeoutController?.cleanup();
102
+ logger.debug(`[${functionTag}] Success. Generated ${data.usage.completion_tokens} tokens`);
95
103
  const content = data.choices[0]?.message?.content || "";
96
104
  return {
97
105
  content,
@@ -106,18 +114,42 @@ export class AzureOpenAIProvider {
106
114
  };
107
115
  }
108
116
  catch (error) {
109
- logger.error("[AzureOpenAIProvider.generateText] Error:", error);
117
+ // Always cleanup timeout
118
+ timeoutController?.cleanup();
119
+ // Log timeout errors specifically
120
+ if (error instanceof TimeoutError) {
121
+ logger.error(`[${functionTag}] Timeout error`, {
122
+ provider,
123
+ timeout: error.timeout,
124
+ message: error.message,
125
+ });
126
+ }
127
+ else if (error?.name === "AbortError") {
128
+ // Convert AbortError to TimeoutError
129
+ const timeoutError = new TimeoutError(`${provider} generate operation timed out after ${timeout}`, timeoutController?.timeoutMs || 0, provider, "generate");
130
+ logger.error(`[${functionTag}] Timeout error`, {
131
+ provider,
132
+ timeout: timeoutController?.timeoutMs,
133
+ message: timeoutError.message,
134
+ });
135
+ throw timeoutError;
136
+ }
137
+ else {
138
+ logger.error(`[${functionTag}] Error:`, error);
139
+ }
110
140
  throw error;
111
141
  }
112
142
  }
113
143
  async streamText(optionsOrPrompt, schema) {
114
- logger.debug("[AzureOpenAIProvider.streamText] Starting text streaming");
144
+ const functionTag = "AzureOpenAIProvider.streamText";
145
+ const provider = "azure";
146
+ logger.debug(`[${functionTag}] Starting text streaming`);
115
147
  // Parse parameters with backward compatibility
116
148
  const options = typeof optionsOrPrompt === "string"
117
149
  ? { prompt: optionsOrPrompt }
118
150
  : optionsOrPrompt;
119
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are a helpful AI assistant.", } = options;
120
- logger.debug(`[AzureOpenAIProvider.streamText] Streaming prompt: "${prompt.substring(0, 100)}..."`);
151
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = "You are a helpful AI assistant.", timeout = getDefaultTimeout(provider, "stream"), } = options;
152
+ logger.debug(`[${functionTag}] Streaming prompt: "${prompt.substring(0, 100)}...", Timeout: ${timeout}`);
121
153
  const messages = [];
122
154
  if (systemPrompt) {
123
155
  messages.push({
@@ -135,30 +167,60 @@ export class AzureOpenAIProvider {
135
167
  max_tokens: maxTokens,
136
168
  stream: true,
137
169
  };
170
+ // Create timeout controller if timeout is specified
171
+ const timeoutController = createTimeoutController(timeout, provider, "stream");
138
172
  try {
139
- const response = await this.makeRequest(requestBody, true);
173
+ const response = await this.makeRequest(requestBody, true, timeoutController?.controller.signal);
140
174
  if (!response.body) {
141
175
  throw new Error("No response body received");
142
176
  }
143
- // Return a StreamTextResult-like object
177
+ // Return a StreamTextResult-like object with timeout signal
144
178
  return {
145
- textStream: this.createAsyncIterable(response.body),
179
+ textStream: this.createAsyncIterable(response.body, timeoutController?.controller.signal),
146
180
  text: "",
147
181
  usage: { promptTokens: 0, completionTokens: 0, totalTokens: 0 },
148
182
  finishReason: "stop",
183
+ // Store timeout controller for external cleanup if needed
184
+ _timeoutController: timeoutController,
149
185
  };
150
186
  }
151
187
  catch (error) {
152
- logger.error("[AzureOpenAIProvider.streamText] Error:", error);
188
+ // Cleanup timeout on error
189
+ timeoutController?.cleanup();
190
+ // Log timeout errors specifically
191
+ if (error instanceof TimeoutError) {
192
+ logger.error(`[${functionTag}] Timeout error`, {
193
+ provider,
194
+ timeout: error.timeout,
195
+ message: error.message,
196
+ });
197
+ }
198
+ else if (error?.name === "AbortError") {
199
+ // Convert AbortError to TimeoutError
200
+ const timeoutError = new TimeoutError(`${provider} stream operation timed out after ${timeout}`, timeoutController?.timeoutMs || 0, provider, "stream");
201
+ logger.error(`[${functionTag}] Timeout error`, {
202
+ provider,
203
+ timeout: timeoutController?.timeoutMs,
204
+ message: timeoutError.message,
205
+ });
206
+ throw timeoutError;
207
+ }
208
+ else {
209
+ logger.error(`[${functionTag}] Error:`, error);
210
+ }
153
211
  throw error;
154
212
  }
155
213
  }
156
- async *createAsyncIterable(body) {
214
+ async *createAsyncIterable(body, signal) {
157
215
  const reader = body.getReader();
158
216
  const decoder = new TextDecoder();
159
217
  let buffer = "";
160
218
  try {
161
219
  while (true) {
220
+ // Check if aborted
221
+ if (signal?.aborted) {
222
+ throw new Error("AbortError");
223
+ }
162
224
  const { done, value } = await reader.read();
163
225
  if (done) {
164
226
  break;
@@ -1,6 +1,8 @@
1
1
  import { createGoogleGenerativeAI } from "@ai-sdk/google";
2
2
  import { streamText, generateText, Output, } from "ai";
3
3
  import { logger } from "../utils/logger.js";
4
+ import { createTimeoutController, TimeoutError, getDefaultTimeout, } from "../utils/timeout.js";
5
+ import { createProxyFetch } from "../proxy/proxy-fetch.js";
4
6
  // CRITICAL: Setup environment variables early for AI SDK compatibility
5
7
  // The AI SDK specifically looks for GOOGLE_GENERATIVE_AI_API_KEY
6
8
  // We need to ensure this is set before any AI SDK operations
@@ -38,8 +40,10 @@ let _google = null;
38
40
  function getGoogleInstance() {
39
41
  if (!_google) {
40
42
  const apiKey = getGoogleAIApiKey();
43
+ const proxyFetch = createProxyFetch();
41
44
  _google = createGoogleGenerativeAI({
42
45
  apiKey: apiKey,
46
+ fetch: proxyFetch,
43
47
  headers: {
44
48
  "X-Powered-By": "NeuroLink",
45
49
  },
@@ -109,7 +113,7 @@ export class GoogleAIStudio {
109
113
  const options = typeof optionsOrPrompt === "string"
110
114
  ? { prompt: optionsOrPrompt }
111
115
  : optionsOrPrompt;
112
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, tools, } = options;
116
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, tools, timeout = getDefaultTimeout(provider, "stream"), } = options;
113
117
  // Use schema from options or fallback parameter
114
118
  const finalSchema = schema || analysisSchema;
115
119
  logger.debug(`[${functionTag}] Stream request started`, {
@@ -121,8 +125,11 @@ export class GoogleAIStudio {
121
125
  hasSchema: !!finalSchema,
122
126
  hasTools: !!tools,
123
127
  toolCount: tools ? Object.keys(tools).length : 0,
128
+ timeout,
124
129
  });
125
130
  const model = this.getModel();
131
+ // Create timeout controller if timeout is specified
132
+ const timeoutController = createTimeoutController(timeout, provider, "stream");
126
133
  const streamOptions = {
127
134
  model: model,
128
135
  prompt: prompt,
@@ -130,6 +137,10 @@ export class GoogleAIStudio {
130
137
  temperature,
131
138
  maxTokens,
132
139
  ...(tools && { tools }), // Add tools if provided
140
+ // Add abort signal if available
141
+ ...(timeoutController && {
142
+ abortSignal: timeoutController.controller.signal,
143
+ }),
133
144
  onError: (event) => {
134
145
  const error = event.error;
135
146
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -171,18 +182,31 @@ export class GoogleAIStudio {
171
182
  });
172
183
  }
173
184
  const result = streamText(streamOptions);
185
+ // For streaming, we can't clean up immediately, but the timeout will auto-clean
186
+ // The user should handle the stream and any timeout errors
174
187
  return result;
175
188
  }
176
189
  catch (err) {
177
- logger.error(`[${functionTag}] Exception`, {
178
- provider,
179
- modelName: this.modelName,
180
- message: "Error in streaming text",
181
- err: String(err),
182
- promptLength: typeof optionsOrPrompt === "string"
183
- ? optionsOrPrompt.length
184
- : optionsOrPrompt.prompt.length,
185
- });
190
+ // Log timeout errors specifically
191
+ if (err instanceof TimeoutError) {
192
+ logger.error(`[${functionTag}] Timeout error`, {
193
+ provider,
194
+ modelName: this.modelName,
195
+ timeout: err.timeout,
196
+ message: err.message,
197
+ });
198
+ }
199
+ else {
200
+ logger.error(`[${functionTag}] Exception`, {
201
+ provider,
202
+ modelName: this.modelName,
203
+ message: "Error in streaming text",
204
+ err: String(err),
205
+ promptLength: typeof optionsOrPrompt === "string"
206
+ ? optionsOrPrompt.length
207
+ : optionsOrPrompt.prompt.length,
208
+ });
209
+ }
186
210
  throw err; // Re-throw error to trigger fallback
187
211
  }
188
212
  }
@@ -200,7 +224,7 @@ export class GoogleAIStudio {
200
224
  const options = typeof optionsOrPrompt === "string"
201
225
  ? { prompt: optionsOrPrompt }
202
226
  : optionsOrPrompt;
203
- const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, tools, } = options;
227
+ const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, tools, timeout = getDefaultTimeout(provider, "generate"), } = options;
204
228
  // Use schema from options or fallback parameter
205
229
  const finalSchema = schema || analysisSchema;
206
230
  logger.debug(`[${functionTag}] Generate request started`, {
@@ -211,8 +235,11 @@ export class GoogleAIStudio {
211
235
  maxTokens,
212
236
  hasTools: !!tools,
213
237
  toolCount: tools ? Object.keys(tools).length : 0,
238
+ timeout,
214
239
  });
215
240
  const model = this.getModel();
241
+ // Create timeout controller if timeout is specified
242
+ const timeoutController = createTimeoutController(timeout, provider, "generate");
216
243
  const generateOptions = {
217
244
  model: model,
218
245
  prompt: prompt,
@@ -223,29 +250,53 @@ export class GoogleAIStudio {
223
250
  tools,
224
251
  maxSteps: 5, // Allow multiple steps for tool execution and response generation
225
252
  }), // Add tools if provided
253
+ // Add abort signal if available
254
+ ...(timeoutController && {
255
+ abortSignal: timeoutController.controller.signal,
256
+ }),
226
257
  };
227
258
  if (finalSchema) {
228
259
  generateOptions.experimental_output = Output.object({
229
260
  schema: finalSchema,
230
261
  });
231
262
  }
232
- const result = await generateText(generateOptions);
233
- logger.debug(`[${functionTag}] Generate text completed`, {
234
- provider,
235
- modelName: this.modelName,
236
- usage: result.usage,
237
- finishReason: result.finishReason,
238
- responseLength: result.text?.length || 0,
239
- });
240
- return result;
263
+ try {
264
+ const result = await generateText(generateOptions);
265
+ // Clean up timeout if successful
266
+ timeoutController?.cleanup();
267
+ logger.debug(`[${functionTag}] Generate text completed`, {
268
+ provider,
269
+ modelName: this.modelName,
270
+ usage: result.usage,
271
+ finishReason: result.finishReason,
272
+ responseLength: result.text?.length || 0,
273
+ timeout,
274
+ });
275
+ return result;
276
+ }
277
+ finally {
278
+ // Always cleanup timeout
279
+ timeoutController?.cleanup();
280
+ }
241
281
  }
242
282
  catch (err) {
243
- logger.error(`[${functionTag}] Exception`, {
244
- provider,
245
- modelName: this.modelName,
246
- message: "Error in generating text",
247
- err: String(err),
248
- });
283
+ // Log timeout errors specifically
284
+ if (err instanceof TimeoutError) {
285
+ logger.error(`[${functionTag}] Timeout error`, {
286
+ provider,
287
+ modelName: this.modelName,
288
+ timeout: err.timeout,
289
+ message: err.message,
290
+ });
291
+ }
292
+ else {
293
+ logger.error(`[${functionTag}] Exception`, {
294
+ provider,
295
+ modelName: this.modelName,
296
+ message: "Error in generating text",
297
+ err: String(err),
298
+ });
299
+ }
249
300
  throw err; // Re-throw error to trigger fallback
250
301
  }
251
302
  }