@f5-sales-demo/pi-ai 21.41.3 → 21.41.5

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/pi-ai",
4
- "version": "21.41.3",
4
+ "version": "21.41.5",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -46,7 +46,7 @@
46
46
  "@anthropic-ai/sdk": "0.123.0",
47
47
  "@aws-sdk/client-bedrock-runtime": "3.1126.0",
48
48
  "@bufbuild/protobuf": "2.14.1",
49
- "@f5-sales-demo/pi-utils": "21.41.3",
49
+ "@f5-sales-demo/pi-utils": "21.41.5",
50
50
  "@google/genai": "2.21.0",
51
51
  "@sinclair/typebox": "0.34.52",
52
52
  "@smithy/node-http-handler": "4.12.1",
@@ -14,6 +14,7 @@ export type CodexRateLimits = {
14
14
  export type CodexErrorInfo = {
15
15
  message: string;
16
16
  status: number;
17
+ code?: string;
17
18
  friendlyMessage?: string;
18
19
  rateLimits?: CodexRateLimits;
19
20
  raw?: string;
@@ -24,6 +25,7 @@ export async function parseCodexError(response: Response): Promise<CodexErrorInf
24
25
  let message = raw || response.statusText || "Request failed";
25
26
  let friendlyMessage: string | undefined;
26
27
  let rateLimits: CodexRateLimits | undefined;
28
+ let code: string | undefined;
27
29
 
28
30
  try {
29
31
  const parsed = JSON.parse(raw) as { error?: Record<string, unknown> };
@@ -45,16 +47,17 @@ export async function parseCodexError(response: Response): Promise<CodexErrorInf
45
47
  ? { primary, secondary }
46
48
  : undefined;
47
49
 
48
- const code = String((err as { code?: string; type?: string }).code ?? (err as { type?: string }).type ?? "");
50
+ code =
51
+ String((err as { code?: string; type?: string }).code ?? (err as { type?: string }).type ?? "") || undefined;
49
52
  const resetsAt = (err as { resets_at?: number }).resets_at ?? primary.resets_at ?? secondary.resets_at;
50
53
  const mins = resetsAt ? Math.max(0, Math.round((resetsAt * 1000 - Date.now()) / 60000)) : undefined;
51
54
 
52
- if (/usage_limit_reached|usage_not_included/i.test(code)) {
55
+ if (/usage_limit_reached|usage_not_included/i.test(code ?? "")) {
53
56
  const planType = (err as { plan_type?: string }).plan_type;
54
57
  const plan = planType ? ` (${String(planType).toLowerCase()} plan)` : "";
55
58
  const when = mins !== undefined ? ` Try again in ~${mins} min.` : "";
56
59
  friendlyMessage = `You have hit your ChatGPT usage limit${plan}.${when}`.trim();
57
- } else if (/rate_limit_exceeded/i.test(code) || response.status === 429) {
60
+ } else if (/rate_limit_exceeded/i.test(code ?? "") || response.status === 429) {
58
61
  const when = mins !== undefined ? ` Try again in ~${mins} min.` : "";
59
62
  friendlyMessage = `ChatGPT rate limit exceeded.${when}`.trim();
60
63
  }
@@ -68,6 +71,7 @@ export async function parseCodexError(response: Response): Promise<CodexErrorInf
68
71
  return {
69
72
  message,
70
73
  status: response.status,
74
+ ...(code ? { code } : {}),
71
75
  friendlyMessage,
72
76
  rateLimits,
73
77
  raw: raw,
@@ -401,6 +401,7 @@ function resetOutputState(output: AssistantMessage): void {
401
401
  output.content.length = 0;
402
402
  output.usage = createEmptyUsage();
403
403
  output.stopReason = "stop";
404
+ delete output.providerFailureCode;
404
405
  }
405
406
 
406
407
  function removeTransientBlockIndices(output: AssistantMessage): void {
@@ -1325,6 +1326,11 @@ async function handleCodexStreamFailure(
1325
1326
  }
1326
1327
  output.stopReason = context.options?.signal?.aborted ? "aborted" : "error";
1327
1328
  output.errorMessage = await finalizeErrorMessage(error, context.requestContext.rawRequestDump);
1329
+ output.providerFailureCode = sanitizeProviderFailureCode(
1330
+ error instanceof CodexProviderStreamError
1331
+ ? error.code
1332
+ : (error as { providerFailureCode?: unknown } | null)?.providerFailureCode,
1333
+ );
1328
1334
  output.duration = Date.now() - context.startTime;
1329
1335
  if (context.firstTokenTime) {
1330
1336
  output.ttft = context.firstTokenTime - context.startTime;
@@ -1937,8 +1943,10 @@ async function openCodexSseEventStream(
1937
1943
  if (!response.ok) {
1938
1944
  const info = await parseCodexError(response);
1939
1945
  const error = new Error(info.friendlyMessage || info.message);
1940
- (error as { headers?: Headers; status?: number }).headers = response.headers;
1941
- (error as { headers?: Headers; status?: number }).status = response.status;
1946
+ const details = error as { headers?: Headers; status?: number; providerFailureCode?: string };
1947
+ details.headers = response.headers;
1948
+ details.status = response.status;
1949
+ details.providerFailureCode = sanitizeProviderFailureCode(info.code);
1942
1950
  throw error;
1943
1951
  }
1944
1952
  if (!response.body) {
@@ -2301,6 +2309,10 @@ function getString(value: unknown): string | undefined {
2301
2309
  return typeof value === "string" ? value : undefined;
2302
2310
  }
2303
2311
 
2312
+ function sanitizeProviderFailureCode(value: unknown): string | undefined {
2313
+ return typeof value === "string" && value.length <= 128 && /^[A-Za-z0-9._-]+$/.test(value) ? value : undefined;
2314
+ }
2315
+
2304
2316
  class CodexProviderStreamError extends Error {
2305
2317
  readonly retryable: boolean;
2306
2318
  readonly code?: string;
@@ -2309,7 +2321,7 @@ class CodexProviderStreamError extends Error {
2309
2321
  super(message);
2310
2322
  this.name = "CodexProviderStreamError";
2311
2323
  this.retryable = retryable;
2312
- this.code = code;
2324
+ this.code = sanitizeProviderFailureCode(code);
2313
2325
  }
2314
2326
  }
2315
2327
 
@@ -2325,7 +2337,9 @@ function isRetryableCodexFailureEvent(rawEvent: Record<string, unknown>): boolea
2325
2337
  }
2326
2338
 
2327
2339
  function createCodexProviderStreamError(rawEvent: Record<string, unknown>): CodexProviderStreamError {
2328
- const code = getString(rawEvent.code) ?? "";
2340
+ const response = asRecord(rawEvent.response);
2341
+ const providerError = asRecord(rawEvent.error) ?? (response ? asRecord(response.error) : null);
2342
+ const code = getString(providerError?.code) ?? getString(providerError?.type) ?? getString(rawEvent.code) ?? "";
2329
2343
  const message = getString(rawEvent.message) ?? "";
2330
2344
  const formattedMessage =
2331
2345
  typeof rawEvent.type === "string" && rawEvent.type === "error"
package/src/types.ts CHANGED
@@ -393,6 +393,8 @@ export interface AssistantMessage {
393
393
  usage: Usage;
394
394
  stopReason: StopReason;
395
395
  errorMessage?: string;
396
+ /** Sanitized provider error identifier used for internal lifecycle classification. */
397
+ providerFailureCode?: string;
396
398
  /** Provider-native completion reason retained for diagnostics and transport-specific recovery. */
397
399
  rawStopReason?: string;
398
400
  /** Provider-specific opaque payload used to reconstruct transport-native history. */