@nathapp/nax-ai 0.1.7 → 0.1.8

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.
@@ -15,11 +15,22 @@ export declare function classifyHttpError(status: number | undefined): ProtocolE
15
15
  * status and no message, and widening it with a second input would make every
16
16
  * caller supply one. This is the message-aware layer above it.
17
17
  *
18
- * Only `bad-request` is refined. A status with a verdict of its own keeps it —
19
- * a 429 mentioning tokens is a rate limit, and the caller should wait rather
20
- * than compact. An absent status stays `unknown` for the same reason: an
21
- * overflow always arrives with a response, so declining to guess costs
22
- * nothing.
18
+ * Two refinements, and only these two.
19
+ *
20
+ * `bad-request` is refined to `context-overflow` by message. A status with a
21
+ * verdict of its own keeps it — a 429 mentioning tokens is a rate limit, and
22
+ * the caller should wait rather than compact.
23
+ *
24
+ * A non-failing status is refined to `transport` (nax#1869). An error event
25
+ * that arrives under a 200 means the response headers were fine and the stream
26
+ * broke afterwards; that is a transport fault, not the "unknown" the bare
27
+ * status table has to answer. It is the shape OpenRouter's "Upstream idle
28
+ * timeout exceeded" takes, and reading it as unknown cost a real run both its
29
+ * agent swap and nax-ai's own bounded retry, since both key on `transport`.
30
+ * An absent status gets the same answer for consistency with `classifyThrown`,
31
+ * the sibling path for the identical fault. Neither can become
32
+ * `context-overflow`: an overflow always arrives on a failing status, so
33
+ * declining to guess one here still costs nothing.
23
34
  */
24
35
  export declare function classifyProviderError(status: number | undefined, message: string | undefined): ProtocolErrorKind;
25
36
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/protocols/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAQ/E;AA4BD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAMhH;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASzG;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAM5D"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/protocols/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEnE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAQ/E;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,iBAAiB,CAWhH;AAaD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CASzG;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,CAM5D"}
@@ -45,6 +45,29 @@ const CONTEXT_OVERFLOW_MARKERS = [
45
45
  "reduce the length of the messages",
46
46
  "too many tokens",
47
47
  ];
48
+ /**
49
+ * Substrings that identify a mid-stream error carrying policy content,
50
+ * lowercased. Read only for a stream that broke under a non-failing status —
51
+ * see `classifyProviderError`.
52
+ *
53
+ * These exist because §10.1 hands rate-limit and overload capacity to the
54
+ * consumer and forbids retrying them inside nax-ai. Without them, a provider
55
+ * that reports a quota exhaustion mid-stream under a 200 would be filed as
56
+ * `transport` and retried here, which is exactly the policy the spec reserves
57
+ * for the caller. Same caveat as the overflow table above: upstream wire
58
+ * strings, one test per phrasing, drift shows up as a failing case.
59
+ */
60
+ const STREAM_POLICY_MARKERS = [
61
+ ["rate limit", "rate-limit"],
62
+ ["rate_limit", "rate-limit"],
63
+ ["too many requests", "rate-limit"],
64
+ ["quota", "rate-limit"],
65
+ ["overloaded", "overloaded"],
66
+ ];
67
+ /** A status that is not itself a report of failure: absent, or below 4xx. */
68
+ function isNonFailingStatus(status) {
69
+ return status === undefined || status < 400;
70
+ }
48
71
  /**
49
72
  * Classifies a provider error from its status and its upstream message.
50
73
  *
@@ -53,19 +76,44 @@ const CONTEXT_OVERFLOW_MARKERS = [
53
76
  * status and no message, and widening it with a second input would make every
54
77
  * caller supply one. This is the message-aware layer above it.
55
78
  *
56
- * Only `bad-request` is refined. A status with a verdict of its own keeps it —
57
- * a 429 mentioning tokens is a rate limit, and the caller should wait rather
58
- * than compact. An absent status stays `unknown` for the same reason: an
59
- * overflow always arrives with a response, so declining to guess costs
60
- * nothing.
79
+ * Two refinements, and only these two.
80
+ *
81
+ * `bad-request` is refined to `context-overflow` by message. A status with a
82
+ * verdict of its own keeps it — a 429 mentioning tokens is a rate limit, and
83
+ * the caller should wait rather than compact.
84
+ *
85
+ * A non-failing status is refined to `transport` (nax#1869). An error event
86
+ * that arrives under a 200 means the response headers were fine and the stream
87
+ * broke afterwards; that is a transport fault, not the "unknown" the bare
88
+ * status table has to answer. It is the shape OpenRouter's "Upstream idle
89
+ * timeout exceeded" takes, and reading it as unknown cost a real run both its
90
+ * agent swap and nax-ai's own bounded retry, since both key on `transport`.
91
+ * An absent status gets the same answer for consistency with `classifyThrown`,
92
+ * the sibling path for the identical fault. Neither can become
93
+ * `context-overflow`: an overflow always arrives on a failing status, so
94
+ * declining to guess one here still costs nothing.
61
95
  */
62
96
  export function classifyProviderError(status, message) {
63
97
  const kind = classifyHttpError(status);
98
+ if (isNonFailingStatus(status)) {
99
+ return classifyBrokenStream(message);
100
+ }
64
101
  if (kind !== "bad-request" || message === undefined)
65
102
  return kind;
66
103
  const haystack = message.toLowerCase();
67
104
  return CONTEXT_OVERFLOW_MARKERS.some((marker) => haystack.includes(marker)) ? "context-overflow" : kind;
68
105
  }
106
+ /**
107
+ * The kind for an error event whose status reported no failure: `transport`,
108
+ * unless the message carries policy content nax-ai must not retry itself.
109
+ */
110
+ function classifyBrokenStream(message) {
111
+ if (message === undefined)
112
+ return "transport";
113
+ const haystack = message.toLowerCase();
114
+ const policy = STREAM_POLICY_MARKERS.find(([marker]) => haystack.includes(marker));
115
+ return policy ? policy[1] : "transport";
116
+ }
69
117
  /**
70
118
  * Reads `retry-after` as a delay in seconds.
71
119
  *
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/protocols/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,UAAU,iBAAiB,CAAC,MAA0B;IAC1D,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC3C,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC;IACpD,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,YAAY,CAAC;IACxC,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,YAAY,CAAC;IAC1D,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG;QAAE,OAAO,aAAa,CAAC;IACxD,IAAI,MAAM,IAAI,GAAG;QAAE,OAAO,WAAW,CAAC;IACtC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,wBAAwB,GAAsB;IAClD,yBAAyB;IACzB,yBAAyB;IACzB,wBAAwB;IACxB,sBAAsB;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,sCAAsC;IACtC,mCAAmC;IACnC,iBAAiB;CAClB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAA0B,EAAE,OAA2B;IAC3F,MAAM,IAAI,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,IAAI,KAAK,aAAa,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEjE,MAAM,QAAQ,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACvC,OAAO,wBAAwB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1G,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,OAAqD;IACnF,IAAI,OAAO,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE5C,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,aAAa,CAAC,CAAC;IAC7F,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE1C,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/D,KAAK;KACN,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/protocols/errors.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax-ai",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Provider-agnostic LLM client: completions, streaming, tool calls, usage and auth across API-key and OAuth providers.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",