@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.
- package/CHANGELOG.md +34 -7
- package/README.md +38 -34
- package/dist/cli/commands/config.d.ts +6 -6
- package/dist/cli/index.js +46 -35
- package/dist/core/types.d.ts +2 -0
- package/dist/lib/core/types.d.ts +2 -0
- package/dist/lib/mcp/plugins/filesystem-mcp.d.ts +1 -1
- package/dist/lib/neurolink.d.ts +2 -0
- package/dist/lib/neurolink.js +23 -2
- package/dist/lib/providers/agent-enhanced-provider.d.ts +1 -0
- package/dist/lib/providers/agent-enhanced-provider.js +115 -51
- package/dist/lib/providers/amazonBedrock.js +74 -24
- package/dist/lib/providers/anthropic.js +80 -16
- package/dist/lib/providers/azureOpenAI.js +77 -15
- package/dist/lib/providers/googleAIStudio.js +77 -26
- package/dist/lib/providers/googleVertexAI.js +77 -24
- package/dist/lib/providers/huggingFace.js +74 -26
- package/dist/lib/providers/mistralAI.js +74 -26
- package/dist/lib/providers/ollama.d.ts +1 -1
- package/dist/lib/providers/ollama.js +32 -10
- package/dist/lib/providers/openAI.js +71 -23
- package/dist/lib/providers/timeout-wrapper.d.ts +40 -0
- package/dist/lib/providers/timeout-wrapper.js +100 -0
- package/dist/lib/proxy/proxy-fetch.d.ts +18 -0
- package/dist/lib/proxy/proxy-fetch.js +64 -0
- package/dist/lib/utils/timeout.d.ts +69 -0
- package/dist/lib/utils/timeout.js +138 -0
- package/dist/mcp/plugins/filesystem-mcp.d.ts +1 -1
- package/dist/mcp/plugins/filesystem-mcp.js +1 -1
- package/dist/neurolink.d.ts +2 -0
- package/dist/neurolink.js +23 -2
- package/dist/providers/agent-enhanced-provider.d.ts +1 -0
- package/dist/providers/agent-enhanced-provider.js +115 -51
- package/dist/providers/amazonBedrock.js +74 -24
- package/dist/providers/anthropic.js +80 -16
- package/dist/providers/azureOpenAI.js +77 -15
- package/dist/providers/googleAIStudio.js +77 -26
- package/dist/providers/googleVertexAI.js +77 -24
- package/dist/providers/huggingFace.js +74 -26
- package/dist/providers/mistralAI.js +74 -26
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +32 -10
- package/dist/providers/openAI.js +71 -23
- package/dist/providers/timeout-wrapper.d.ts +40 -0
- package/dist/providers/timeout-wrapper.js +100 -0
- package/dist/proxy/proxy-fetch.d.ts +18 -0
- package/dist/proxy/proxy-fetch.js +64 -0
- package/dist/utils/timeout.d.ts +69 -0
- package/dist/utils/timeout.js +138 -0
- package/package.json +2 -1
|
@@ -9,6 +9,7 @@ import { anthropic } from "@ai-sdk/anthropic";
|
|
|
9
9
|
import { getToolsForCategory, } from "../agent/direct-tools.js";
|
|
10
10
|
import { UnifiedMCPSystem } from "../mcp/unified-mcp.js";
|
|
11
11
|
import { mcpLogger } from "../mcp/logging.js";
|
|
12
|
+
import { parseTimeout } from "../utils/timeout.js";
|
|
12
13
|
/**
|
|
13
14
|
* Agent-Enhanced Provider Class
|
|
14
15
|
* Provides AI generation with tool calling capabilities
|
|
@@ -65,14 +66,15 @@ export class AgentEnhancedProvider {
|
|
|
65
66
|
try {
|
|
66
67
|
mcpLogger.info("[AgentEnhancedProvider] Initializing MCP integration...");
|
|
67
68
|
this.mcpSystem = new UnifiedMCPSystem({
|
|
68
|
-
configPath: this.config.mcpDiscoveryOptions?.configFiles?.[0] ||
|
|
69
|
+
configPath: this.config.mcpDiscoveryOptions?.configFiles?.[0] ||
|
|
70
|
+
".mcp-config.json",
|
|
69
71
|
enableExternalServers: true,
|
|
70
72
|
enableInternalServers: true,
|
|
71
|
-
autoInitialize: false
|
|
73
|
+
autoInitialize: false,
|
|
72
74
|
});
|
|
73
75
|
// ADD TIMEOUT to prevent hanging forever
|
|
74
76
|
const initPromise = this.mcpSystem.initialize();
|
|
75
|
-
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error(
|
|
77
|
+
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("MCP initialization timeout after 15 seconds")), this.config.mcpInitTimeoutMs || 15000));
|
|
76
78
|
await Promise.race([initPromise, timeoutPromise]);
|
|
77
79
|
this.mcpInitialized = true;
|
|
78
80
|
mcpLogger.info("[AgentEnhancedProvider] MCP integration initialized successfully");
|
|
@@ -100,10 +102,13 @@ export class AgentEnhancedProvider {
|
|
|
100
102
|
return directTools;
|
|
101
103
|
}
|
|
102
104
|
// Get MCP tools if available
|
|
103
|
-
|
|
105
|
+
const mcpTools = {};
|
|
104
106
|
try {
|
|
105
107
|
// Skip if MCP failed to initialize or is still initializing
|
|
106
|
-
if (this.mcpInitFailed ||
|
|
108
|
+
if (this.mcpInitFailed ||
|
|
109
|
+
this.mcpInitializing ||
|
|
110
|
+
!this.mcpInitialized ||
|
|
111
|
+
!this.mcpSystem) {
|
|
107
112
|
return directTools;
|
|
108
113
|
}
|
|
109
114
|
const mcpToolInfos = await this.mcpSystem.listTools();
|
|
@@ -114,33 +119,51 @@ export class AgentEnhancedProvider {
|
|
|
114
119
|
description: toolInfo.description || `MCP tool: ${toolInfo.name}`,
|
|
115
120
|
parameters: toolInfo.inputSchema || {},
|
|
116
121
|
execute: async (args) => {
|
|
122
|
+
let timeoutId;
|
|
117
123
|
try {
|
|
124
|
+
// Create timeout controller for tool execution if configured
|
|
125
|
+
const toolTimeout = this.config.toolExecutionTimeout;
|
|
126
|
+
const toolAbortController = toolTimeout
|
|
127
|
+
? new AbortController()
|
|
128
|
+
: undefined;
|
|
129
|
+
if (toolAbortController && toolTimeout) {
|
|
130
|
+
const timeoutMs = typeof toolTimeout === "string"
|
|
131
|
+
? parseTimeout(toolTimeout)
|
|
132
|
+
: toolTimeout;
|
|
133
|
+
timeoutId = setTimeout(() => {
|
|
134
|
+
toolAbortController.abort();
|
|
135
|
+
}, timeoutMs);
|
|
136
|
+
}
|
|
118
137
|
const context = {
|
|
119
|
-
sessionId:
|
|
120
|
-
userId:
|
|
138
|
+
sessionId: "cli-session",
|
|
139
|
+
userId: "cli-user",
|
|
121
140
|
secureFS: {
|
|
122
141
|
readFile: async (path, encoding) => {
|
|
123
|
-
const fs = await import(
|
|
124
|
-
return encoding
|
|
142
|
+
const fs = await import("fs/promises");
|
|
143
|
+
return encoding
|
|
144
|
+
? fs.readFile(path, {
|
|
145
|
+
encoding: encoding,
|
|
146
|
+
})
|
|
147
|
+
: fs.readFile(path);
|
|
125
148
|
},
|
|
126
149
|
writeFile: async (path, content) => {
|
|
127
|
-
const fs = await import(
|
|
150
|
+
const fs = await import("fs/promises");
|
|
128
151
|
await fs.writeFile(path, content);
|
|
129
152
|
},
|
|
130
153
|
readdir: async (path) => {
|
|
131
|
-
const fs = await import(
|
|
154
|
+
const fs = await import("fs/promises");
|
|
132
155
|
return fs.readdir(path);
|
|
133
156
|
},
|
|
134
157
|
stat: async (path) => {
|
|
135
|
-
const fs = await import(
|
|
158
|
+
const fs = await import("fs/promises");
|
|
136
159
|
return fs.stat(path);
|
|
137
160
|
},
|
|
138
161
|
mkdir: async (path, options) => {
|
|
139
|
-
const fs = await import(
|
|
162
|
+
const fs = await import("fs/promises");
|
|
140
163
|
await fs.mkdir(path, options);
|
|
141
164
|
},
|
|
142
165
|
exists: async (path) => {
|
|
143
|
-
const fs = await import(
|
|
166
|
+
const fs = await import("fs/promises");
|
|
144
167
|
try {
|
|
145
168
|
await fs.access(path);
|
|
146
169
|
return true;
|
|
@@ -148,34 +171,34 @@ export class AgentEnhancedProvider {
|
|
|
148
171
|
catch {
|
|
149
172
|
return false;
|
|
150
173
|
}
|
|
151
|
-
}
|
|
174
|
+
},
|
|
152
175
|
},
|
|
153
176
|
path: {
|
|
154
177
|
join: (...paths) => {
|
|
155
|
-
const path = require(
|
|
178
|
+
const path = require("path");
|
|
156
179
|
return path.join(...paths);
|
|
157
180
|
},
|
|
158
181
|
resolve: (...paths) => {
|
|
159
|
-
const path = require(
|
|
182
|
+
const path = require("path");
|
|
160
183
|
return path.resolve(...paths);
|
|
161
184
|
},
|
|
162
185
|
relative: (from, to) => {
|
|
163
|
-
const path = require(
|
|
186
|
+
const path = require("path");
|
|
164
187
|
return path.relative(from, to);
|
|
165
188
|
},
|
|
166
189
|
dirname: (path) => {
|
|
167
|
-
const pathLib = require(
|
|
190
|
+
const pathLib = require("path");
|
|
168
191
|
return pathLib.dirname(path);
|
|
169
192
|
},
|
|
170
193
|
basename: (path, ext) => {
|
|
171
|
-
const pathLib = require(
|
|
194
|
+
const pathLib = require("path");
|
|
172
195
|
return pathLib.basename(path, ext);
|
|
173
|
-
}
|
|
196
|
+
},
|
|
174
197
|
},
|
|
175
|
-
grantedPermissions: [
|
|
198
|
+
grantedPermissions: ["read", "write", "execute"],
|
|
176
199
|
log: (level, message, data) => {
|
|
177
200
|
const logFn = mcpLogger[level];
|
|
178
|
-
if (typeof logFn ===
|
|
201
|
+
if (typeof logFn === "function") {
|
|
179
202
|
if (data) {
|
|
180
203
|
logFn(`${message} ${JSON.stringify(data)}`);
|
|
181
204
|
}
|
|
@@ -183,16 +206,39 @@ export class AgentEnhancedProvider {
|
|
|
183
206
|
logFn(message);
|
|
184
207
|
}
|
|
185
208
|
}
|
|
186
|
-
}
|
|
209
|
+
},
|
|
187
210
|
};
|
|
188
|
-
const
|
|
211
|
+
const toolPromise = this.mcpSystem.executeTool(toolInfo.name, args, context);
|
|
212
|
+
let result;
|
|
213
|
+
if (toolAbortController) {
|
|
214
|
+
// Race between tool execution and timeout
|
|
215
|
+
result = await Promise.race([
|
|
216
|
+
toolPromise,
|
|
217
|
+
new Promise((_, reject) => {
|
|
218
|
+
toolAbortController.signal.addEventListener("abort", () => {
|
|
219
|
+
reject(new Error(`Tool ${toolInfo.name} timed out after ${this.config.toolExecutionTimeout}`));
|
|
220
|
+
});
|
|
221
|
+
}),
|
|
222
|
+
]);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
result = await toolPromise;
|
|
226
|
+
}
|
|
227
|
+
// Clear timeout if successful
|
|
228
|
+
if (timeoutId) {
|
|
229
|
+
clearTimeout(timeoutId);
|
|
230
|
+
}
|
|
189
231
|
return result.data || result;
|
|
190
232
|
}
|
|
191
233
|
catch (error) {
|
|
234
|
+
// Clear timeout on error
|
|
235
|
+
if (timeoutId) {
|
|
236
|
+
clearTimeout(timeoutId);
|
|
237
|
+
}
|
|
192
238
|
mcpLogger.error(`MCP tool ${toolInfo.name} execution failed:`, error);
|
|
193
239
|
throw error;
|
|
194
240
|
}
|
|
195
|
-
}
|
|
241
|
+
},
|
|
196
242
|
};
|
|
197
243
|
}
|
|
198
244
|
mcpLogger.info(`[AgentEnhancedProvider] Loaded ${Object.keys(mcpTools).length} MCP tools`);
|
|
@@ -206,20 +252,26 @@ export class AgentEnhancedProvider {
|
|
|
206
252
|
const options = typeof optionsOrPrompt === "string"
|
|
207
253
|
? { prompt: optionsOrPrompt }
|
|
208
254
|
: optionsOrPrompt;
|
|
209
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, schema, } = options;
|
|
255
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, schema, timeout, } = options;
|
|
210
256
|
// Get combined tools (direct + MCP) if enabled
|
|
211
|
-
const tools = this.config.enableTools
|
|
212
|
-
? await this.getCombinedTools()
|
|
213
|
-
: {};
|
|
257
|
+
const tools = this.config.enableTools ? await this.getCombinedTools() : {};
|
|
214
258
|
const log = (msg, data) => {
|
|
215
|
-
mcpLogger.info(`[AgentEnhancedProvider] ${msg}`, data ? JSON.stringify(data, null, 2) :
|
|
259
|
+
mcpLogger.info(`[AgentEnhancedProvider] ${msg}`, data ? JSON.stringify(data, null, 2) : "");
|
|
216
260
|
};
|
|
217
|
-
log(
|
|
261
|
+
log("Starting text generation", {
|
|
218
262
|
prompt: prompt.substring(0, 100),
|
|
219
263
|
toolsCount: Object.keys(tools).length,
|
|
220
|
-
maxSteps: this.config.maxSteps
|
|
264
|
+
maxSteps: this.config.maxSteps,
|
|
221
265
|
});
|
|
222
266
|
try {
|
|
267
|
+
// Parse timeout if provided
|
|
268
|
+
let abortSignal;
|
|
269
|
+
if (timeout) {
|
|
270
|
+
const timeoutMs = typeof timeout === "string" ? parseTimeout(timeout) : timeout;
|
|
271
|
+
if (timeoutMs !== undefined) {
|
|
272
|
+
abortSignal = AbortSignal.timeout(timeoutMs);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
223
275
|
// The AI SDK with maxSteps automatically handles tool calling and result integration
|
|
224
276
|
const result = await generateText({
|
|
225
277
|
model: this.model,
|
|
@@ -231,25 +283,28 @@ export class AgentEnhancedProvider {
|
|
|
231
283
|
temperature,
|
|
232
284
|
maxTokens,
|
|
233
285
|
toolChoice: this.shouldForceToolUsage(prompt) ? "required" : "auto",
|
|
286
|
+
abortSignal, // Pass abort signal for timeout support
|
|
234
287
|
});
|
|
235
|
-
log(
|
|
288
|
+
log("Generation completed", {
|
|
236
289
|
text: result.text?.substring(0, 200),
|
|
237
290
|
finishReason: result.finishReason,
|
|
238
291
|
toolCallsCount: result.toolCalls?.length || 0,
|
|
239
292
|
toolResultsCount: result.toolResults?.length || 0,
|
|
240
|
-
stepsCount: result.steps?.length || 0
|
|
293
|
+
stepsCount: result.steps?.length || 0,
|
|
241
294
|
});
|
|
242
295
|
// Check if tools were called but no final text was generated
|
|
243
|
-
if (result.finishReason ===
|
|
244
|
-
|
|
296
|
+
if (result.finishReason === "tool-calls" &&
|
|
297
|
+
!result.text &&
|
|
298
|
+
result.toolResults?.length > 0) {
|
|
299
|
+
log("Tools called but no final text generated, creating summary response");
|
|
245
300
|
try {
|
|
246
301
|
// Extract tool results and create a summary prompt
|
|
247
|
-
let toolResultsSummary =
|
|
302
|
+
let toolResultsSummary = "";
|
|
248
303
|
if (result.toolResults) {
|
|
249
304
|
for (const toolResult of result.toolResults) {
|
|
250
305
|
const resultData = toolResult.result || toolResult;
|
|
251
306
|
// Try to extract meaningful data from the result
|
|
252
|
-
if (typeof resultData ===
|
|
307
|
+
if (typeof resultData === "object" && resultData !== null) {
|
|
253
308
|
if (resultData.success && resultData.items) {
|
|
254
309
|
// This looks like a filesystem listing
|
|
255
310
|
toolResultsSummary += `Directory listing for ${resultData.path}:\n`;
|
|
@@ -264,34 +319,36 @@ export class AgentEnhancedProvider {
|
|
|
264
319
|
else {
|
|
265
320
|
toolResultsSummary += String(resultData);
|
|
266
321
|
}
|
|
267
|
-
toolResultsSummary +=
|
|
322
|
+
toolResultsSummary += "\n\n";
|
|
268
323
|
}
|
|
269
324
|
}
|
|
270
|
-
log(
|
|
325
|
+
log("Tool results extracted", {
|
|
271
326
|
summaryLength: toolResultsSummary.length,
|
|
272
|
-
preview: toolResultsSummary.substring(0, 200)
|
|
327
|
+
preview: toolResultsSummary.substring(0, 200),
|
|
273
328
|
});
|
|
274
329
|
// Create a simple, direct summary
|
|
275
330
|
const finalText = `Based on the user request "${prompt}", here's what I found:\n\n${toolResultsSummary}`;
|
|
276
|
-
log(
|
|
331
|
+
log("Final text created", {
|
|
277
332
|
textLength: finalText.length,
|
|
278
|
-
preview: finalText.substring(0, 200)
|
|
333
|
+
preview: finalText.substring(0, 200),
|
|
279
334
|
});
|
|
280
335
|
// Return result with the formatted text
|
|
281
336
|
return {
|
|
282
337
|
...result,
|
|
283
338
|
text: finalText,
|
|
284
|
-
finishReason:
|
|
339
|
+
finishReason: "stop",
|
|
285
340
|
};
|
|
286
341
|
}
|
|
287
342
|
catch (error) {
|
|
288
|
-
log(
|
|
343
|
+
log("Error in summary generation", {
|
|
344
|
+
error: error instanceof Error ? error.message : String(error),
|
|
345
|
+
});
|
|
289
346
|
// Fallback: return raw tool results
|
|
290
347
|
const fallbackText = `Tool execution completed. Raw results: ${JSON.stringify(result.toolResults, null, 2)}`;
|
|
291
348
|
return {
|
|
292
349
|
...result,
|
|
293
350
|
text: fallbackText,
|
|
294
|
-
finishReason:
|
|
351
|
+
finishReason: "stop",
|
|
295
352
|
};
|
|
296
353
|
}
|
|
297
354
|
}
|
|
@@ -307,12 +364,18 @@ export class AgentEnhancedProvider {
|
|
|
307
364
|
const options = typeof optionsOrPrompt === "string"
|
|
308
365
|
? { prompt: optionsOrPrompt }
|
|
309
366
|
: optionsOrPrompt;
|
|
310
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, } = options;
|
|
367
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, timeout, } = options;
|
|
311
368
|
// Get combined tools (direct + MCP) if enabled
|
|
312
|
-
const tools = this.config.enableTools
|
|
313
|
-
? await this.getCombinedTools()
|
|
314
|
-
: {};
|
|
369
|
+
const tools = this.config.enableTools ? await this.getCombinedTools() : {};
|
|
315
370
|
try {
|
|
371
|
+
// Parse timeout if provided
|
|
372
|
+
let abortSignal;
|
|
373
|
+
if (timeout) {
|
|
374
|
+
const timeoutMs = typeof timeout === "string" ? parseTimeout(timeout) : timeout;
|
|
375
|
+
if (timeoutMs !== undefined) {
|
|
376
|
+
abortSignal = AbortSignal.timeout(timeoutMs);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
316
379
|
const result = await streamText({
|
|
317
380
|
model: this.model,
|
|
318
381
|
prompt: systemPrompt
|
|
@@ -323,6 +386,7 @@ export class AgentEnhancedProvider {
|
|
|
323
386
|
temperature,
|
|
324
387
|
maxTokens,
|
|
325
388
|
toolChoice: this.shouldForceToolUsage(prompt) ? "required" : "auto",
|
|
389
|
+
abortSignal, // Pass abort signal for timeout support
|
|
326
390
|
});
|
|
327
391
|
return result;
|
|
328
392
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAmazonBedrock } from "@ai-sdk/amazon-bedrock";
|
|
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";
|
|
4
5
|
// Default system context
|
|
5
6
|
const DEFAULT_SYSTEM_CONTEXT = {
|
|
6
7
|
systemPrompt: "You are a helpful AI assistant.",
|
|
@@ -128,7 +129,7 @@ export class AmazonBedrock {
|
|
|
128
129
|
const options = typeof optionsOrPrompt === "string"
|
|
129
130
|
? { prompt: optionsOrPrompt }
|
|
130
131
|
: optionsOrPrompt;
|
|
131
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
|
|
132
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout = getDefaultTimeout(provider, "stream"), } = options;
|
|
132
133
|
// Use schema from options or fallback parameter
|
|
133
134
|
const finalSchema = schema || analysisSchema;
|
|
134
135
|
logger.debug(`[${functionTag}] Stream request started`, {
|
|
@@ -137,13 +138,20 @@ export class AmazonBedrock {
|
|
|
137
138
|
promptLength: prompt.length,
|
|
138
139
|
temperature,
|
|
139
140
|
maxTokens,
|
|
141
|
+
timeout,
|
|
140
142
|
});
|
|
143
|
+
// Create timeout controller if timeout is specified
|
|
144
|
+
const timeoutController = createTimeoutController(timeout, provider, "stream");
|
|
141
145
|
const streamOptions = {
|
|
142
146
|
model: this.model,
|
|
143
147
|
prompt: prompt,
|
|
144
148
|
system: systemPrompt,
|
|
145
149
|
temperature,
|
|
146
150
|
maxTokens,
|
|
151
|
+
// Add abort signal if available
|
|
152
|
+
...(timeoutController && {
|
|
153
|
+
abortSignal: timeoutController.controller.signal,
|
|
154
|
+
}),
|
|
147
155
|
onError: (event) => {
|
|
148
156
|
const error = event.error;
|
|
149
157
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -193,16 +201,30 @@ export class AmazonBedrock {
|
|
|
193
201
|
modelName: this.modelName,
|
|
194
202
|
promptLength: prompt.length,
|
|
195
203
|
});
|
|
204
|
+
// For streaming, we can't clean up immediately, but the timeout will auto-clean
|
|
205
|
+
// The user should handle the stream and any timeout errors
|
|
196
206
|
return result;
|
|
197
207
|
}
|
|
198
208
|
catch (err) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
209
|
+
// Log timeout errors specifically
|
|
210
|
+
if (err instanceof TimeoutError) {
|
|
211
|
+
logger.error(`[${functionTag}] Timeout error`, {
|
|
212
|
+
provider,
|
|
213
|
+
modelName: this.modelName,
|
|
214
|
+
region: getAWSRegion(),
|
|
215
|
+
timeout: err.timeout,
|
|
216
|
+
message: err.message,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
logger.error(`[${functionTag}] Exception`, {
|
|
221
|
+
provider,
|
|
222
|
+
modelName: this.modelName,
|
|
223
|
+
region: getAWSRegion(),
|
|
224
|
+
message: "Error in streaming text",
|
|
225
|
+
err: String(err),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
206
228
|
throw err; // Re-throw error to trigger fallback
|
|
207
229
|
}
|
|
208
230
|
}
|
|
@@ -214,7 +236,7 @@ export class AmazonBedrock {
|
|
|
214
236
|
const options = typeof optionsOrPrompt === "string"
|
|
215
237
|
? { prompt: optionsOrPrompt }
|
|
216
238
|
: optionsOrPrompt;
|
|
217
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, } = options;
|
|
239
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt = DEFAULT_SYSTEM_CONTEXT.systemPrompt, schema, timeout = getDefaultTimeout(provider, "generate"), } = options;
|
|
218
240
|
// Use schema from options or fallback parameter
|
|
219
241
|
const finalSchema = schema || analysisSchema;
|
|
220
242
|
logger.debug(`[${functionTag}] Generate text started`, {
|
|
@@ -224,36 +246,64 @@ export class AmazonBedrock {
|
|
|
224
246
|
promptLength: prompt.length,
|
|
225
247
|
temperature,
|
|
226
248
|
maxTokens,
|
|
249
|
+
timeout,
|
|
227
250
|
});
|
|
251
|
+
// Create timeout controller if timeout is specified
|
|
252
|
+
const timeoutController = createTimeoutController(timeout, provider, "generate");
|
|
228
253
|
const generateOptions = {
|
|
229
254
|
model: this.model,
|
|
230
255
|
prompt: prompt,
|
|
231
256
|
system: systemPrompt,
|
|
232
257
|
temperature,
|
|
233
258
|
maxTokens,
|
|
259
|
+
// Add abort signal if available
|
|
260
|
+
...(timeoutController && {
|
|
261
|
+
abortSignal: timeoutController.controller.signal,
|
|
262
|
+
}),
|
|
234
263
|
};
|
|
235
264
|
if (finalSchema) {
|
|
236
265
|
generateOptions.experimental_output = Output.object({
|
|
237
266
|
schema: finalSchema,
|
|
238
267
|
});
|
|
239
268
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
269
|
+
try {
|
|
270
|
+
const result = await generateText(generateOptions);
|
|
271
|
+
// Clean up timeout if successful
|
|
272
|
+
timeoutController?.cleanup();
|
|
273
|
+
logger.debug(`[${functionTag}] Generate text completed`, {
|
|
274
|
+
provider,
|
|
275
|
+
modelName: this.modelName,
|
|
276
|
+
usage: result.usage,
|
|
277
|
+
finishReason: result.finishReason,
|
|
278
|
+
responseLength: result.text?.length || 0,
|
|
279
|
+
timeout,
|
|
280
|
+
});
|
|
281
|
+
return result;
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
// Always cleanup timeout
|
|
285
|
+
timeoutController?.cleanup();
|
|
286
|
+
}
|
|
249
287
|
}
|
|
250
288
|
catch (err) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
289
|
+
// Log timeout errors specifically
|
|
290
|
+
if (err instanceof TimeoutError) {
|
|
291
|
+
logger.error(`[${functionTag}] Timeout error`, {
|
|
292
|
+
provider,
|
|
293
|
+
modelName: this.modelName,
|
|
294
|
+
region: getAWSRegion(),
|
|
295
|
+
timeout: err.timeout,
|
|
296
|
+
message: err.message,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
logger.error(`[${functionTag}] Exception`, {
|
|
301
|
+
provider,
|
|
302
|
+
modelName: this.modelName,
|
|
303
|
+
message: "Error in generating text",
|
|
304
|
+
err: String(err),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
257
307
|
throw err; // Re-throw error to trigger fallback instead of returning null
|
|
258
308
|
}
|
|
259
309
|
}
|