@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.
- package/CHANGELOG.md +7 -0
- package/README.md +31 -5
- package/dist/cli/commands/config.d.ts +6 -6
- package/dist/cli/index.js +29 -30
- package/dist/core/types.d.ts +2 -0
- package/dist/lib/core/types.d.ts +2 -0
- 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 +59 -3
- package/dist/lib/providers/amazonBedrock.js +70 -24
- package/dist/lib/providers/anthropic.js +77 -15
- package/dist/lib/providers/azureOpenAI.js +77 -15
- package/dist/lib/providers/googleAIStudio.js +70 -26
- package/dist/lib/providers/googleVertexAI.js +70 -24
- package/dist/lib/providers/huggingFace.js +70 -26
- package/dist/lib/providers/mistralAI.js +70 -26
- package/dist/lib/providers/ollama.d.ts +1 -1
- package/dist/lib/providers/ollama.js +24 -10
- package/dist/lib/providers/openAI.js +67 -23
- package/dist/lib/providers/timeout-wrapper.d.ts +40 -0
- package/dist/lib/providers/timeout-wrapper.js +100 -0
- package/dist/lib/utils/timeout.d.ts +69 -0
- package/dist/lib/utils/timeout.js +130 -0
- 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 +59 -3
- package/dist/providers/amazonBedrock.js +70 -24
- package/dist/providers/anthropic.js +77 -15
- package/dist/providers/azureOpenAI.js +77 -15
- package/dist/providers/googleAIStudio.js +70 -26
- package/dist/providers/googleVertexAI.js +70 -24
- package/dist/providers/huggingFace.js +70 -26
- package/dist/providers/mistralAI.js +70 -26
- package/dist/providers/ollama.d.ts +1 -1
- package/dist/providers/ollama.js +24 -10
- package/dist/providers/openAI.js +67 -23
- package/dist/providers/timeout-wrapper.d.ts +40 -0
- package/dist/providers/timeout-wrapper.js +100 -0
- package/dist/utils/timeout.d.ts +69 -0
- package/dist/utils/timeout.js +130 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [2.1.0](https://github.com/juspay/neurolink/compare/v2.0.0...v2.1.0) (2025-06-29)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **timeout:** add comprehensive timeout support for all AI providers ([8610f4a](https://github.com/juspay/neurolink/commit/8610f4ade418345b0395ab72af6e675f6eec6f93))
|
|
7
|
+
|
|
1
8
|
# [2.0.0](https://github.com/juspay/neurolink/compare/v1.11.3...v2.0.0) (2025-06-28)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -61,6 +61,7 @@ import { createBestAIProvider } from "@juspay/neurolink";
|
|
|
61
61
|
const provider = createBestAIProvider();
|
|
62
62
|
const result = await provider.generateText({
|
|
63
63
|
prompt: "Write a haiku about programming",
|
|
64
|
+
timeout: '30s' // Optional: Set custom timeout (default: 30s)
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
console.log(result.text);
|
|
@@ -182,36 +183,61 @@ npx @juspay/neurolink gen "What time is it?"
|
|
|
182
183
|
# Disable tools for training-data-only responses
|
|
183
184
|
npx @juspay/neurolink generate "What time is it?" --disable-tools
|
|
184
185
|
|
|
186
|
+
# With custom timeout for complex prompts
|
|
187
|
+
npx @juspay/neurolink generate "Explain quantum computing in detail" --timeout 1m
|
|
188
|
+
|
|
185
189
|
# Real-time streaming with agent support (default)
|
|
186
190
|
npx @juspay/neurolink stream "What time is it?"
|
|
187
191
|
|
|
188
192
|
# Streaming without tools (traditional mode)
|
|
189
193
|
npx @juspay/neurolink stream "Tell me a story" --disable-tools
|
|
190
194
|
|
|
195
|
+
# Streaming with extended timeout
|
|
196
|
+
npx @juspay/neurolink stream "Write a long story" --timeout 5m
|
|
197
|
+
|
|
191
198
|
# Provider diagnostics
|
|
192
199
|
npx @juspay/neurolink status --verbose
|
|
193
200
|
|
|
194
201
|
# Batch processing
|
|
195
202
|
echo -e "Write a haiku\nExplain gravity" > prompts.txt
|
|
196
203
|
npx @juspay/neurolink batch prompts.txt --output results.json
|
|
204
|
+
|
|
205
|
+
# Batch with custom timeout per request
|
|
206
|
+
npx @juspay/neurolink batch prompts.txt --timeout 45s --output results.json
|
|
197
207
|
```
|
|
198
208
|
|
|
199
209
|
### SDK Integration
|
|
200
210
|
|
|
201
211
|
```typescript
|
|
202
|
-
// SvelteKit API route
|
|
212
|
+
// SvelteKit API route with timeout handling
|
|
203
213
|
export const POST: RequestHandler = async ({ request }) => {
|
|
204
214
|
const { message } = await request.json();
|
|
205
215
|
const provider = createBestAIProvider();
|
|
206
|
-
|
|
207
|
-
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const result = await provider.streamText({
|
|
219
|
+
prompt: message,
|
|
220
|
+
timeout: '2m' // 2 minutes for streaming
|
|
221
|
+
});
|
|
222
|
+
return new Response(result.toReadableStream());
|
|
223
|
+
} catch (error) {
|
|
224
|
+
if (error.name === 'TimeoutError') {
|
|
225
|
+
return new Response('Request timed out', { status: 408 });
|
|
226
|
+
}
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
208
229
|
};
|
|
209
230
|
|
|
210
|
-
// Next.js API route
|
|
231
|
+
// Next.js API route with timeout
|
|
211
232
|
export async function POST(request: NextRequest) {
|
|
212
233
|
const { prompt } = await request.json();
|
|
213
234
|
const provider = createBestAIProvider();
|
|
214
|
-
|
|
235
|
+
|
|
236
|
+
const result = await provider.generateText({
|
|
237
|
+
prompt,
|
|
238
|
+
timeout: process.env.AI_TIMEOUT || '30s' // Configurable timeout
|
|
239
|
+
});
|
|
240
|
+
|
|
215
241
|
return NextResponse.json({ text: result.text });
|
|
216
242
|
}
|
|
217
243
|
```
|
|
@@ -117,12 +117,12 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
117
117
|
model: z.ZodDefault<z.ZodString>;
|
|
118
118
|
timeout: z.ZodDefault<z.ZodNumber>;
|
|
119
119
|
}, "strip", z.ZodTypeAny, {
|
|
120
|
-
model: string;
|
|
121
120
|
timeout: number;
|
|
121
|
+
model: string;
|
|
122
122
|
baseUrl: string;
|
|
123
123
|
}, {
|
|
124
|
-
model?: string | undefined;
|
|
125
124
|
timeout?: number | undefined;
|
|
125
|
+
model?: string | undefined;
|
|
126
126
|
baseUrl?: string | undefined;
|
|
127
127
|
}>>;
|
|
128
128
|
mistral: z.ZodOptional<z.ZodObject<{
|
|
@@ -176,8 +176,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
176
176
|
apiKey?: string | undefined;
|
|
177
177
|
} | undefined;
|
|
178
178
|
ollama?: {
|
|
179
|
-
model: string;
|
|
180
179
|
timeout: number;
|
|
180
|
+
model: string;
|
|
181
181
|
baseUrl: string;
|
|
182
182
|
} | undefined;
|
|
183
183
|
mistral?: {
|
|
@@ -225,8 +225,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
225
225
|
apiKey?: string | undefined;
|
|
226
226
|
} | undefined;
|
|
227
227
|
ollama?: {
|
|
228
|
-
model?: string | undefined;
|
|
229
228
|
timeout?: number | undefined;
|
|
229
|
+
model?: string | undefined;
|
|
230
230
|
baseUrl?: string | undefined;
|
|
231
231
|
} | undefined;
|
|
232
232
|
mistral?: {
|
|
@@ -299,8 +299,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
299
299
|
apiKey?: string | undefined;
|
|
300
300
|
} | undefined;
|
|
301
301
|
ollama?: {
|
|
302
|
-
model: string;
|
|
303
302
|
timeout: number;
|
|
303
|
+
model: string;
|
|
304
304
|
baseUrl: string;
|
|
305
305
|
} | undefined;
|
|
306
306
|
mistral?: {
|
|
@@ -360,8 +360,8 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
360
360
|
apiKey?: string | undefined;
|
|
361
361
|
} | undefined;
|
|
362
362
|
ollama?: {
|
|
363
|
-
model?: string | undefined;
|
|
364
363
|
timeout?: number | undefined;
|
|
364
|
+
model?: string | undefined;
|
|
365
365
|
baseUrl?: string | undefined;
|
|
366
366
|
} | undefined;
|
|
367
367
|
mistral?: {
|
package/dist/cli/index.js
CHANGED
|
@@ -253,9 +253,9 @@ const cli = yargs(args)
|
|
|
253
253
|
description: "Enable debug mode with verbose output",
|
|
254
254
|
}) // Kept for potential specific debug logic
|
|
255
255
|
.option("timeout", {
|
|
256
|
-
type: "
|
|
257
|
-
default:
|
|
258
|
-
description: "Timeout for the request
|
|
256
|
+
type: "string",
|
|
257
|
+
default: "30s",
|
|
258
|
+
description: "Timeout for the request (e.g., 30s, 2m, 1h, 5000)",
|
|
259
259
|
})
|
|
260
260
|
.option("disable-tools", {
|
|
261
261
|
type: "boolean",
|
|
@@ -285,9 +285,8 @@ const cli = yargs(args)
|
|
|
285
285
|
? null
|
|
286
286
|
: ora("🤖 Generating text...").start();
|
|
287
287
|
try {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
});
|
|
288
|
+
// The SDK will handle the timeout internally, so we don't need this wrapper anymore
|
|
289
|
+
// Just pass the timeout to the SDK
|
|
291
290
|
// Use AgentEnhancedProvider when tools are enabled, otherwise use standard SDK
|
|
292
291
|
let generatePromise;
|
|
293
292
|
if (argv.disableTools === true) {
|
|
@@ -300,6 +299,7 @@ const cli = yargs(args)
|
|
|
300
299
|
temperature: argv.temperature,
|
|
301
300
|
maxTokens: argv.maxTokens,
|
|
302
301
|
systemPrompt: argv.system,
|
|
302
|
+
timeout: argv.timeout,
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
305
|
else {
|
|
@@ -323,10 +323,7 @@ const cli = yargs(args)
|
|
|
323
323
|
});
|
|
324
324
|
generatePromise = agentProvider.generateText(argv.prompt);
|
|
325
325
|
}
|
|
326
|
-
const result =
|
|
327
|
-
generatePromise,
|
|
328
|
-
timeoutPromise,
|
|
329
|
-
]));
|
|
326
|
+
const result = await generatePromise;
|
|
330
327
|
if (argv.format === "json" && originalConsole.log) {
|
|
331
328
|
Object.assign(console, originalConsole);
|
|
332
329
|
}
|
|
@@ -334,20 +331,20 @@ const cli = yargs(args)
|
|
|
334
331
|
spinner.succeed(chalk.green("✅ Text generated successfully!"));
|
|
335
332
|
}
|
|
336
333
|
// Handle both AgentEnhancedProvider (AI SDK) and standard NeuroLink SDK responses
|
|
337
|
-
const responseText = result.text || result.content || "";
|
|
338
|
-
const responseUsage = result.usage || {
|
|
334
|
+
const responseText = result ? (result.text || result.content || "") : "";
|
|
335
|
+
const responseUsage = result ? (result.usage || {
|
|
339
336
|
promptTokens: 0,
|
|
340
337
|
completionTokens: 0,
|
|
341
338
|
totalTokens: 0,
|
|
342
|
-
};
|
|
339
|
+
}) : { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
343
340
|
if (argv.format === "json") {
|
|
344
341
|
const jsonOutput = {
|
|
345
342
|
content: responseText,
|
|
346
|
-
provider: result.provider || argv.provider,
|
|
343
|
+
provider: result ? (result.provider || argv.provider) : argv.provider,
|
|
347
344
|
usage: responseUsage,
|
|
348
|
-
responseTime: result.responseTime || 0,
|
|
349
|
-
toolCalls: result.toolCalls || [],
|
|
350
|
-
toolResults: result.toolResults || [],
|
|
345
|
+
responseTime: result ? (result.responseTime || 0) : 0,
|
|
346
|
+
toolCalls: result ? (result.toolCalls || []) : [],
|
|
347
|
+
toolResults: result ? (result.toolResults || []) : [],
|
|
351
348
|
};
|
|
352
349
|
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + "\n");
|
|
353
350
|
}
|
|
@@ -357,7 +354,7 @@ const cli = yargs(args)
|
|
|
357
354
|
console.log("\n" + responseText + "\n");
|
|
358
355
|
}
|
|
359
356
|
// Show tool calls if any
|
|
360
|
-
if (result.toolCalls && result.toolCalls.length > 0) {
|
|
357
|
+
if (result && result.toolCalls && result.toolCalls.length > 0) {
|
|
361
358
|
console.log(chalk.blue("🔧 Tools Called:"));
|
|
362
359
|
for (const toolCall of result.toolCalls) {
|
|
363
360
|
console.log(`- ${toolCall.toolName}`);
|
|
@@ -366,7 +363,7 @@ const cli = yargs(args)
|
|
|
366
363
|
console.log();
|
|
367
364
|
}
|
|
368
365
|
// Show tool results if any
|
|
369
|
-
if (result.toolResults && result.toolResults.length > 0) {
|
|
366
|
+
if (result && result.toolResults && result.toolResults.length > 0) {
|
|
370
367
|
console.log(chalk.blue("📋 Tool Results:"));
|
|
371
368
|
for (const toolResult of result.toolResults) {
|
|
372
369
|
console.log(`- ${toolResult.toolCallId}`);
|
|
@@ -375,9 +372,9 @@ const cli = yargs(args)
|
|
|
375
372
|
console.log();
|
|
376
373
|
}
|
|
377
374
|
console.log(JSON.stringify({
|
|
378
|
-
provider: result.provider || argv.provider,
|
|
375
|
+
provider: result ? (result.provider || argv.provider) : argv.provider,
|
|
379
376
|
usage: responseUsage,
|
|
380
|
-
responseTime: result.responseTime || 0,
|
|
377
|
+
responseTime: result ? (result.responseTime || 0) : 0,
|
|
381
378
|
}, null, 2));
|
|
382
379
|
if (responseUsage.totalTokens) {
|
|
383
380
|
console.log(chalk.blue(`ℹ️ ${responseUsage.totalTokens} tokens used`));
|
|
@@ -436,6 +433,11 @@ const cli = yargs(args)
|
|
|
436
433
|
type: "number",
|
|
437
434
|
default: 0.7,
|
|
438
435
|
description: "Creativity level",
|
|
436
|
+
})
|
|
437
|
+
.option("timeout", {
|
|
438
|
+
type: "string",
|
|
439
|
+
default: "2m",
|
|
440
|
+
description: "Timeout for streaming (e.g., 30s, 2m, 1h)",
|
|
439
441
|
})
|
|
440
442
|
.option("debug", {
|
|
441
443
|
type: "boolean",
|
|
@@ -468,6 +470,7 @@ const cli = yargs(args)
|
|
|
468
470
|
? undefined
|
|
469
471
|
: argv.provider,
|
|
470
472
|
temperature: argv.temperature,
|
|
473
|
+
timeout: argv.timeout,
|
|
471
474
|
});
|
|
472
475
|
}
|
|
473
476
|
else {
|
|
@@ -552,9 +555,9 @@ const cli = yargs(args)
|
|
|
552
555
|
description: "AI provider to use",
|
|
553
556
|
})
|
|
554
557
|
.option("timeout", {
|
|
555
|
-
type: "
|
|
556
|
-
default:
|
|
557
|
-
description: "Timeout for each request
|
|
558
|
+
type: "string",
|
|
559
|
+
default: "30s",
|
|
560
|
+
description: "Timeout for each request (e.g., 30s, 2m, 1h)",
|
|
558
561
|
})
|
|
559
562
|
.option("temperature", {
|
|
560
563
|
type: "number",
|
|
@@ -607,8 +610,7 @@ const cli = yargs(args)
|
|
|
607
610
|
spinner.text = `Processing ${i + 1}/${prompts.length}: ${prompts[i].substring(0, 30)}...`;
|
|
608
611
|
}
|
|
609
612
|
try {
|
|
610
|
-
const
|
|
611
|
-
const generatePromise = sdk.generateText({
|
|
613
|
+
const result = await sdk.generateText({
|
|
612
614
|
prompt: prompts[i],
|
|
613
615
|
provider: argv.provider === "auto"
|
|
614
616
|
? undefined
|
|
@@ -616,11 +618,8 @@ const cli = yargs(args)
|
|
|
616
618
|
temperature: argv.temperature,
|
|
617
619
|
maxTokens: argv.maxTokens,
|
|
618
620
|
systemPrompt: argv.system,
|
|
621
|
+
timeout: argv.timeout,
|
|
619
622
|
});
|
|
620
|
-
const result = (await Promise.race([
|
|
621
|
-
generatePromise,
|
|
622
|
-
timeoutPromise,
|
|
623
|
-
]));
|
|
624
623
|
results.push({ prompt: prompts[i], response: result.content });
|
|
625
624
|
if (spinner) {
|
|
626
625
|
spinner.render();
|
package/dist/core/types.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ export interface TextGenerationOptions {
|
|
|
80
80
|
systemPrompt?: string;
|
|
81
81
|
schema?: ZodType<unknown, ZodTypeDef, unknown> | Schema<unknown>;
|
|
82
82
|
tools?: Record<string, Tool>;
|
|
83
|
+
timeout?: number | string;
|
|
83
84
|
}
|
|
84
85
|
/**
|
|
85
86
|
* Stream text options interface
|
|
@@ -92,6 +93,7 @@ export interface StreamTextOptions {
|
|
|
92
93
|
systemPrompt?: string;
|
|
93
94
|
schema?: ZodType<unknown, ZodTypeDef, unknown> | Schema<unknown>;
|
|
94
95
|
tools?: Record<string, Tool>;
|
|
96
|
+
timeout?: number | string;
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
99
|
* AI Provider interface with flexible parameter support
|
package/dist/lib/core/types.d.ts
CHANGED
|
@@ -80,6 +80,7 @@ export interface TextGenerationOptions {
|
|
|
80
80
|
systemPrompt?: string;
|
|
81
81
|
schema?: ZodType<unknown, ZodTypeDef, unknown> | Schema<unknown>;
|
|
82
82
|
tools?: Record<string, Tool>;
|
|
83
|
+
timeout?: number | string;
|
|
83
84
|
}
|
|
84
85
|
/**
|
|
85
86
|
* Stream text options interface
|
|
@@ -92,6 +93,7 @@ export interface StreamTextOptions {
|
|
|
92
93
|
systemPrompt?: string;
|
|
93
94
|
schema?: ZodType<unknown, ZodTypeDef, unknown> | Schema<unknown>;
|
|
94
95
|
tools?: Record<string, Tool>;
|
|
96
|
+
timeout?: number | string;
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
99
|
* AI Provider interface with flexible parameter support
|
package/dist/lib/neurolink.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/neurolink.js
CHANGED
|
@@ -11,6 +11,7 @@ import { toolRegistry } from "./mcp/tool-registry.js";
|
|
|
11
11
|
import { unifiedRegistry } from "./mcp/unified-registry.js";
|
|
12
12
|
import { logger } from "./utils/logger.js";
|
|
13
13
|
import { getBestProvider } from "./utils/providerUtils-fixed.js";
|
|
14
|
+
import { TimeoutError } from "./utils/timeout.js";
|
|
14
15
|
export class NeuroLink {
|
|
15
16
|
mcpInitialized = false;
|
|
16
17
|
contextManager;
|
|
@@ -32,10 +33,9 @@ export class NeuroLink {
|
|
|
32
33
|
const mcpInitPromise = Promise.race([
|
|
33
34
|
this.doIsolatedMCPInitialization(),
|
|
34
35
|
new Promise((_, reject) => {
|
|
35
|
-
|
|
36
|
+
setTimeout(() => {
|
|
36
37
|
reject(new Error("MCP initialization timeout after 3s"));
|
|
37
38
|
}, initTimeout);
|
|
38
|
-
timer.unref(); // Don't keep process alive
|
|
39
39
|
}),
|
|
40
40
|
]);
|
|
41
41
|
await mcpInitPromise;
|
|
@@ -134,6 +134,7 @@ export class NeuroLink {
|
|
|
134
134
|
temperature: options.temperature,
|
|
135
135
|
maxTokens: options.maxTokens,
|
|
136
136
|
systemPrompt: enhancedSystemPrompt,
|
|
137
|
+
timeout: options.timeout,
|
|
137
138
|
}, options.schema);
|
|
138
139
|
if (!result) {
|
|
139
140
|
throw new Error("No response received from AI provider");
|
|
@@ -222,6 +223,7 @@ export class NeuroLink {
|
|
|
222
223
|
temperature: options.temperature,
|
|
223
224
|
maxTokens: options.maxTokens,
|
|
224
225
|
systemPrompt: options.systemPrompt,
|
|
226
|
+
timeout: options.timeout,
|
|
225
227
|
}, options.schema);
|
|
226
228
|
if (!result) {
|
|
227
229
|
throw new Error("No response received from AI provider");
|
|
@@ -252,9 +254,18 @@ export class NeuroLink {
|
|
|
252
254
|
catch (error) {
|
|
253
255
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
254
256
|
lastError = error instanceof Error ? error : new Error(errorMessage);
|
|
257
|
+
// Special handling for timeout errors
|
|
258
|
+
if (error instanceof TimeoutError) {
|
|
259
|
+
logger.warn(`[${functionTag}] Provider timed out`, {
|
|
260
|
+
provider: providerName,
|
|
261
|
+
timeout: error.timeout,
|
|
262
|
+
operation: error.operation,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
255
265
|
logger.debug(`[${functionTag}] Provider failed, trying next`, {
|
|
256
266
|
provider: providerName,
|
|
257
267
|
error: errorMessage,
|
|
268
|
+
isTimeout: error instanceof TimeoutError,
|
|
258
269
|
remainingProviders: tryProviders.slice(tryProviders.indexOf(providerName) + 1),
|
|
259
270
|
});
|
|
260
271
|
// Continue to next provider
|
|
@@ -338,6 +349,7 @@ Note: Tool integration is currently in development. Please provide helpful respo
|
|
|
338
349
|
temperature: options.temperature,
|
|
339
350
|
maxTokens: options.maxTokens,
|
|
340
351
|
systemPrompt: options.systemPrompt,
|
|
352
|
+
timeout: options.timeout,
|
|
341
353
|
});
|
|
342
354
|
if (!result) {
|
|
343
355
|
throw new Error("No stream response received from AI provider");
|
|
@@ -358,9 +370,18 @@ Note: Tool integration is currently in development. Please provide helpful respo
|
|
|
358
370
|
catch (error) {
|
|
359
371
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
360
372
|
lastError = error instanceof Error ? error : new Error(errorMessage);
|
|
373
|
+
// Special handling for timeout errors
|
|
374
|
+
if (error instanceof TimeoutError) {
|
|
375
|
+
logger.warn(`[${functionTag}] Provider timed out`, {
|
|
376
|
+
provider: providerName,
|
|
377
|
+
timeout: error.timeout,
|
|
378
|
+
operation: error.operation,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
361
381
|
logger.debug(`[${functionTag}] Provider failed, trying next`, {
|
|
362
382
|
provider: providerName,
|
|
363
383
|
error: errorMessage,
|
|
384
|
+
isTimeout: error instanceof TimeoutError,
|
|
364
385
|
remainingProviders: tryProviders.slice(tryProviders.indexOf(providerName) + 1),
|
|
365
386
|
});
|
|
366
387
|
// Continue to next provider
|
|
@@ -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
|
|
@@ -114,7 +115,21 @@ export class AgentEnhancedProvider {
|
|
|
114
115
|
description: toolInfo.description || `MCP tool: ${toolInfo.name}`,
|
|
115
116
|
parameters: toolInfo.inputSchema || {},
|
|
116
117
|
execute: async (args) => {
|
|
118
|
+
let timeoutId;
|
|
117
119
|
try {
|
|
120
|
+
// Create timeout controller for tool execution if configured
|
|
121
|
+
const toolTimeout = this.config.toolExecutionTimeout;
|
|
122
|
+
const toolAbortController = toolTimeout
|
|
123
|
+
? new AbortController()
|
|
124
|
+
: undefined;
|
|
125
|
+
if (toolAbortController && toolTimeout) {
|
|
126
|
+
const timeoutMs = typeof toolTimeout === 'string'
|
|
127
|
+
? parseTimeout(toolTimeout)
|
|
128
|
+
: toolTimeout;
|
|
129
|
+
timeoutId = setTimeout(() => {
|
|
130
|
+
toolAbortController.abort();
|
|
131
|
+
}, timeoutMs);
|
|
132
|
+
}
|
|
118
133
|
const context = {
|
|
119
134
|
sessionId: 'cli-session',
|
|
120
135
|
userId: 'cli-user',
|
|
@@ -185,10 +200,33 @@ export class AgentEnhancedProvider {
|
|
|
185
200
|
}
|
|
186
201
|
}
|
|
187
202
|
};
|
|
188
|
-
const
|
|
203
|
+
const toolPromise = this.mcpSystem.executeTool(toolInfo.name, args, context);
|
|
204
|
+
let result;
|
|
205
|
+
if (toolAbortController) {
|
|
206
|
+
// Race between tool execution and timeout
|
|
207
|
+
result = await Promise.race([
|
|
208
|
+
toolPromise,
|
|
209
|
+
new Promise((_, reject) => {
|
|
210
|
+
toolAbortController.signal.addEventListener('abort', () => {
|
|
211
|
+
reject(new Error(`Tool ${toolInfo.name} timed out after ${this.config.toolExecutionTimeout}`));
|
|
212
|
+
});
|
|
213
|
+
})
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
result = await toolPromise;
|
|
218
|
+
}
|
|
219
|
+
// Clear timeout if successful
|
|
220
|
+
if (timeoutId) {
|
|
221
|
+
clearTimeout(timeoutId);
|
|
222
|
+
}
|
|
189
223
|
return result.data || result;
|
|
190
224
|
}
|
|
191
225
|
catch (error) {
|
|
226
|
+
// Clear timeout on error
|
|
227
|
+
if (timeoutId) {
|
|
228
|
+
clearTimeout(timeoutId);
|
|
229
|
+
}
|
|
192
230
|
mcpLogger.error(`MCP tool ${toolInfo.name} execution failed:`, error);
|
|
193
231
|
throw error;
|
|
194
232
|
}
|
|
@@ -206,7 +244,7 @@ export class AgentEnhancedProvider {
|
|
|
206
244
|
const options = typeof optionsOrPrompt === "string"
|
|
207
245
|
? { prompt: optionsOrPrompt }
|
|
208
246
|
: optionsOrPrompt;
|
|
209
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, schema, } = options;
|
|
247
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, schema, timeout, } = options;
|
|
210
248
|
// Get combined tools (direct + MCP) if enabled
|
|
211
249
|
const tools = this.config.enableTools
|
|
212
250
|
? await this.getCombinedTools()
|
|
@@ -220,6 +258,14 @@ export class AgentEnhancedProvider {
|
|
|
220
258
|
maxSteps: this.config.maxSteps
|
|
221
259
|
});
|
|
222
260
|
try {
|
|
261
|
+
// Parse timeout if provided
|
|
262
|
+
let abortSignal;
|
|
263
|
+
if (timeout) {
|
|
264
|
+
const timeoutMs = typeof timeout === 'string' ? parseTimeout(timeout) : timeout;
|
|
265
|
+
if (timeoutMs !== undefined) {
|
|
266
|
+
abortSignal = AbortSignal.timeout(timeoutMs);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
223
269
|
// The AI SDK with maxSteps automatically handles tool calling and result integration
|
|
224
270
|
const result = await generateText({
|
|
225
271
|
model: this.model,
|
|
@@ -231,6 +277,7 @@ export class AgentEnhancedProvider {
|
|
|
231
277
|
temperature,
|
|
232
278
|
maxTokens,
|
|
233
279
|
toolChoice: this.shouldForceToolUsage(prompt) ? "required" : "auto",
|
|
280
|
+
abortSignal, // Pass abort signal for timeout support
|
|
234
281
|
});
|
|
235
282
|
log('Generation completed', {
|
|
236
283
|
text: result.text?.substring(0, 200),
|
|
@@ -307,12 +354,20 @@ export class AgentEnhancedProvider {
|
|
|
307
354
|
const options = typeof optionsOrPrompt === "string"
|
|
308
355
|
? { prompt: optionsOrPrompt }
|
|
309
356
|
: optionsOrPrompt;
|
|
310
|
-
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, } = options;
|
|
357
|
+
const { prompt, temperature = 0.7, maxTokens = 1000, systemPrompt, timeout, } = options;
|
|
311
358
|
// Get combined tools (direct + MCP) if enabled
|
|
312
359
|
const tools = this.config.enableTools
|
|
313
360
|
? await this.getCombinedTools()
|
|
314
361
|
: {};
|
|
315
362
|
try {
|
|
363
|
+
// Parse timeout if provided
|
|
364
|
+
let abortSignal;
|
|
365
|
+
if (timeout) {
|
|
366
|
+
const timeoutMs = typeof timeout === 'string' ? parseTimeout(timeout) : timeout;
|
|
367
|
+
if (timeoutMs !== undefined) {
|
|
368
|
+
abortSignal = AbortSignal.timeout(timeoutMs);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
316
371
|
const result = await streamText({
|
|
317
372
|
model: this.model,
|
|
318
373
|
prompt: systemPrompt
|
|
@@ -323,6 +378,7 @@ export class AgentEnhancedProvider {
|
|
|
323
378
|
temperature,
|
|
324
379
|
maxTokens,
|
|
325
380
|
toolChoice: this.shouldForceToolUsage(prompt) ? "required" : "auto",
|
|
381
|
+
abortSignal, // Pass abort signal for timeout support
|
|
326
382
|
});
|
|
327
383
|
return result;
|
|
328
384
|
}
|