@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
|
@@ -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
|
|
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
|
-
|
|
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(`[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(`[
|
|
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
|
-
|
|
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
|
-
|
|
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(`[
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(`[
|
|
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
|
-
|
|
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
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
}
|