@ax-llm/ax 10.0.43 → 10.0.44

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/index.cjs CHANGED
@@ -58,6 +58,13 @@ __export(index_exports, {
58
58
  AxAIOpenAIModel: () => AxAIOpenAIModel,
59
59
  AxAIReka: () => AxAIReka,
60
60
  AxAIRekaModel: () => AxAIRekaModel,
61
+ AxAIServiceAuthenticationError: () => AxAIServiceAuthenticationError,
62
+ AxAIServiceError: () => AxAIServiceError,
63
+ AxAIServiceNetworkError: () => AxAIServiceNetworkError,
64
+ AxAIServiceResponseError: () => AxAIServiceResponseError,
65
+ AxAIServiceStatusError: () => AxAIServiceStatusError,
66
+ AxAIServiceStreamTerminatedError: () => AxAIServiceStreamTerminatedError,
67
+ AxAIServiceTimeoutError: () => AxAIServiceTimeoutError,
61
68
  AxAITogether: () => AxAITogether,
62
69
  AxAgent: () => AxAgent,
63
70
  AxApacheTika: () => AxApacheTika,
@@ -182,15 +189,21 @@ var import_api = require("@opentelemetry/api");
182
189
  // util/sse.ts
183
190
  var import_web = require("stream/web");
184
191
  var SSEParser = class extends import_web.TransformStream {
185
- constructor(dataParser = JSON.parse) {
192
+ buffer = "";
193
+ currentEvent = { rawData: "" };
194
+ dataParser;
195
+ onError;
196
+ constructor(options = {}) {
186
197
  super({
187
198
  transform: (chunk, controller) => this.handleChunk(chunk, controller),
188
199
  flush: (controller) => this.handleFlush(controller)
189
200
  });
190
- this.dataParser = dataParser;
201
+ this.dataParser = options.dataParser || JSON.parse;
202
+ this.onError = options.onError || ((error, rawData) => {
203
+ console.warn("Failed to parse event data:", error);
204
+ console.log("Raw data that failed to parse:", rawData);
205
+ });
191
206
  }
192
- buffer = "";
193
- currentEvent = { rawData: "" };
194
207
  handleChunk(chunk, controller) {
195
208
  this.buffer += chunk;
196
209
  this.processBuffer(controller);
@@ -198,24 +211,28 @@ var SSEParser = class extends import_web.TransformStream {
198
211
  handleFlush(controller) {
199
212
  this.processBuffer(controller);
200
213
  if (this.currentEvent.rawData) {
201
- this.emitEvent(controller);
214
+ this.processEvent(controller);
202
215
  }
203
216
  }
204
217
  processBuffer(controller) {
205
- const lines = this.buffer.split(/\r\n|\r|\n/);
218
+ const normalizedBuffer = this.buffer.replace(/\r\n|\r/g, "\n");
219
+ const lines = normalizedBuffer.split("\n");
206
220
  this.buffer = lines.pop() || "";
207
221
  for (const line of lines) {
208
- if (line.trim() === "") {
209
- this.emitEvent(controller);
222
+ if (line === "") {
223
+ this.processEvent(controller);
210
224
  } else {
211
225
  this.parseLine(line);
212
226
  }
213
227
  }
214
228
  }
215
229
  parseLine(line) {
230
+ if (line.startsWith(":")) {
231
+ return;
232
+ }
216
233
  const colonIndex = line.indexOf(":");
217
234
  if (colonIndex === -1) {
218
- this.currentEvent.rawData += this.currentEvent.rawData ? "\n" + line.trim() : line.trim();
235
+ this.currentEvent.rawData += (this.currentEvent.rawData && !this.currentEvent.rawData.endsWith("\n") ? "\n" : "") + line.trim();
219
236
  return;
220
237
  }
221
238
  const field = line.slice(0, colonIndex).trim();
@@ -225,7 +242,7 @@ var SSEParser = class extends import_web.TransformStream {
225
242
  this.currentEvent.event = value;
226
243
  break;
227
244
  case "data":
228
- this.currentEvent.rawData += this.currentEvent.rawData ? "\n" + value : value;
245
+ this.currentEvent.rawData += (this.currentEvent.rawData && !this.currentEvent.rawData.endsWith("\n") ? "\n" : "") + value;
229
246
  break;
230
247
  case "id":
231
248
  this.currentEvent.id = value;
@@ -239,21 +256,20 @@ var SSEParser = class extends import_web.TransformStream {
239
256
  }
240
257
  }
241
258
  }
242
- emitEvent(controller) {
259
+ processEvent(controller) {
243
260
  if (this.currentEvent.rawData) {
244
- if (this.currentEvent.rawData.trim() === "[DONE]" || this.currentEvent.rawData.trim().startsWith("[")) {
261
+ if (!this.currentEvent.event) {
262
+ this.currentEvent.event = "message";
263
+ }
264
+ if (this.currentEvent.rawData.trim() === "[DONE]") {
265
+ this.currentEvent = { rawData: "" };
245
266
  return;
246
- } else {
247
- try {
248
- const parsedData = this.dataParser(this.currentEvent.rawData);
249
- controller.enqueue(parsedData);
250
- } catch (e) {
251
- console.warn("Failed to parse event data:", e);
252
- console.log(
253
- "Raw data that failed to parse:",
254
- this.currentEvent.rawData
255
- );
256
- }
267
+ }
268
+ try {
269
+ const parsedData = this.dataParser(this.currentEvent.rawData);
270
+ controller.enqueue(parsedData);
271
+ } catch (e) {
272
+ this.onError(e, this.currentEvent.rawData);
257
273
  }
258
274
  this.currentEvent = { rawData: "" };
259
275
  }
@@ -290,52 +306,305 @@ var TextDecoderStreamPolyfill = class extends import_web2.TransformStream {
290
306
  };
291
307
 
292
308
  // util/apicall.ts
309
+ var defaultRetryConfig = {
310
+ maxRetries: 3,
311
+ initialDelayMs: 1e3,
312
+ maxDelayMs: 6e4,
313
+ // Increased to 60 seconds
314
+ backoffFactor: 2,
315
+ retryableStatusCodes: [500, 408, 429, 502, 503, 504]
316
+ };
317
+ var defaultTimeoutMs = 3e4;
293
318
  var textDecoderStream = import_web3.TextDecoderStream ?? TextDecoderStreamPolyfill;
319
+ var AxAIServiceError = class extends Error {
320
+ constructor(message, url, requestBody, context = {}) {
321
+ super(message);
322
+ this.url = url;
323
+ this.requestBody = requestBody;
324
+ this.context = context;
325
+ this.name = "AxAIServiceError";
326
+ this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
327
+ this.errorId = crypto.randomUUID();
328
+ }
329
+ timestamp;
330
+ errorId;
331
+ toString() {
332
+ return `${this.name} [${this.errorId}]: ${this.message}
333
+ Timestamp: ${this.timestamp}
334
+ URL: ${this.url}${this.requestBody ? `
335
+ Request Body: ${JSON.stringify(this.requestBody, null, 2)}` : ""}${this.context ? `
336
+ Context: ${JSON.stringify(this.context, null, 2)}` : ""}`;
337
+ }
338
+ toJSON() {
339
+ return {
340
+ name: this.name,
341
+ errorId: this.errorId,
342
+ message: this.message,
343
+ timestamp: this.timestamp,
344
+ url: this.url,
345
+ requestBody: this.requestBody,
346
+ context: this.context,
347
+ stack: this.stack
348
+ };
349
+ }
350
+ };
351
+ var AxAIServiceStatusError = class extends AxAIServiceError {
352
+ constructor(status, statusText, url, requestBody, context) {
353
+ super(`HTTP ${status} - ${statusText}`, url, requestBody, {
354
+ httpStatus: status,
355
+ httpStatusText: statusText,
356
+ ...context
357
+ });
358
+ this.status = status;
359
+ this.statusText = statusText;
360
+ this.name = "AxAIServiceStatusError";
361
+ }
362
+ };
363
+ var AxAIServiceNetworkError = class extends AxAIServiceError {
364
+ constructor(originalError, url, requestBody, context) {
365
+ super(`Network Error: ${originalError.message}`, url, requestBody, {
366
+ originalErrorName: originalError.name,
367
+ originalErrorStack: originalError.stack,
368
+ ...context
369
+ });
370
+ this.originalError = originalError;
371
+ this.name = "AxAIServiceNetworkError";
372
+ this.stack = originalError.stack;
373
+ }
374
+ };
375
+ var AxAIServiceResponseError = class extends AxAIServiceError {
376
+ constructor(message, url, requestBody, context) {
377
+ super(message, url, requestBody, context);
378
+ this.name = "AxAIServiceResponseError";
379
+ }
380
+ };
381
+ var AxAIServiceStreamTerminatedError = class extends AxAIServiceError {
382
+ constructor(url, requestBody, lastChunk, context) {
383
+ super("Stream terminated unexpectedly by remote host", url, requestBody, {
384
+ lastChunk,
385
+ ...context
386
+ });
387
+ this.lastChunk = lastChunk;
388
+ this.name = "AxAIServiceStreamTerminatedError";
389
+ }
390
+ };
391
+ var AxAIServiceTimeoutError = class extends AxAIServiceError {
392
+ constructor(url, timeoutMs, requestBody, context) {
393
+ super(`Request timeout after ${timeoutMs}ms`, url, requestBody, {
394
+ timeoutMs,
395
+ ...context
396
+ });
397
+ this.name = "AxAIServiceTimeoutError";
398
+ }
399
+ };
400
+ var AxAIServiceAuthenticationError = class extends AxAIServiceError {
401
+ constructor(url, requestBody, context) {
402
+ super("Authentication failed", url, requestBody, context);
403
+ this.name = "AxAIServiceAuthenticationError";
404
+ }
405
+ };
406
+ function calculateRetryDelay(attempt, config) {
407
+ const delay = Math.min(
408
+ config.maxDelayMs,
409
+ config.initialDelayMs * Math.pow(config.backoffFactor, attempt)
410
+ );
411
+ return delay * (0.75 + Math.random() * 0.5);
412
+ }
413
+ function createRequestMetrics() {
414
+ return {
415
+ startTime: Date.now(),
416
+ retryCount: 0
417
+ };
418
+ }
419
+ function updateRetryMetrics(metrics) {
420
+ metrics.retryCount++;
421
+ metrics.lastRetryTime = Date.now();
422
+ }
294
423
  var apiCall = async (api, json) => {
424
+ const retryConfig = { ...defaultRetryConfig, ...api.retry };
425
+ const timeoutMs = api.timeout ?? defaultTimeoutMs;
426
+ const metrics = createRequestMetrics();
295
427
  const baseUrl = new URL(process.env["PROXY"] ?? api.url);
296
428
  const apiPath = import_path.default.join(baseUrl.pathname, api.name ?? "/", baseUrl.search);
297
429
  const apiUrl = new URL(apiPath, baseUrl);
430
+ const requestId = crypto.randomUUID();
298
431
  if (api.span?.isRecording()) {
299
432
  api.span.setAttributes({
300
433
  "http.request.method": api.put ? "PUT" : "POST",
301
- "url.full": apiUrl.href
434
+ "url.full": apiUrl.href,
435
+ "request.id": requestId,
436
+ "request.startTime": metrics.startTime
302
437
  });
303
438
  }
304
- let res;
305
- try {
306
- res = await (api.fetch ?? fetch)(apiUrl, {
307
- method: api.put ? "PUT" : "POST",
308
- headers: {
309
- "Content-Type": "application/json",
310
- ...api.headers
311
- },
312
- body: JSON.stringify(json)
313
- });
314
- if (res.status >= 400) {
315
- const reqBody = JSON.stringify(json, null, 2);
316
- throw new Error(
317
- `API Request Error: ${res.status}, ${res.statusText}
318
- :Request Body: ${reqBody}`
319
- );
320
- }
321
- if (!api.stream) {
322
- const resJson = await res.json();
323
- return resJson;
324
- }
325
- if (!res.body) {
326
- throw new Error("Response body is null");
327
- }
328
- const st = res.body.pipeThrough(new textDecoderStream()).pipeThrough(new SSEParser());
329
- return st;
330
- } catch (e) {
331
- if (api.span?.isRecording()) {
332
- api.span.recordException(e);
439
+ let attempt = 0;
440
+ while (true) {
441
+ const controller = new AbortController();
442
+ let timeoutId = setTimeout(() => {
443
+ controller.abort("Request timeout");
444
+ }, timeoutMs);
445
+ try {
446
+ const res = await (api.fetch ?? fetch)(apiUrl, {
447
+ method: api.put ? "PUT" : "POST",
448
+ headers: {
449
+ "Content-Type": "application/json",
450
+ "X-Request-ID": requestId,
451
+ "X-Retry-Count": attempt.toString(),
452
+ ...api.headers
453
+ },
454
+ body: JSON.stringify(json),
455
+ signal: controller.signal
456
+ });
457
+ clearTimeout(timeoutId);
458
+ if (res.status === 401 || res.status === 403) {
459
+ throw new AxAIServiceAuthenticationError(apiUrl.href, json, { metrics });
460
+ }
461
+ if (res.status >= 400 && attempt < retryConfig.maxRetries && retryConfig.retryableStatusCodes.includes(res.status)) {
462
+ const delay = calculateRetryDelay(attempt, retryConfig);
463
+ attempt++;
464
+ updateRetryMetrics(metrics);
465
+ if (api.span?.isRecording()) {
466
+ api.span.addEvent("retry", {
467
+ attempt,
468
+ delay,
469
+ status: res.status,
470
+ "metrics.startTime": metrics.startTime,
471
+ "metrics.retryCount": metrics.retryCount,
472
+ "metrics.lastRetryTime": metrics.lastRetryTime
473
+ });
474
+ }
475
+ clearTimeout(timeoutId);
476
+ continue;
477
+ }
478
+ if (res.status >= 400) {
479
+ throw new AxAIServiceStatusError(
480
+ res.status,
481
+ res.statusText,
482
+ apiUrl.href,
483
+ json,
484
+ { metrics }
485
+ );
486
+ }
487
+ if (!api.stream) {
488
+ const resJson = await res.json();
489
+ if (api.span?.isRecording()) {
490
+ api.span.setAttributes({
491
+ "response.time": Date.now() - metrics.startTime,
492
+ "response.retries": metrics.retryCount
493
+ });
494
+ }
495
+ return resJson;
496
+ }
497
+ if (!res.body) {
498
+ throw new AxAIServiceResponseError(
499
+ "Response body is null",
500
+ apiUrl.href,
501
+ json,
502
+ { metrics }
503
+ );
504
+ }
505
+ let lastChunk;
506
+ let chunkCount = 0;
507
+ const trackingStream = new TransformStream({
508
+ transform(chunk, controller2) {
509
+ lastChunk = chunk;
510
+ chunkCount++;
511
+ metrics.streamChunks = chunkCount;
512
+ metrics.lastChunkTime = Date.now();
513
+ controller2.enqueue(chunk);
514
+ },
515
+ flush(controller2) {
516
+ if (api.span?.isRecording()) {
517
+ api.span.setAttributes({
518
+ "stream.chunks": chunkCount,
519
+ "stream.duration": Date.now() - metrics.startTime,
520
+ "response.retries": metrics.retryCount
521
+ });
522
+ }
523
+ controller2.terminate();
524
+ }
525
+ });
526
+ const wrappedStream = new import_web3.ReadableStream({
527
+ start(controller2) {
528
+ const reader = res.body.pipeThrough(new textDecoderStream()).pipeThrough(new SSEParser()).pipeThrough(trackingStream).getReader();
529
+ async function read() {
530
+ try {
531
+ while (true) {
532
+ const { done, value } = await reader.read();
533
+ if (done) {
534
+ controller2.close();
535
+ break;
536
+ } else {
537
+ controller2.enqueue(value);
538
+ }
539
+ }
540
+ } catch (e) {
541
+ const error = e;
542
+ const streamMetrics = {
543
+ ...metrics,
544
+ streamDuration: Date.now() - metrics.startTime
545
+ };
546
+ if (error.name === "AbortError" || error.message?.includes("aborted")) {
547
+ controller2.error(
548
+ new AxAIServiceStreamTerminatedError(
549
+ apiUrl.href,
550
+ json,
551
+ lastChunk,
552
+ { streamMetrics }
553
+ )
554
+ );
555
+ } else {
556
+ controller2.error(
557
+ new AxAIServiceResponseError(
558
+ `Stream processing error: ${error.message}`,
559
+ apiUrl.href,
560
+ json,
561
+ { streamMetrics }
562
+ )
563
+ );
564
+ }
565
+ } finally {
566
+ reader.releaseLock();
567
+ }
568
+ }
569
+ read();
570
+ }
571
+ });
572
+ return wrappedStream;
573
+ } catch (error) {
574
+ clearTimeout(timeoutId);
575
+ if (error instanceof Error && error.name === "AbortError") {
576
+ throw new AxAIServiceTimeoutError(apiUrl.href, timeoutMs, json, {
577
+ metrics
578
+ });
579
+ }
580
+ if (api.span?.isRecording()) {
581
+ api.span.recordException(error);
582
+ api.span.setAttributes({
583
+ "error.time": Date.now() - metrics.startTime,
584
+ "error.retries": metrics.retryCount
585
+ });
586
+ }
587
+ if (error instanceof AxAIServiceNetworkError && attempt < retryConfig.maxRetries) {
588
+ const delay = calculateRetryDelay(attempt, retryConfig);
589
+ attempt++;
590
+ updateRetryMetrics(metrics);
591
+ if (api.span?.isRecording()) {
592
+ api.span.addEvent("retry", {
593
+ attempt,
594
+ delay,
595
+ error: error.message,
596
+ "metrics.startTime": metrics.startTime,
597
+ "metrics.retryCount": metrics.retryCount,
598
+ "metrics.lastRetryTime": metrics.lastRetryTime
599
+ });
600
+ }
601
+ continue;
602
+ }
603
+ if (error instanceof AxAIServiceError) {
604
+ error.context["metrics"] = metrics;
605
+ }
606
+ throw error;
333
607
  }
334
- const reqBody = JSON.stringify(json, null, 2);
335
- throw new Error(
336
- `API Response Error: ${apiUrl.href}, ${e}
337
- Request Body: ${reqBody}`
338
- );
339
608
  }
340
609
  };
341
610
 
@@ -5897,6 +6166,25 @@ var AxBalancer = class _AxBalancer {
5897
6166
  try {
5898
6167
  return await this.currentService.chat(req, options);
5899
6168
  } catch (e) {
6169
+ if (!(e instanceof AxAIServiceError)) {
6170
+ throw e;
6171
+ }
6172
+ switch (e.constructor) {
6173
+ case AxAIServiceAuthenticationError:
6174
+ throw e;
6175
+ case AxAIServiceStatusError:
6176
+ break;
6177
+ case AxAIServiceNetworkError:
6178
+ break;
6179
+ case AxAIServiceResponseError:
6180
+ break;
6181
+ case AxAIServiceStreamTerminatedError:
6182
+ break;
6183
+ case AxAIServiceTimeoutError:
6184
+ break;
6185
+ default:
6186
+ throw e;
6187
+ }
5900
6188
  console.warn(`Service ${this.currentService.getName()} failed`);
5901
6189
  if (!this.getNextService()) {
5902
6190
  throw e;
@@ -7583,6 +7871,13 @@ var AxRAG = class extends AxChainOfThought {
7583
7871
  AxAIOpenAIModel,
7584
7872
  AxAIReka,
7585
7873
  AxAIRekaModel,
7874
+ AxAIServiceAuthenticationError,
7875
+ AxAIServiceError,
7876
+ AxAIServiceNetworkError,
7877
+ AxAIServiceResponseError,
7878
+ AxAIServiceStatusError,
7879
+ AxAIServiceStreamTerminatedError,
7880
+ AxAIServiceTimeoutError,
7586
7881
  AxAITogether,
7587
7882
  AxAgent,
7588
7883
  AxApacheTika,