@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.js CHANGED
@@ -74,16 +74,16 @@ var AxSpanKindValues = /* @__PURE__ */ ((AxSpanKindValues2) => {
74
74
  })(AxSpanKindValues || {});
75
75
 
76
76
  // util/apicall.ts
77
- import path from "path";
78
77
  import {
79
78
  ReadableStream,
80
- TextDecoderStream as TextDecoderStreamNative
79
+ TextDecoderStream as TextDecoderStreamNative,
80
+ TransformStream as TransformStream3
81
81
  } from "stream/web";
82
82
  import "@opentelemetry/api";
83
83
 
84
84
  // util/sse.ts
85
- import { TransformStream as TransformStream2 } from "stream/web";
86
- var SSEParser = class extends TransformStream2 {
85
+ import { TransformStream } from "stream/web";
86
+ var SSEParser = class extends TransformStream {
87
87
  buffer = "";
88
88
  currentEvent = { rawData: "" };
89
89
  dataParser;
@@ -173,7 +173,7 @@ var SSEParser = class extends TransformStream2 {
173
173
 
174
174
  // util/stream.ts
175
175
  import {
176
- TransformStream as TransformStream3
176
+ TransformStream as TransformStream2
177
177
  } from "stream/web";
178
178
  var TextDecodeTransformer = class {
179
179
  decoder;
@@ -196,7 +196,7 @@ var TextDecodeTransformer = class {
196
196
  }
197
197
  }
198
198
  };
199
- var TextDecoderStreamPolyfill = class extends TransformStream3 {
199
+ var TextDecoderStreamPolyfill = class extends TransformStream2 {
200
200
  constructor() {
201
201
  super(new TextDecodeTransformer());
202
202
  }
@@ -207,7 +207,6 @@ var defaultRetryConfig = {
207
207
  maxRetries: 3,
208
208
  initialDelayMs: 1e3,
209
209
  maxDelayMs: 6e4,
210
- // Increased to 60 seconds
211
210
  backoffFactor: 2,
212
211
  retryableStatusCodes: [500, 408, 429, 502, 503, 504]
213
212
  };
@@ -218,18 +217,19 @@ var AxAIServiceError = class extends Error {
218
217
  super(message);
219
218
  this.url = url;
220
219
  this.requestBody = requestBody;
221
- this.context = context;
222
220
  this.name = "AxAIServiceError";
223
221
  this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
224
222
  this.errorId = crypto.randomUUID();
223
+ this.context = context;
225
224
  }
226
225
  timestamp;
227
226
  errorId;
227
+ context;
228
228
  toString() {
229
229
  return `${this.name} [${this.errorId}]: ${this.message}
230
230
  Timestamp: ${this.timestamp}
231
231
  URL: ${this.url}${this.requestBody ? `
232
- Request Body: ${JSON.stringify(this.requestBody, null, 2)}` : ""}${this.context ? `
232
+ Request Body: ${JSON.stringify(this.requestBody, null, 2)}` : ""}${Object.keys(this.context).length ? `
233
233
  Context: ${JSON.stringify(this.context, null, 2)}` : ""}`;
234
234
  }
235
235
  toJSON() {
@@ -317,14 +317,31 @@ function updateRetryMetrics(metrics) {
317
317
  metrics.retryCount++;
318
318
  metrics.lastRetryTime = Date.now();
319
319
  }
320
+ function shouldRetry(error, status, attempt, config) {
321
+ if (attempt >= config.maxRetries) return false;
322
+ if (status && config.retryableStatusCodes.includes(status)) return true;
323
+ return error instanceof AxAIServiceNetworkError && !(error instanceof AxAIServiceAuthenticationError);
324
+ }
320
325
  var apiCall = async (api, json) => {
321
326
  const retryConfig = { ...defaultRetryConfig, ...api.retry };
322
327
  const timeoutMs = api.timeout ?? defaultTimeoutMs;
323
328
  const metrics = createRequestMetrics();
329
+ let timeoutId;
324
330
  const baseUrl = new URL(process.env["PROXY"] ?? api.url);
325
- const apiPath = path.join(baseUrl.pathname, api.name ?? "/", baseUrl.search);
331
+ const apiPath = [baseUrl.pathname, api.name].filter(Boolean).join("/").replace(/\/+/g, "/");
326
332
  const apiUrl = new URL(apiPath, baseUrl);
327
333
  const requestId = crypto.randomUUID();
334
+ if (api.validateRequest) {
335
+ const isValid = await api.validateRequest(json);
336
+ if (!isValid) {
337
+ throw new AxAIServiceResponseError(
338
+ "Invalid request data",
339
+ apiUrl.href,
340
+ json,
341
+ { validation: "request" }
342
+ );
343
+ }
344
+ }
328
345
  if (api.span?.isRecording()) {
329
346
  api.span.setAttributes({
330
347
  "http.request.method": api.put ? "PUT" : "POST",
@@ -336,7 +353,7 @@ var apiCall = async (api, json) => {
336
353
  let attempt = 0;
337
354
  while (true) {
338
355
  const controller = new AbortController();
339
- let timeoutId = setTimeout(() => {
356
+ timeoutId = setTimeout(() => {
340
357
  controller.abort("Request timeout");
341
358
  }, timeoutMs);
342
359
  try {
@@ -355,7 +372,7 @@ var apiCall = async (api, json) => {
355
372
  if (res.status === 401 || res.status === 403) {
356
373
  throw new AxAIServiceAuthenticationError(apiUrl.href, json, { metrics });
357
374
  }
358
- if (res.status >= 400 && attempt < retryConfig.maxRetries && retryConfig.retryableStatusCodes.includes(res.status)) {
375
+ if (res.status >= 400 && shouldRetry(new Error(), res.status, attempt, retryConfig)) {
359
376
  const delay = calculateRetryDelay(attempt, retryConfig);
360
377
  attempt++;
361
378
  updateRetryMetrics(metrics);
@@ -369,7 +386,7 @@ var apiCall = async (api, json) => {
369
386
  "metrics.lastRetryTime": metrics.lastRetryTime
370
387
  });
371
388
  }
372
- clearTimeout(timeoutId);
389
+ await new Promise((resolve) => setTimeout(resolve, delay));
373
390
  continue;
374
391
  }
375
392
  if (res.status >= 400) {
@@ -383,6 +400,17 @@ var apiCall = async (api, json) => {
383
400
  }
384
401
  if (!api.stream) {
385
402
  const resJson = await res.json();
403
+ if (api.validateResponse) {
404
+ const isValid = await api.validateResponse(resJson);
405
+ if (!isValid) {
406
+ throw new AxAIServiceResponseError(
407
+ "Invalid response data",
408
+ apiUrl.href,
409
+ json,
410
+ { validation: "response" }
411
+ );
412
+ }
413
+ }
386
414
  if (api.span?.isRecording()) {
387
415
  api.span.setAttributes({
388
416
  "response.time": Date.now() - metrics.startTime,
@@ -401,7 +429,7 @@ var apiCall = async (api, json) => {
401
429
  }
402
430
  let lastChunk;
403
431
  let chunkCount = 0;
404
- const trackingStream = new TransformStream({
432
+ const trackingStream = new TransformStream3({
405
433
  transform(chunk, controller2) {
406
434
  lastChunk = chunk;
407
435
  chunkCount++;
@@ -409,7 +437,7 @@ var apiCall = async (api, json) => {
409
437
  metrics.lastChunkTime = Date.now();
410
438
  controller2.enqueue(chunk);
411
439
  },
412
- flush(controller2) {
440
+ flush() {
413
441
  if (api.span?.isRecording()) {
414
442
  api.span.setAttributes({
415
443
  "stream.chunks": chunkCount,
@@ -417,10 +445,10 @@ var apiCall = async (api, json) => {
417
445
  "response.retries": metrics.retryCount
418
446
  });
419
447
  }
420
- controller2.terminate();
421
448
  }
422
449
  });
423
- const wrappedStream = new ReadableStream({
450
+ let closed = false;
451
+ return new ReadableStream({
424
452
  start(controller2) {
425
453
  const reader = res.body.pipeThrough(new textDecoderStream()).pipeThrough(new SSEParser()).pipeThrough(trackingStream).getReader();
426
454
  async function read() {
@@ -428,11 +456,14 @@ var apiCall = async (api, json) => {
428
456
  while (true) {
429
457
  const { done, value } = await reader.read();
430
458
  if (done) {
431
- controller2.close();
459
+ if (!closed) {
460
+ closed = true;
461
+ controller2.close();
462
+ }
432
463
  break;
433
- } else {
434
- controller2.enqueue(value);
435
464
  }
465
+ if (closed) break;
466
+ controller2.enqueue(value);
436
467
  }
437
468
  } catch (e) {
438
469
  const error = e;
@@ -449,26 +480,42 @@ var apiCall = async (api, json) => {
449
480
  { streamMetrics }
450
481
  )
451
482
  );
452
- } else {
483
+ } else if (error instanceof TypeError && error.message.includes("cancelled")) {
453
484
  controller2.error(
454
- new AxAIServiceResponseError(
455
- `Stream processing error: ${error.message}`,
485
+ new AxAIServiceStreamTerminatedError(
456
486
  apiUrl.href,
457
487
  json,
458
- { streamMetrics }
488
+ lastChunk,
489
+ {
490
+ streamMetrics,
491
+ cancelReason: "Stream cancelled by client"
492
+ }
459
493
  )
460
494
  );
495
+ } else {
496
+ controller2.error(
497
+ new AxAIServiceNetworkError(error, apiUrl.href, json, {
498
+ streamMetrics
499
+ })
500
+ );
461
501
  }
502
+ throw error;
462
503
  } finally {
504
+ clearTimeout(timeoutId);
463
505
  reader.releaseLock();
506
+ if (api.span?.isRecording()) {
507
+ api.span.end();
508
+ }
464
509
  }
465
510
  }
466
511
  read();
512
+ },
513
+ // When the consumer cancels the stream, set our flag to stop processing further.
514
+ cancel() {
515
+ closed = true;
467
516
  }
468
517
  });
469
- return wrappedStream;
470
518
  } catch (error) {
471
- clearTimeout(timeoutId);
472
519
  if (error instanceof Error && error.name === "AbortError") {
473
520
  throw new AxAIServiceTimeoutError(apiUrl.href, timeoutMs, json, {
474
521
  metrics
@@ -481,7 +528,7 @@ var apiCall = async (api, json) => {
481
528
  "error.retries": metrics.retryCount
482
529
  });
483
530
  }
484
- if (error instanceof AxAIServiceNetworkError && attempt < retryConfig.maxRetries) {
531
+ if (error instanceof AxAIServiceNetworkError && shouldRetry(error, void 0, attempt, retryConfig)) {
485
532
  const delay = calculateRetryDelay(attempt, retryConfig);
486
533
  attempt++;
487
534
  updateRetryMetrics(metrics);
@@ -495,12 +542,20 @@ var apiCall = async (api, json) => {
495
542
  "metrics.lastRetryTime": metrics.lastRetryTime
496
543
  });
497
544
  }
545
+ await new Promise((resolve) => setTimeout(resolve, delay));
498
546
  continue;
499
547
  }
500
548
  if (error instanceof AxAIServiceError) {
501
549
  error.context["metrics"] = metrics;
502
550
  }
503
551
  throw error;
552
+ } finally {
553
+ if (timeoutId !== void 0) {
554
+ clearTimeout(timeoutId);
555
+ }
556
+ if (api.span?.isRecording()) {
557
+ api.span.end();
558
+ }
504
559
  }
505
560
  }
506
561
  };
@@ -1552,8 +1607,9 @@ function mapFinishReason(stopReason) {
1552
1607
 
1553
1608
  // ai/openai/types.ts
1554
1609
  var AxAIOpenAIModel = /* @__PURE__ */ ((AxAIOpenAIModel2) => {
1555
- AxAIOpenAIModel2["O1Preview"] = "o1-preview";
1610
+ AxAIOpenAIModel2["O1"] = "o1";
1556
1611
  AxAIOpenAIModel2["O1Mini"] = "o1-mini";
1612
+ AxAIOpenAIModel2["O3Mini"] = "o3-mini";
1557
1613
  AxAIOpenAIModel2["GPT4"] = "gpt-4";
1558
1614
  AxAIOpenAIModel2["GPT4O"] = "gpt-4o";
1559
1615
  AxAIOpenAIModel2["GPT4OMini"] = "gpt-4o-mini";
@@ -1576,7 +1632,7 @@ var AxAIOpenAIEmbedModel = /* @__PURE__ */ ((AxAIOpenAIEmbedModel2) => {
1576
1632
  // ai/openai/info.ts
1577
1633
  var axModelInfoOpenAI = [
1578
1634
  {
1579
- name: "o1-preview" /* O1Preview */,
1635
+ name: "o1" /* O1 */,
1580
1636
  currency: "usd",
1581
1637
  promptTokenCostPer1M: 15,
1582
1638
  completionTokenCostPer1M: 60
@@ -1584,8 +1640,14 @@ var axModelInfoOpenAI = [
1584
1640
  {
1585
1641
  name: "o1-mini" /* O1Mini */,
1586
1642
  currency: "usd",
1587
- promptTokenCostPer1M: 3,
1588
- completionTokenCostPer1M: 12
1643
+ promptTokenCostPer1M: 1.1,
1644
+ completionTokenCostPer1M: 14.4
1645
+ },
1646
+ {
1647
+ name: "o3-mini" /* O3Mini */,
1648
+ currency: "usd",
1649
+ promptTokenCostPer1M: 1.1,
1650
+ completionTokenCostPer1M: 4.4
1589
1651
  },
1590
1652
  {
1591
1653
  name: "gpt-4" /* GPT4 */,
@@ -1685,16 +1747,12 @@ var AxAIOpenAIImpl = class {
1685
1747
  parameters: v.parameters
1686
1748
  }
1687
1749
  }));
1688
- if (tools && isO1Model(model)) {
1689
- throw new Error("Functions are not supported for O1 models");
1690
- }
1691
1750
  const toolsChoice = !req.functionCall && req.functions && req.functions.length > 0 ? "auto" : req.functionCall;
1692
1751
  const messages = createMessages2(req);
1693
1752
  const frequencyPenalty = req.modelConfig?.frequencyPenalty ?? this.config.frequencyPenalty;
1694
1753
  const stream = req.modelConfig?.stream ?? this.config.stream;
1695
- if (stream && isO1Model(model)) {
1696
- throw new Error("Streaming is not supported for O1 models");
1697
- }
1754
+ const reasoningEffort = isReasoningModel(model) ? this.config.reasoningEffort : void 0;
1755
+ const store = this.config.store;
1698
1756
  const reqValue = {
1699
1757
  model,
1700
1758
  messages,
@@ -1709,7 +1767,9 @@ var AxAIOpenAIImpl = class {
1709
1767
  presence_penalty: req.modelConfig?.presencePenalty ?? this.config.presencePenalty,
1710
1768
  logit_bias: this.config.logitBias,
1711
1769
  ...frequencyPenalty ? { frequency_penalty: frequencyPenalty } : {},
1712
- ...stream && this.streamingUsage ? { stream: true, stream_options: { include_usage: true } } : {}
1770
+ ...stream && this.streamingUsage ? { stream: true, stream_options: { include_usage: true } } : {},
1771
+ ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {},
1772
+ ...store ? { store } : {}
1713
1773
  };
1714
1774
  return [apiConfig, reqValue];
1715
1775
  }
@@ -1835,12 +1895,6 @@ var mapFinishReason2 = (finishReason) => {
1835
1895
  };
1836
1896
  function createMessages2(req) {
1837
1897
  return req.chatPrompt.map((msg) => {
1838
- if (msg.role === "system" && isO1Model(req.model)) {
1839
- msg = {
1840
- role: "user",
1841
- content: msg.content
1842
- };
1843
- }
1844
1898
  switch (msg.role) {
1845
1899
  case "system":
1846
1900
  return { role: "system", content: msg.content };
@@ -1930,14 +1984,14 @@ var AxAIOpenAI = class extends AxBaseAI {
1930
1984
  embedModel: _config.embedModel
1931
1985
  },
1932
1986
  options,
1933
- supportFor: (model) => {
1934
- return isO1Model(model) ? { functions: false, streaming: false } : { functions: true, streaming: true };
1987
+ supportFor: () => {
1988
+ return { functions: true, streaming: true };
1935
1989
  },
1936
1990
  modelMap
1937
1991
  });
1938
1992
  }
1939
1993
  };
1940
- var isO1Model = (model) => ["o1-mini" /* O1Mini */, "o1-preview" /* O1Preview */].includes(
1994
+ var isReasoningModel = (model) => ["o1-mini" /* O1Mini */, "o1" /* O1 */, "o3-mini" /* O3Mini */].includes(
1941
1995
  model
1942
1996
  );
1943
1997
 
@@ -5182,8 +5236,10 @@ function* streamValues(sig, values, xstate, content, final = false) {
5182
5236
  const v = content.substring(s);
5183
5237
  const v1 = v.replace(/[\s\n\t]+$/, "");
5184
5238
  const v2 = pos === 0 ? v1.trimStart() : v1;
5185
- yield { [fieldName]: v2 };
5186
- xstate.streamedIndex[fieldName] = pos + v1.length;
5239
+ if (v2.length > 0) {
5240
+ yield { [fieldName]: v2 };
5241
+ xstate.streamedIndex[fieldName] = pos + v1.length;
5242
+ }
5187
5243
  return;
5188
5244
  }
5189
5245
  }
@@ -5192,7 +5248,7 @@ function* streamValues(sig, values, xstate, content, final = false) {
5192
5248
  if (Array.isArray(value)) {
5193
5249
  const s = xstate.streamedIndex[key] ?? 0;
5194
5250
  const v = value.slice(s);
5195
- if (v) {
5251
+ if (v && v.length > 0) {
5196
5252
  yield { [key]: v };
5197
5253
  xstate.streamedIndex[key] = s + 1;
5198
5254
  }
@@ -5288,7 +5344,7 @@ var extractBlock = (input) => {
5288
5344
  // dsp/jsonschema.ts
5289
5345
  var validateJSONSchema = (schema) => {
5290
5346
  const errors = [];
5291
- const validateSchemaObject = (schema2, path2 = "") => {
5347
+ const validateSchemaObject = (schema2, path = "") => {
5292
5348
  const validTypes = [
5293
5349
  "array",
5294
5350
  "integer",
@@ -5299,31 +5355,31 @@ var validateJSONSchema = (schema) => {
5299
5355
  "object"
5300
5356
  ];
5301
5357
  if (!validTypes.includes(schema2.type)) {
5302
- errors.push(`Invalid type '${schema2.type}' at ${path2 || "root"}`);
5358
+ errors.push(`Invalid type '${schema2.type}' at ${path || "root"}`);
5303
5359
  return;
5304
5360
  }
5305
5361
  if (schema2.type === "object" && schema2.properties) {
5306
5362
  if (typeof schema2.properties !== "object" || Array.isArray(schema2.properties)) {
5307
- errors.push(`Invalid properties object at ${path2 || "root"}`);
5363
+ errors.push(`Invalid properties object at ${path || "root"}`);
5308
5364
  } else {
5309
5365
  for (const key in schema2.properties) {
5310
5366
  const value = schema2.properties[key];
5311
5367
  if (typeof value !== "object") {
5312
- errors.push(`Invalid schema object at ${path2}${key}`);
5368
+ errors.push(`Invalid schema object at ${path}${key}`);
5313
5369
  continue;
5314
5370
  }
5315
- validateSchemaObject(value, `${path2}${key}.`);
5371
+ validateSchemaObject(value, `${path}${key}.`);
5316
5372
  }
5317
5373
  }
5318
5374
  if (schema2.required && !Array.isArray(schema2.required)) {
5319
- errors.push(`'required' should be an array at ${path2 || "root"}`);
5375
+ errors.push(`'required' should be an array at ${path || "root"}`);
5320
5376
  }
5321
5377
  }
5322
5378
  if (schema2.type === "array" && schema2.items) {
5323
5379
  if (typeof schema2.items !== "object") {
5324
- errors.push(`Invalid items schema at ${path2 || "root"}`);
5380
+ errors.push(`Invalid items schema at ${path || "root"}`);
5325
5381
  } else {
5326
- validateSchemaObject(schema2.items, `${path2}items.`);
5382
+ validateSchemaObject(schema2.items, `${path}items.`);
5327
5383
  }
5328
5384
  }
5329
5385
  };
@@ -5707,6 +5763,7 @@ var AxGen = class extends AxProgramWithSignature {
5707
5763
  const e1 = e;
5708
5764
  errorFields = e1.getFixingInstructions();
5709
5765
  err = e;
5766
+ } else if (e instanceof AxAIServiceStreamTerminatedError) {
5710
5767
  } else {
5711
5768
  throw e;
5712
5769
  }
@@ -5970,6 +6027,7 @@ var AxBalancer = class _AxBalancer {
5970
6027
  services;
5971
6028
  currentServiceIndex = 0;
5972
6029
  currentService;
6030
+ debug;
5973
6031
  constructor(services, options) {
5974
6032
  if (services.length === 0) {
5975
6033
  throw new Error("No AI services provided.");
@@ -5982,6 +6040,7 @@ var AxBalancer = class _AxBalancer {
5982
6040
  throw new Error("Error initializing the AI services.");
5983
6041
  }
5984
6042
  this.currentService = cs;
6043
+ this.debug = options?.debug ?? true;
5985
6044
  }
5986
6045
  /**
5987
6046
  * Service comparator that respects the input order of services.
@@ -6056,16 +6115,20 @@ var AxBalancer = class _AxBalancer {
6056
6115
  default:
6057
6116
  throw e;
6058
6117
  }
6059
- console.warn(
6060
- `AxBalancer: Service ${this.currentService.getName()} failed`,
6061
- e
6062
- );
6118
+ if (this.debug) {
6119
+ console.warn(
6120
+ `AxBalancer: Service ${this.currentService.getName()} failed`,
6121
+ e
6122
+ );
6123
+ }
6063
6124
  if (!this.getNextService()) {
6064
6125
  throw e;
6065
6126
  }
6066
- console.warn(
6067
- `AxBalancer: Switching to service ${this.currentService.getName()}`
6068
- );
6127
+ if (this.debug) {
6128
+ console.warn(
6129
+ `AxBalancer: Switching to service ${this.currentService.getName()}`
6130
+ );
6131
+ }
6069
6132
  }
6070
6133
  }
6071
6134
  }
@@ -6075,11 +6138,15 @@ var AxBalancer = class _AxBalancer {
6075
6138
  try {
6076
6139
  return await this.currentService.embed(req, options);
6077
6140
  } catch (e) {
6078
- console.warn(`Service ${this.currentService.getName()} failed`);
6141
+ if (this.debug) {
6142
+ console.warn(`Service ${this.currentService.getName()} failed`);
6143
+ }
6079
6144
  if (!this.getNextService()) {
6080
6145
  throw e;
6081
6146
  }
6082
- console.warn(`Switching to service ${this.currentService.getName()}`);
6147
+ if (this.debug) {
6148
+ console.warn(`Switching to service ${this.currentService.getName()}`);
6149
+ }
6083
6150
  }
6084
6151
  }
6085
6152
  }