@ax-llm/ax 10.0.46 → 10.0.47

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
@@ -182,7 +182,6 @@ var AxSpanKindValues = /* @__PURE__ */ ((AxSpanKindValues2) => {
182
182
  })(AxSpanKindValues || {});
183
183
 
184
184
  // util/apicall.ts
185
- var import_path = __toESM(require("path"), 1);
186
185
  var import_web3 = require("stream/web");
187
186
  var import_api = require("@opentelemetry/api");
188
187
 
@@ -310,7 +309,6 @@ var defaultRetryConfig = {
310
309
  maxRetries: 3,
311
310
  initialDelayMs: 1e3,
312
311
  maxDelayMs: 6e4,
313
- // Increased to 60 seconds
314
312
  backoffFactor: 2,
315
313
  retryableStatusCodes: [500, 408, 429, 502, 503, 504]
316
314
  };
@@ -321,18 +319,19 @@ var AxAIServiceError = class extends Error {
321
319
  super(message);
322
320
  this.url = url;
323
321
  this.requestBody = requestBody;
324
- this.context = context;
325
322
  this.name = "AxAIServiceError";
326
323
  this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
327
324
  this.errorId = crypto.randomUUID();
325
+ this.context = context;
328
326
  }
329
327
  timestamp;
330
328
  errorId;
329
+ context;
331
330
  toString() {
332
331
  return `${this.name} [${this.errorId}]: ${this.message}
333
332
  Timestamp: ${this.timestamp}
334
333
  URL: ${this.url}${this.requestBody ? `
335
- Request Body: ${JSON.stringify(this.requestBody, null, 2)}` : ""}${this.context ? `
334
+ Request Body: ${JSON.stringify(this.requestBody, null, 2)}` : ""}${Object.keys(this.context).length ? `
336
335
  Context: ${JSON.stringify(this.context, null, 2)}` : ""}`;
337
336
  }
