@juspay/neurolink 9.67.1 → 9.67.2
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 +2 -0
- package/dist/browser/neurolink.min.js +342 -342
- package/dist/lib/providers/googleVertex.js +8 -7
- package/dist/lib/providers/litellm.d.ts +31 -24
- package/dist/lib/providers/litellm.js +590 -391
- package/dist/lib/providers/openaiChatCompletionsClient.d.ts +67 -0
- package/dist/lib/providers/openaiChatCompletionsClient.js +526 -0
- package/dist/lib/providers/openaiCompatible.js +6 -516
- package/dist/lib/types/providers.d.ts +2 -0
- package/dist/providers/googleVertex.js +8 -7
- package/dist/providers/litellm.d.ts +31 -24
- package/dist/providers/litellm.js +590 -391
- package/dist/providers/openaiChatCompletionsClient.d.ts +67 -0
- package/dist/providers/openaiChatCompletionsClient.js +525 -0
- package/dist/providers/openaiCompatible.js +6 -516
- package/dist/types/providers.d.ts +2 -0
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createParser } from "eventsource-parser";
|
|
2
1
|
import { BaseProvider } from "../core/baseProvider.js";
|
|
3
2
|
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
4
3
|
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
|
@@ -7,11 +6,11 @@ import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, Ra
|
|
|
7
6
|
import { logger } from "../utils/logger.js";
|
|
8
7
|
import { NoOutputGeneratedError } from "../utils/generationErrors.js";
|
|
9
8
|
import { buildNoOutputSentinel, stampNoOutputSpan, } from "../utils/noOutputSentinel.js";
|
|
10
|
-
import { convertZodToJsonSchema } from "../utils/schemaConversion.js";
|
|
11
9
|
import { composeAbortSignals, createTimeoutController, mergeAbortSignals, TimeoutError, } from "../utils/timeout.js";
|
|
12
10
|
import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
|
|
13
11
|
import { resolveToolChoice } from "../utils/toolChoice.js";
|
|
14
12
|
import { transformToolExecutions } from "../utils/transformationUtils.js";
|
|
13
|
+
import { buildAPIError, buildBody, buildToolsForOpenAI, createChunkQueue, createDeferredAnalytics, mapNeuroLinkToolChoice, mergeUsage, messageBuilderToOpenAI, parseSSEStream, stringifyToolOutput, stripTrailingSlash, v3ResponseFormatToOpenAI, v3ToolChoiceToOpenAI, v3ToolsToOpenAI, } from "./openaiChatCompletionsClient.js";
|
|
15
14
|
const FALLBACK_OPENAI_COMPATIBLE_MODEL = "gpt-3.5-turbo";
|
|
16
15
|
const getOpenAICompatibleConfig = () => {
|
|
17
16
|
const baseURL = process.env.OPENAI_COMPATIBLE_BASE_URL;
|
|
@@ -32,468 +31,12 @@ const getDefaultOpenAICompatibleModel = () => {
|
|
|
32
31
|
// =============================================================================
|
|
33
32
|
// Direct HTTP client for OpenAI chat-completions.
|
|
34
33
|
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// "@ai-sdk/provider" — the
|
|
34
|
+
// Wire-format converters, SSE parser, request builder, and error builder all
|
|
35
|
+
// live in ./openaiChatCompletionsClient.ts so providers that share the OpenAI
|
|
36
|
+
// chat-completions shape (litellm, etc.) can reuse them without duplication.
|
|
37
|
+
// Nothing in this module imports from "ai" or "@ai-sdk/provider" — the
|
|
38
|
+
// openai-compatible path is a clean cut.
|
|
39
39
|
// =============================================================================
|
|
40
|
-
const stripTrailingSlash = (s) => s.replace(/\/+$/, "");
|
|
41
|
-
const messageBuilderToOpenAI = (messages) => {
|
|
42
|
-
const out = [];
|
|
43
|
-
for (const msg of messages) {
|
|
44
|
-
switch (msg.role) {
|
|
45
|
-
case "system":
|
|
46
|
-
out.push({
|
|
47
|
-
role: "system",
|
|
48
|
-
content: typeof msg.content === "string"
|
|
49
|
-
? msg.content
|
|
50
|
-
: safeStringify(msg.content),
|
|
51
|
-
});
|
|
52
|
-
break;
|
|
53
|
-
case "user":
|
|
54
|
-
out.push({
|
|
55
|
-
role: "user",
|
|
56
|
-
content: convertContentForOpenAI(msg.content),
|
|
57
|
-
});
|
|
58
|
-
break;
|
|
59
|
-
case "assistant": {
|
|
60
|
-
const parts = Array.isArray(msg.content) ? msg.content : [msg.content];
|
|
61
|
-
const text = [];
|
|
62
|
-
const toolCalls = [];
|
|
63
|
-
for (const part of parts) {
|
|
64
|
-
if (part && typeof part === "object") {
|
|
65
|
-
const p = part;
|
|
66
|
-
if (p.type === "text") {
|
|
67
|
-
text.push({
|
|
68
|
-
type: "text",
|
|
69
|
-
text: part.text ?? "",
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
else if (p.type === "tool-call") {
|
|
73
|
-
const tc = part;
|
|
74
|
-
toolCalls.push({
|
|
75
|
-
id: tc.toolCallId ?? "",
|
|
76
|
-
type: "function",
|
|
77
|
-
function: {
|
|
78
|
-
name: tc.toolName ?? "",
|
|
79
|
-
arguments: stringifyToolInput(tc.input),
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
else if (typeof part === "string") {
|
|
85
|
-
text.push({ type: "text", text: part });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const flat = text.length === 0
|
|
89
|
-
? null
|
|
90
|
-
: text.length === 1 && text[0].type === "text"
|
|
91
|
-
? text[0].text
|
|
92
|
-
: text;
|
|
93
|
-
out.push({
|
|
94
|
-
role: "assistant",
|
|
95
|
-
content: flat,
|
|
96
|
-
...(toolCalls.length > 0 ? { tool_calls: toolCalls } : {}),
|
|
97
|
-
});
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
case "tool": {
|
|
101
|
-
// V3 tool messages carry `{ toolCallId, output }` per content[] entry,
|
|
102
|
-
// not at the top-level. Emit one OpenAI `role: "tool"` message per
|
|
103
|
-
// tool-result part so the model can correlate by tool_call_id.
|
|
104
|
-
if (Array.isArray(msg.content)) {
|
|
105
|
-
for (const part of msg.content) {
|
|
106
|
-
if (!part || typeof part !== "object") {
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
const p = part;
|
|
110
|
-
if (p.type === "tool-result") {
|
|
111
|
-
out.push({
|
|
112
|
-
role: "tool",
|
|
113
|
-
tool_call_id: p.toolCallId ?? "",
|
|
114
|
-
content: stringifyToolOutput(p.output),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
else if (typeof msg.content === "string") {
|
|
120
|
-
// Legacy / flat-string callers (not V3): forward as-is.
|
|
121
|
-
out.push({
|
|
122
|
-
role: "tool",
|
|
123
|
-
tool_call_id: msg.toolCallId ?? "",
|
|
124
|
-
content: msg.content,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
break;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return out;
|
|
132
|
-
};
|
|
133
|
-
const convertContentForOpenAI = (content) => {
|
|
134
|
-
if (typeof content === "string") {
|
|
135
|
-
return content;
|
|
136
|
-
}
|
|
137
|
-
if (!Array.isArray(content)) {
|
|
138
|
-
return safeStringify(content);
|
|
139
|
-
}
|
|
140
|
-
const out = [];
|
|
141
|
-
for (const part of content) {
|
|
142
|
-
if (typeof part === "string") {
|
|
143
|
-
out.push({ type: "text", text: part });
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (!part || typeof part !== "object") {
|
|
147
|
-
continue;
|
|
148
|
-
}
|
|
149
|
-
const p = part;
|
|
150
|
-
if (p.type === "text") {
|
|
151
|
-
out.push({
|
|
152
|
-
type: "text",
|
|
153
|
-
text: part.text ?? "",
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
else if (p.type === "image" || p.type === "image_url") {
|
|
157
|
-
const data = part.image ??
|
|
158
|
-
part.data ??
|
|
159
|
-
part.url;
|
|
160
|
-
const url = imageDataToURL(data);
|
|
161
|
-
if (url) {
|
|
162
|
-
out.push({ type: "image_url", image_url: { url } });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
if (out.length === 1 && out[0].type === "text") {
|
|
167
|
-
return out[0].text;
|
|
168
|
-
}
|
|
169
|
-
return out;
|
|
170
|
-
};
|
|
171
|
-
const imageDataToURL = (data) => {
|
|
172
|
-
if (typeof data === "string") {
|
|
173
|
-
if (data.startsWith("data:") || /^https?:\/\//i.test(data)) {
|
|
174
|
-
return data;
|
|
175
|
-
}
|
|
176
|
-
return `data:image/png;base64,${data}`;
|
|
177
|
-
}
|
|
178
|
-
if (data instanceof URL) {
|
|
179
|
-
return data.toString();
|
|
180
|
-
}
|
|
181
|
-
if (data instanceof Uint8Array) {
|
|
182
|
-
return `data:image/png;base64,${Buffer.from(data).toString("base64")}`;
|
|
183
|
-
}
|
|
184
|
-
return undefined;
|
|
185
|
-
};
|
|
186
|
-
const stringifyToolInput = (input) => {
|
|
187
|
-
if (typeof input === "string") {
|
|
188
|
-
return input;
|
|
189
|
-
}
|
|
190
|
-
try {
|
|
191
|
-
return JSON.stringify(input ?? {});
|
|
192
|
-
}
|
|
193
|
-
catch {
|
|
194
|
-
return "{}";
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
const safeStringify = (value) => {
|
|
198
|
-
try {
|
|
199
|
-
return JSON.stringify(value ?? "");
|
|
200
|
-
}
|
|
201
|
-
catch {
|
|
202
|
-
return String(value ?? "");
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
// V3 tool-result `output` is a tagged union ({type:"text"|"json"|...}).
|
|
206
|
-
// Serialize each variant the way an OpenAI-compatible endpoint expects
|
|
207
|
-
// to read it as the `content` of a `role: "tool"` message.
|
|
208
|
-
const stringifyToolOutput = (output) => {
|
|
209
|
-
if (output === null || output === undefined) {
|
|
210
|
-
return "";
|
|
211
|
-
}
|
|
212
|
-
if (typeof output === "string") {
|
|
213
|
-
return output;
|
|
214
|
-
}
|
|
215
|
-
if (typeof output !== "object") {
|
|
216
|
-
return String(output);
|
|
217
|
-
}
|
|
218
|
-
const o = output;
|
|
219
|
-
switch (o.type) {
|
|
220
|
-
case "text":
|
|
221
|
-
return typeof o.value === "string" ? o.value : safeStringify(o.value);
|
|
222
|
-
case "json":
|
|
223
|
-
return safeStringify(o.value);
|
|
224
|
-
case "execution-denied":
|
|
225
|
-
return `Tool execution denied${o.reason ? `: ${o.reason}` : ""}`;
|
|
226
|
-
case "error-text":
|
|
227
|
-
return typeof o.value === "string" ? o.value : safeStringify(o.value);
|
|
228
|
-
case "error-json":
|
|
229
|
-
return safeStringify(o.value);
|
|
230
|
-
case "content":
|
|
231
|
-
if (Array.isArray(o.value)) {
|
|
232
|
-
return o.value
|
|
233
|
-
.map((p) => {
|
|
234
|
-
if (p &&
|
|
235
|
-
typeof p === "object" &&
|
|
236
|
-
p.type === "text") {
|
|
237
|
-
return String(p.text ?? "");
|
|
238
|
-
}
|
|
239
|
-
return "";
|
|
240
|
-
})
|
|
241
|
-
.filter((s) => s.length > 0)
|
|
242
|
-
.join("\n");
|
|
243
|
-
}
|
|
244
|
-
return "";
|
|
245
|
-
default:
|
|
246
|
-
// Plain output object (not a V3 tagged union) — just stringify.
|
|
247
|
-
return safeStringify(output);
|
|
248
|
-
}
|
|
249
|
-
};
|
|
250
|
-
const buildToolsForOpenAI = (tools) => {
|
|
251
|
-
const entries = Object.entries(tools);
|
|
252
|
-
if (entries.length === 0) {
|
|
253
|
-
return undefined;
|
|
254
|
-
}
|
|
255
|
-
const out = [];
|
|
256
|
-
for (const [name, tool] of entries) {
|
|
257
|
-
const t = tool;
|
|
258
|
-
const rawSchema = t.inputSchema ?? t.parameters;
|
|
259
|
-
// tool.inputSchema may be a Zod schema, an AI SDK jsonSchema() wrapper,
|
|
260
|
-
// or plain JSON Schema — convertZodToJsonSchema normalizes all three.
|
|
261
|
-
// Sending raw Zod internals (with `_def`) gets rejected by most
|
|
262
|
-
// OpenAI-compatible endpoints.
|
|
263
|
-
const parameters = rawSchema
|
|
264
|
-
? convertZodToJsonSchema(rawSchema)
|
|
265
|
-
: { type: "object", properties: {} };
|
|
266
|
-
out.push({
|
|
267
|
-
type: "function",
|
|
268
|
-
function: {
|
|
269
|
-
name,
|
|
270
|
-
...(t.description ? { description: t.description } : {}),
|
|
271
|
-
parameters,
|
|
272
|
-
},
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
return out;
|
|
276
|
-
};
|
|
277
|
-
// V3 → OpenAI conversion helpers used by the non-streaming `doGenerate`
|
|
278
|
-
// path that BaseProvider's `generate()` still drives via the AI SDK's
|
|
279
|
-
// `generateText`. The streaming path doesn't need these — it consumes
|
|
280
|
-
// NeuroLink-shaped options directly.
|
|
281
|
-
const v3ToolsToOpenAI = (tools) => {
|
|
282
|
-
if (!tools || tools.length === 0) {
|
|
283
|
-
return undefined;
|
|
284
|
-
}
|
|
285
|
-
const out = [];
|
|
286
|
-
for (const t of tools) {
|
|
287
|
-
if (t.type === "function") {
|
|
288
|
-
out.push({
|
|
289
|
-
type: "function",
|
|
290
|
-
function: {
|
|
291
|
-
name: t.name,
|
|
292
|
-
...(t.description ? { description: t.description } : {}),
|
|
293
|
-
parameters: t.inputSchema,
|
|
294
|
-
...(t.strict !== undefined ? { strict: t.strict } : {}),
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
// provider-defined V3 tools are silently dropped here — they have no
|
|
299
|
-
// OpenAI chat-completions equivalent.
|
|
300
|
-
}
|
|
301
|
-
return out.length > 0 ? out : undefined;
|
|
302
|
-
};
|
|
303
|
-
const v3ToolChoiceToOpenAI = (choice) => {
|
|
304
|
-
switch (choice.type) {
|
|
305
|
-
case "auto":
|
|
306
|
-
case "none":
|
|
307
|
-
case "required":
|
|
308
|
-
return choice.type;
|
|
309
|
-
case "tool":
|
|
310
|
-
return { type: "function", function: { name: choice.toolName } };
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
const v3ResponseFormatToOpenAI = (rf) => {
|
|
314
|
-
if (rf.type === "text") {
|
|
315
|
-
return { type: "text" };
|
|
316
|
-
}
|
|
317
|
-
if (!rf.schema) {
|
|
318
|
-
return { type: "json_object" };
|
|
319
|
-
}
|
|
320
|
-
return {
|
|
321
|
-
type: "json_schema",
|
|
322
|
-
json_schema: {
|
|
323
|
-
name: rf.name ?? "response",
|
|
324
|
-
schema: rf.schema,
|
|
325
|
-
...(rf.description ? { description: rf.description } : {}),
|
|
326
|
-
strict: true,
|
|
327
|
-
},
|
|
328
|
-
};
|
|
329
|
-
};
|
|
330
|
-
const mapNeuroLinkToolChoice = (choice) => {
|
|
331
|
-
if (!choice) {
|
|
332
|
-
return undefined;
|
|
333
|
-
}
|
|
334
|
-
if (choice === "auto" || choice === "none" || choice === "required") {
|
|
335
|
-
return choice;
|
|
336
|
-
}
|
|
337
|
-
if (typeof choice === "object" && choice !== null) {
|
|
338
|
-
const c = choice;
|
|
339
|
-
if (c.type === "tool" && c.toolName) {
|
|
340
|
-
return { type: "function", function: { name: c.toolName } };
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return undefined;
|
|
344
|
-
};
|
|
345
|
-
const buildBody = (args) => {
|
|
346
|
-
const { modelId, messages, options, tools, toolChoice, streaming, responseFormat, } = args;
|
|
347
|
-
const body = {
|
|
348
|
-
model: modelId,
|
|
349
|
-
messages,
|
|
350
|
-
...(streaming ? { stream: true } : {}),
|
|
351
|
-
...(streaming ? { stream_options: { include_usage: true } } : {}),
|
|
352
|
-
};
|
|
353
|
-
if (options.maxTokens !== undefined && options.maxTokens !== null) {
|
|
354
|
-
body.max_tokens = options.maxTokens;
|
|
355
|
-
}
|
|
356
|
-
if (options.temperature !== undefined && options.temperature !== null) {
|
|
357
|
-
body.temperature = options.temperature;
|
|
358
|
-
}
|
|
359
|
-
if (options.topP !== undefined && options.topP !== null) {
|
|
360
|
-
body.top_p = options.topP;
|
|
361
|
-
}
|
|
362
|
-
if (options.presencePenalty !== undefined &&
|
|
363
|
-
options.presencePenalty !== null) {
|
|
364
|
-
body.presence_penalty = options.presencePenalty;
|
|
365
|
-
}
|
|
366
|
-
if (options.frequencyPenalty !== undefined &&
|
|
367
|
-
options.frequencyPenalty !== null) {
|
|
368
|
-
body.frequency_penalty = options.frequencyPenalty;
|
|
369
|
-
}
|
|
370
|
-
if (options.seed !== undefined && options.seed !== null) {
|
|
371
|
-
body.seed = options.seed;
|
|
372
|
-
}
|
|
373
|
-
if (options.stopSequences && options.stopSequences.length > 0) {
|
|
374
|
-
body.stop = options.stopSequences;
|
|
375
|
-
}
|
|
376
|
-
if (tools) {
|
|
377
|
-
body.tools = tools;
|
|
378
|
-
}
|
|
379
|
-
if (toolChoice !== undefined) {
|
|
380
|
-
body.tool_choice = toolChoice;
|
|
381
|
-
}
|
|
382
|
-
if (responseFormat) {
|
|
383
|
-
body.response_format = responseFormat;
|
|
384
|
-
}
|
|
385
|
-
return body;
|
|
386
|
-
};
|
|
387
|
-
const parseSSEStream = async (body, onTextDelta) => {
|
|
388
|
-
const result = {
|
|
389
|
-
text: "",
|
|
390
|
-
toolCalls: new Map(),
|
|
391
|
-
finishReason: null,
|
|
392
|
-
usage: undefined,
|
|
393
|
-
};
|
|
394
|
-
const decoder = new TextDecoder();
|
|
395
|
-
let parseErr;
|
|
396
|
-
const handleEvent = (msg) => {
|
|
397
|
-
const data = msg.data;
|
|
398
|
-
if (!data || data === "[DONE]") {
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
let chunk;
|
|
402
|
-
try {
|
|
403
|
-
chunk = JSON.parse(data);
|
|
404
|
-
}
|
|
405
|
-
catch (err) {
|
|
406
|
-
parseErr = err instanceof Error ? err : new Error(String(err));
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (chunk.usage) {
|
|
410
|
-
result.usage = chunk.usage;
|
|
411
|
-
}
|
|
412
|
-
const choice = chunk.choices?.[0];
|
|
413
|
-
if (!choice) {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
const delta = choice.delta;
|
|
417
|
-
if (delta?.content) {
|
|
418
|
-
result.text += delta.content;
|
|
419
|
-
onTextDelta(delta.content);
|
|
420
|
-
}
|
|
421
|
-
if (delta?.tool_calls) {
|
|
422
|
-
for (const tc of delta.tool_calls) {
|
|
423
|
-
let state = result.toolCalls.get(tc.index);
|
|
424
|
-
if (!state) {
|
|
425
|
-
state = {
|
|
426
|
-
id: tc.id ?? `call_${tc.index}_${Date.now()}`,
|
|
427
|
-
name: tc.function?.name ?? "",
|
|
428
|
-
argsBuffered: "",
|
|
429
|
-
};
|
|
430
|
-
result.toolCalls.set(tc.index, state);
|
|
431
|
-
}
|
|
432
|
-
else if (tc.id) {
|
|
433
|
-
state.id = tc.id;
|
|
434
|
-
}
|
|
435
|
-
if (tc.function?.name) {
|
|
436
|
-
state.name = tc.function.name;
|
|
437
|
-
}
|
|
438
|
-
if (tc.function?.arguments) {
|
|
439
|
-
state.argsBuffered += tc.function.arguments;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (choice.finish_reason) {
|
|
444
|
-
result.finishReason = choice.finish_reason;
|
|
445
|
-
}
|
|
446
|
-
};
|
|
447
|
-
const parser = createParser({ onEvent: handleEvent });
|
|
448
|
-
const reader = body.getReader();
|
|
449
|
-
try {
|
|
450
|
-
for (;;) {
|
|
451
|
-
const { done, value } = await reader.read();
|
|
452
|
-
if (done) {
|
|
453
|
-
break;
|
|
454
|
-
}
|
|
455
|
-
parser.feed(decoder.decode(value, { stream: true }));
|
|
456
|
-
}
|
|
457
|
-
parser.feed(decoder.decode());
|
|
458
|
-
}
|
|
459
|
-
finally {
|
|
460
|
-
reader.releaseLock();
|
|
461
|
-
}
|
|
462
|
-
if (parseErr) {
|
|
463
|
-
throw parseErr;
|
|
464
|
-
}
|
|
465
|
-
return result;
|
|
466
|
-
};
|
|
467
|
-
const buildAPIError = async (url, body, res) => {
|
|
468
|
-
let bodyText;
|
|
469
|
-
let parsed;
|
|
470
|
-
try {
|
|
471
|
-
bodyText = await res.text();
|
|
472
|
-
parsed = bodyText
|
|
473
|
-
? JSON.parse(bodyText)
|
|
474
|
-
: undefined;
|
|
475
|
-
}
|
|
476
|
-
catch {
|
|
477
|
-
parsed = undefined;
|
|
478
|
-
}
|
|
479
|
-
const msg = parsed?.error?.message ??
|
|
480
|
-
`OpenAI-compatible request failed with status ${res.status}`;
|
|
481
|
-
const err = new Error(msg);
|
|
482
|
-
err.statusCode = res.status;
|
|
483
|
-
err.url = url;
|
|
484
|
-
// Redacted summary only — never attach raw prompts, tool definitions, or
|
|
485
|
-
// tool arguments to the thrown error. Anything serialized by upstream
|
|
486
|
-
// logging would leak them otherwise.
|
|
487
|
-
err.requestBody = {
|
|
488
|
-
model: body.model,
|
|
489
|
-
stream: body.stream === true,
|
|
490
|
-
tool_count: body.tools?.length ?? 0,
|
|
491
|
-
};
|
|
492
|
-
if (bodyText !== undefined) {
|
|
493
|
-
err.responseBody = bodyText;
|
|
494
|
-
}
|
|
495
|
-
return err;
|
|
496
|
-
};
|
|
497
40
|
// =============================================================================
|
|
498
41
|
// Provider
|
|
499
42
|
// =============================================================================
|
|
@@ -1186,56 +729,3 @@ export class OpenAICompatibleProvider extends BaseProvider {
|
|
|
1186
729
|
];
|
|
1187
730
|
}
|
|
1188
731
|
}
|
|
1189
|
-
// Deferred-promise pair for `usage` and `finishReason` so the analytics
|
|
1190
|
-
// collector resolves with the actual aggregated values after the multi-step
|
|
1191
|
-
// loop ends, not the zeros they had at result-construction time.
|
|
1192
|
-
const createDeferredAnalytics = () => {
|
|
1193
|
-
let resolveUsage = () => { };
|
|
1194
|
-
const usagePromise = new Promise((r) => {
|
|
1195
|
-
resolveUsage = r;
|
|
1196
|
-
});
|
|
1197
|
-
let resolveFinish = () => { };
|
|
1198
|
-
const finishPromise = new Promise((r) => {
|
|
1199
|
-
resolveFinish = r;
|
|
1200
|
-
});
|
|
1201
|
-
return { usagePromise, finishPromise, resolveUsage, resolveFinish };
|
|
1202
|
-
};
|
|
1203
|
-
// Single-producer / single-consumer chunk queue. The streaming loop pushes
|
|
1204
|
-
// `{content}` deltas as they arrive from SSE and a final `{done:true}` when
|
|
1205
|
-
// it finishes; the consumer's AsyncIterable pulls from `nextChunk()`.
|
|
1206
|
-
const createChunkQueue = () => {
|
|
1207
|
-
const chunkQueue = [];
|
|
1208
|
-
let pendingResolve;
|
|
1209
|
-
const pushChunk = (c) => {
|
|
1210
|
-
if (pendingResolve) {
|
|
1211
|
-
const r = pendingResolve;
|
|
1212
|
-
pendingResolve = undefined;
|
|
1213
|
-
r(c);
|
|
1214
|
-
}
|
|
1215
|
-
else {
|
|
1216
|
-
chunkQueue.push(c);
|
|
1217
|
-
}
|
|
1218
|
-
};
|
|
1219
|
-
const nextChunk = () => new Promise((resolve) => {
|
|
1220
|
-
if (chunkQueue.length > 0) {
|
|
1221
|
-
resolve(chunkQueue.shift());
|
|
1222
|
-
}
|
|
1223
|
-
else {
|
|
1224
|
-
pendingResolve = resolve;
|
|
1225
|
-
}
|
|
1226
|
-
});
|
|
1227
|
-
return { pushChunk, nextChunk };
|
|
1228
|
-
};
|
|
1229
|
-
const mergeUsage = (a, b) => {
|
|
1230
|
-
if (!a) {
|
|
1231
|
-
return b;
|
|
1232
|
-
}
|
|
1233
|
-
if (!b) {
|
|
1234
|
-
return a;
|
|
1235
|
-
}
|
|
1236
|
-
return {
|
|
1237
|
-
prompt_tokens: (a.prompt_tokens ?? 0) + (b.prompt_tokens ?? 0),
|
|
1238
|
-
completion_tokens: (a.completion_tokens ?? 0) + (b.completion_tokens ?? 0),
|
|
1239
|
-
total_tokens: (a.total_tokens ?? 0) + (b.total_tokens ?? 0),
|
|
1240
|
-
};
|
|
1241
|
-
};
|
|
@@ -903,6 +903,8 @@ export type AnthropicVertexSettings = {
|
|
|
903
903
|
projectId: string;
|
|
904
904
|
/** Google Cloud region for Anthropic models (e.g., 'us-east5') */
|
|
905
905
|
region: string;
|
|
906
|
+
/** SDK request timeout in milliseconds */
|
|
907
|
+
timeout?: number;
|
|
906
908
|
};
|
|
907
909
|
/**
|
|
908
910
|
* OpenAI-compatible models endpoint response structure
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/neurolink",
|
|
3
|
-
"version": "9.67.
|
|
3
|
+
"version": "9.67.2",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
|
|
6
6
|
"author": {
|