338
337
  toJSON() {
@@ -420,14 +419,31 @@ function updateRetryMetrics(metrics) {
420
419
  metrics.retryCount++;
421
420
  metrics.lastRetryTime = Date.now();
422
421
  }
422
+ function shouldRetry(error, status, attempt, config) {
423
+ if (attempt >= config.maxRetries) return false;
424
+ if (status && config.retryableStatusCodes.includes(status)) return true;
425
+ return error instanceof AxAIServiceNetworkError && !(error instanceof AxAIServiceAuthenticationError);
426
+ }
423
427
  var apiCall = async (api, json) => {
424
428
  const retryConfig = { ...defaultRetryConfig, ...api.retry };
425
429
  const timeoutMs = api.timeout ?? defaultTimeoutMs;
426
430
  const metrics = createRequestMetrics();
431
+ let timeoutId;
427
432
  const baseUrl = new URL(process.env["PROXY"] ?? api.url);
428
- const apiPath = import_path.default.join(baseUrl.pathname, api.name ?? "/", baseUrl.search);
433
+ const apiPath = [baseUrl.pathname, api.name].filter(Boolean).join("/").replace(/\/+/g, "/");
429
434
  const apiUrl = new URL(apiPath, baseUrl);
430
435
  const requestId = crypto.randomUUID();
436
+ if (api.validateRequest) {
437
+ const isValid = await api.validateRequest(json);
438
+ if (!isValid) {
439
+ throw new AxAIServiceResponseError(
440
+ "Invalid request data",
441
+ apiUrl.href,
442
+ json,
443
+ { validation: "request" }
444
+ );
445
+ }
446
+ }
431
447
  if (api.span?.isRecording()) {
432
448
  api.span.setAttributes({
433
449
  "http.request.method": api.put ? "PUT" : "POST",
@@ -439,7 +455,7 @@ var apiCall = async (api, json) => {
439
455
  let attempt = 0;
440
456
  while (true) {
441
457
  const controller = new AbortController();
442
- let timeoutId = setTimeout(() => {
458
+ timeoutId = setTimeout(() => {
443
459
  controller.abort("Request timeout");
444
460
  }, timeoutMs);
445
461
  try {
@@ -458,7 +474,7 @@ var apiCall = async (api, json) => {
458
474
  if (res.status === 401 || res.status === 403) {
459
475
  throw new AxAIServiceAuthenticationError(apiUrl.href, json, { metrics });
460
476
  }
461
- if (res.status >= 400 && attempt < retryConfig.maxRetries && retryConfig.retryableStatusCodes.includes(res.status)) {
477
+ if (res.status >= 400 && shouldRetry(new Error(), res.status, attempt, retryConfig)) {
462
478
  const delay = calculateRetryDelay(attempt, retryConfig);
463
479
  attempt++;
464
480
  updateRetryMetrics(metrics);
@@ -472,7 +488,7 @@ var apiCall = async (api, json) => {
472
488
  "metrics.lastRetryTime": metrics.lastRetryTime
473
489
  });
474
490
  }
475
- clearTimeout(timeoutId);
491
+ await new Promise((resolve) => setTimeout(resolve, delay));
476
492
  continue;
477
493
  }
478
494
  if (res.status >= 400) {
@@ -486,6 +502,17 @@ var apiCall = async (api, json) => {
486
502
  }
487
503
  if (!api.stream) {
488
504
  const resJson = await res.json();
505
+ if (api.validateResponse) {
506
+ const isValid = await api.validateResponse(resJson);
507
+ if (!isValid) {
508
+ throw new AxAIServiceResponseError(
509
+ "Invalid response data",
510
+ apiUrl.href,
511
+ json,
512
+ { validation: "response" }
513
+ );
514
+ }
515
+ }
489
516
  if (api.span?.isRecording()) {
490
517
  api.span.setAttributes({
491
518
  "response.time": Date.now() - metrics.startTime,
@@ -504,7 +531,7 @@ var apiCall = async (api, json) => {
504
531
  }
505
532
  let lastChunk;
506
533
  let chunkCount = 0;
507
- const trackingStream = new TransformStream({
534
+ const trackingStream = new import_web3.TransformStream({
508
535
  transform(chunk, controller2) {
509
536
  lastChunk = chunk;
510
537
  chunkCount++;
@@ -512,7 +539,7 @@ var apiCall = async (api, json) => {
512
539
  metrics.lastChunkTime = Date.now();
513
540
  controller2.enqueue(chunk);
514
541
  },
515
- flush(controller2) {
542
+ flush() {
516
543
  if (api.span?.isRecording()) {
517
544
  api.span.setAttributes({
518
545
  "stream.chunks": chunkCount,
@@ -520,10 +547,10 @@ var apiCall = async (api, json) => {
520
547
  "response.retries": metrics.retryCount
521
548
  });
522
549
  }
523
- controller2.terminate();
524
550
  }
525
551
  });
526
- const wrappedStream = new import_web3.ReadableStream({
552
+ let closed = false;
553
+ return new import_web3.ReadableStream({
527
554
  start(controller2) {
528
555
  const reader = res.body.pipeThrough(new textDecoderStream()).pipeThrough(new SSEParser()).pipeThrough(trackingStream).getReader();
529
556
  async function read() {
@@ -531,11 +558,14 @@ var apiCall = async (api, json) => {
531
558
  while (true) {
532
559
  const { done, value } = await reader.read();
533
560
  if (done) {
534
- controller2.close();
561
+ if (!closed) {
562
+ closed = true;
563
+ controller2.close();
564
+ }
535
565
  break;
536
- } else {
537
- controller2.enqueue(value);
538
566
  }
567
+ if (closed) break;
568
+ controller2.enqueue(value);
539
569
  }
540
570
  } catch (e) {
541
571
  const error = e;
@@ -552,26 +582,42 @@ var apiCall = async (api, json) => {
552
582
  { streamMetrics }
553
583
  )
554
584
  );
555
- } else {
585
+ } else if (error instanceof TypeError && error.message.includes("cancelled")) {
556
586
  controller2.error(
557
- new AxAIServiceResponseError(
558
- `Stream processing error: ${error.message}`,
587
+ new AxAIServiceStreamTerminatedError(
559
588
  apiUrl.href,
560
589
  json,
561
- { streamMetrics }
590
+ lastChunk,
591
+ {
592
+ streamMetrics,
593
+ cancelReason: "Stream cancelled by client"
594
+ }
562
595
  )
563
596
  );
597
+ } else {
598
+ controller2.error(
599
+ new AxAIServiceNetworkError(error, apiUrl.href, json, {
600
+ streamMetrics
601
+ })
602
+ );
564
603
  }
604
+ throw error;
565
605
  } finally {
606
+ clearTimeout(timeoutId);
566
607
  reader.releaseLock();
608
+ if (api.span?.isRecording()) {
609
+ api.span.end();
610
+ }
567
611
  }
568
612
  }
569
613
  read();
614
+ },
615
+ // When the consumer cancels the stream, set our flag to stop processing further.
616
+ cancel() {
617
+ closed = true;
570
618
  }
571
619
  });
572
- return wrappedStream;
573
620
  } catch (error) {
574
- clearTimeout(timeoutId);
575
621
  if (error instanceof Error && error.name === "AbortError") {
576
622
  throw new AxAIServiceTimeoutError(apiUrl.href, timeoutMs, json, {
577
623
  metrics
@@ -584,7 +630,7 @@ var apiCall = async (api, json) => {
584
630
  "error.retries": metrics.retryCount
585
631
  });
586
632
  }
587
- if (error instanceof AxAIServiceNetworkError && attempt < retryConfig.maxRetries) {
633
+ if (error instanceof AxAIServiceNetworkError && shouldRetry(error, void 0, attempt, retryConfig)) {
588
634
  const delay = calculateRetryDelay(attempt, retryConfig);
589
635
  attempt++;
590
636
  updateRetryMetrics(metrics);
@@ -598,12 +644,20 @@ var apiCall = async (api, json) => {
598
644
  "metrics.lastRetryTime": metrics.lastRetryTime
599
645
  });
600
646
  }
647
+ await new Promise((resolve) => setTimeout(resolve, delay));
601
648
  continue;
602
649
  }
603
650
  if (error instanceof AxAIServiceError) {
604
651
  error.context["metrics"] = metrics;
605
652
  }
606
653
  throw error;
654
+ } finally {
655
+ if (timeoutId !== void 0) {
656
+ clearTimeout(timeoutId);
657
+ }
658
+ if (api.span?.isRecording()) {
659
+ api.span.end();
660
+ }
607
661
  }
608
662
  }
609
663
  };
@@ -1653,8 +1707,9 @@ function mapFinishReason(stopReason) {
1653
1707
 
1654
1708
  // ai/openai/types.ts
1655
1709
  var AxAIOpenAIModel = /* @__PURE__ */ ((AxAIOpenAIModel2) => {
1656
- AxAIOpenAIModel2["O1Preview"] = "o1-preview";
1710
+ AxAIOpenAIModel2["O1"] = "o1";
1657
1711
  AxAIOpenAIModel2["O1Mini"] = "o1-mini";
1712
+ AxAIOpenAIModel2["O3Mini"] = "o3-mini";
1658
1713
  AxAIOpenAIModel2["GPT4"] = "gpt-4";
1659
1714
  AxAIOpenAIModel2["GPT4O"] = "gpt-4o";
1660
1715
  AxAIOpenAIModel2["GPT4OMini"] = "gpt-4o-mini";
@@ -1677,7 +1732,7 @@ var AxAIOpenAIEmbedModel = /* @__PURE__ */ ((AxAIOpenAIEmbedModel2) => {
1677
1732
  // ai/openai/info.ts
1678
1733
  var axModelInfoOpenAI = [
1679
1734
  {
1680
- name: "o1-preview" /* O1Preview */,
1735
+ name: "o1" /* O1 */,
1681
1736
  currency: "usd",
1682
1737
  promptTokenCostPer1M: 15,
1683
1738
  completionTokenCostPer1M: 60
@@ -1685,8 +1740,14 @@ var axModelInfoOpenAI = [
1685
1740
  {
1686
1741
  name: "o1-mini" /* O1Mini */,
1687
1742
  currency: "usd",
1688
- promptTokenCostPer1M: 3,
1689
- completionTokenCostPer1M: 12
1743
+ promptTokenCostPer1M: 1.1,
1744
+ completionTokenCostPer1M: 14.4
1745
+ },
1746
+ {
1747
+ name: "o3-mini" /* O3Mini */,
1748
+ currency: "usd",
1749
+ promptTokenCostPer1M: 1.1,
1750
+ completionTokenCostPer1M: 4.4
1690
1751
  },
1691
1752
  {
1692
1753
  name: "gpt-4" /* GPT4 */,
@@ -1786,16 +1847,12 @@ var AxAIOpenAIImpl = class {
1786
1847
  parameters: v.parameters
1787
1848
  }
1788
1849
  }));
1789
- if (tools && isO1Model(model)) {
1790
- throw new Error("Functions are not supported for O1 models");
1791
- }
1792
1850
  const toolsChoice = !req.functionCall && req.functions && req.functions.length > 0 ? "auto" : req.functionCall;
1793
1851
  const messages = createMessages2(req);
1794
1852
  const frequencyPenalty = req.modelConfig?.frequencyPenalty ?? this.config.frequencyPenalty;
1795
1853
  const stream = req.modelConfig?.stream ?? this.config.stream;
1796
- if (stream && isO1Model(model)) {
1797
- throw new Error("Streaming is not supported for O1 models");
1798
- }
1854
+ const reasoningEffort = isReasoningModel(model) ? this.config.reasoningEffort : void 0;
1855
+ const store = this.config.store;
1799
1856
  const reqValue = {
1800
1857
  model,
1801
1858
  messages,
@@ -1810,7 +1867,9 @@ var AxAIOpenAIImpl = class {
1810
1867
  presence_penalty: req.modelConfig?.presencePenalty ?? this.config.presencePenalty,
1811
1868
  logit_bias: this.config.logitBias,
1812
1869
  ...frequencyPenalty ? { frequency_penalty: frequencyPenalty } : {},
1813
- ...stream && this.streamingUsage ? { stream: true, stream_options: { include_usage: true } } : {}
1870
+ ...stream && this.streamingUsage ? { stream: true, stream_options: { include_usage: true } } : {},
1871
+ ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {},
1872
+ ...store ? { store } : {}
1814
1873
  };
1815
1874
  return [apiConfig, reqValue];
1816
1875
  }
@@ -1936,12 +1995,6 @@ var mapFinishReason2 = (finishReason) => {
1936
1995
  };
1937
1996
  function createMessages2(req) {
1938
1997
  return req.chatPrompt.map((msg) => {
1939
- if (msg.role === "system" && isO1Model(req.model)) {
1940
- msg = {
1941
- role: "user",
1942
- content: msg.content
1943
- };
1944
- }
1945
1998
  switch (msg.role) {
1946
1999
  case "system":
1947
2000
  return { role: "system", content: msg.content };
@@ -2031,14 +2084,14 @@ var AxAIOpenAI = class extends AxBaseAI {
2031
2084
  embedModel: _config.embedModel
2032
2085
  },
2033
2086
  options,
2034
- supportFor: (model) => {
2035
- return isO1Model(model) ? { functions: false, streaming: false } : { functions: true, streaming: true };
2087
+ supportFor: () => {
2088
+ return { functions: true, streaming: true };
2036
2089
  },
2037
2090
  modelMap
2038
2091
  });
2039
2092
  }
2040
2093
  };
2041
- var isO1Model = (model) => ["o1-mini" /* O1Mini */, "o1-preview" /* O1Preview */].includes(
2094
+ var isReasoningModel = (model) => ["o1-mini" /* O1Mini */, "o1" /* O1 */, "o3-mini" /* O3Mini */].includes(
2042
2095
  model
2043
2096
  );
2044
2097
 
@@ -5283,8 +5336,10 @@ function* streamValues(sig, values, xstate, content, final = false) {
5283
5336
  const v = content.substring(s);
5284
5337
  const v1 = v.replace(/[\s\n\t]+$/, "");
5285
5338
  const v2 = pos === 0 ? v1.trimStart() : v1;
5286
- yield { [fieldName]: v2 };
5287
- xstate.streamedIndex[fieldName] = pos + v1.length;
5339
+ if (v2.length > 0) {
5340
+ yield { [fieldName]: v2 };
5341
+ xstate.streamedIndex[fieldName] = pos + v1.length;
5342
+ }
5288
5343
  return;
5289
5344
  }
5290
5345
  }
@@ -5293,7 +5348,7 @@ function* streamValues(sig, values, xstate, content, final = false) {
5293
5348
  if (Array.isArray(value)) {
5294
5349
  const s = xstate.streamedIndex[key] ?? 0;
5295
5350
  const v = value.slice(s);
5296
- if (v) {
5351
+ if (v && v.length > 0) {
5297
5352
  yield { [key]: v };
5298
5353
  xstate.streamedIndex[key] = s + 1;
5299
5354
  }
@@ -5389,7 +5444,7 @@ var extractBlock = (input) => {
5389
5444
  // dsp/jsonschema.ts
5390
5445
  var validateJSONSchema = (schema) => {
5391
5446
  const errors = [];
5392
- const validateSchemaObject = (schema2, path2 = "") => {
5447
+ const validateSchemaObject = (schema2, path = "") => {
5393
5448
  const validTypes = [
5394
5449
  "array",
5395
5450
  "integer",
@@ -5400,31 +5455,31 @@ var validateJSONSchema = (schema) => {
5400
5455
  "object"
5401
5456
  ];
5402
5457
  if (!validTypes.includes(schema2.type)) {
5403
- errors.push(`Invalid type '${schema2.type}' at ${path2 || "root"}`);
5458
+ errors.push(`Invalid type '${schema2.type}' at ${path || "root"}`);
5404
5459
  return;
5405
5460
  }
5406
5461
  if (schema2.type === "object" && schema2.properties) {
5407
5462
  if (typeof schema2.properties !== "object" || Array.isArray(schema2.properties)) {
5408
- errors.push(`Invalid properties object at ${path2 || "root"}`);
5463
+ errors.push(`Invalid properties object at ${path || "root"}`);
5409
5464
  } else {
5410
5465
  for (const key in schema2.properties) {
5411
5466
  const value = schema2.properties[key];
5412
5467
  if (typeof value !== "object") {
5413
- errors.push(`Invalid schema object at ${path2}${key}`);
5468
+ errors.push(`Invalid schema object at ${path}${key}`);
5414
5469
  continue;
5415
5470
  }
5416
- validateSchemaObject(value, `${path2}${key}.`);
5471
+ validateSchemaObject(value, `${path}${key}.`);
5417
5472
  }
5418
5473
  }
5419
5474
  if (schema2.required && !Array.isArray(schema2.required)) {
5420
- errors.push(`'required' should be an array at ${path2 || "root"}`);
5475
+ errors.push(`'required' should be an array at ${path || "root"}`);
5421
5476
  }
5422
5477
  }
5423
5478
  if (schema2.type === "array" && schema2.items) {
5424
5479
  if (typeof schema2.items !== "object") {
5425
- errors.push(`Invalid items schema at ${path2 || "root"}`);
5480
+ errors.push(`Invalid items schema at ${path || "root"}`);
5426
5481
  } else {
5427
- validateSchemaObject(schema2.items, `${path2}items.`);
5482
+ validateSchemaObject(schema2.items, `${path}items.`);
5428
5483
  }
5429
5484
  }
5430
5485
  };
@@ -5808,6 +5863,7 @@ var AxGen = class extends AxProgramWithSignature {
5808
5863
  const e1 = e;
5809
5864
  errorFields = e1.getFixingInstructions();
5810
5865
  err = e;
5866
+ } else if (e instanceof AxAIServiceStreamTerminatedError) {
5811
5867
  } else {
5812
5868
  throw e;
5813
5869
  }
@@ -6071,6 +6127,7 @@ var AxBalancer = class _AxBalancer {
6071
6127
  services;
6072
6128
  currentServiceIndex = 0;
6073
6129
  currentService;
6130
+ debug;
6074
6131
  constructor(services, options) {
6075
6132
  if (services.length === 0) {
6076
6133
  throw new Error("No AI services provided.");
@@ -6083,6 +6140,7 @@ var AxBalancer = class _AxBalancer {
6083
6140
  throw new Error("Error initializing the AI services.");
6084
6141
  }
6085
6142
  this.currentService = cs;
6143
+ this.debug = options?.debug ?? true;
6086
6144
  }
6087
6145
  /**
6088
6146
  * Service comparator that respects the input order of services.
@@ -6157,16 +6215,20 @@ var AxBalancer = class _AxBalancer {
6157
6215
  default:
6158
6216
  throw e;
6159
6217
  }
6160
- console.warn(
6161
- `AxBalancer: Service ${this.currentService.getName()} failed`,
6162
- e
6163
- );
6218
+ if (this.debug) {
6219
+ console.warn(
6220
+ `AxBalancer: Service ${this.currentService.getName()} failed`,
6221
+ e
6222
+ );
6223
+ }
6164
6224
  if (!this.getNextService()) {
6165
6225
  throw e;
6166
6226
  }
6167
- console.warn(
6168
- `AxBalancer: Switching to service ${this.currentService.getName()}`
6169
- );
6227
+ if (this.debug) {
6228
+ console.warn(
6229
+ `AxBalancer: Switching to service ${this.currentService.getName()}`
6230
+ );
6231
+ }
6170
6232
  }
6171
6233
  }
6172
6234
  }
@@ -6176,11 +6238,15 @@ var AxBalancer = class _AxBalancer {
6176
6238
  try {
6177
6239
  return await this.currentService.embed(req, options);
6178
6240
  } catch (e) {
6179
- console.warn(`Service ${this.currentService.getName()} failed`);
6241
+ if (this.debug) {
6242
+ console.warn(`Service ${this.currentService.getName()} failed`);
6243
+ }
6180
6244
  if (!this.getNextService()) {
6181
6245
  throw e;
6182
6246
  }
6183
- console.warn(`Switching to service ${this.currentService.getName()}`);
6247
+ if (this.debug) {
6248
+ console.warn(`Switching to service ${this.currentService.getName()}`);
6249
+ }
6184
6250
  }
6185
6251
  }
6186
6252
  }