@modelprofile.com/flexharness-models 9.3.0 → 9.3.1

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.
@@ -2,7 +2,8 @@ import { type IModelLimitInfo } from './smartai.limits.js';
2
2
  /**
3
3
  * `limit`: a usage or rate limit refused the call; `limit` describes it.
4
4
  * `http`: the provider answered with an HTTP error status.
5
- * `stream`: the provider reported an error inside a response stream that had already begun.
5
+ * `stream`: the provider answered with a response stream and reported an error inside it, before or
6
+ * after output began.
6
7
  * `response`: the provider answered, but its response could not be read or processed.
7
8
  * `network`: no response arrived because the connection failed.
8
9
  * `aborted`: an abort signal ended the call, a timeout signal included.
@@ -12,9 +13,15 @@ export type TModelFailureKind = 'limit' | 'http' | 'stream' | 'response' | 'netw
12
13
  /** Why a model call failed, without provider response text, headers or credentials. */
13
14
  export interface IModelFailureDescription {
14
15
  kind: TModelFailureKind;
15
- /** The HTTP status the provider answered with or, for a stream error, the status it states. */
16
+ /**
17
+ * The HTTP status the provider answered with. A `stream` failure has none: its answer was a
18
+ * success, and the status the AI SDK derives from the error's code is no HTTP answer.
19
+ */
16
20
  status?: number;
17
- /** The provider's machine-readable error code of a stream error, for example `server_error`. */
21
+ /**
22
+ * The provider's machine-readable name of a stream error: its code, for example `server_error`,
23
+ * or, for an error without one, its type, for example Anthropic's `overloaded_error`.
24
+ */
18
25
  providerCode?: string;
19
26
  /** The limit of a `limit` failure. */
20
27
  limit?: Readonly<IModelLimitInfo>;
@@ -22,6 +29,6 @@ export interface IModelFailureDescription {
22
29
  /**
23
30
  * Names why a model call failed, from the error a call threw or a stream reported. It follows
24
31
  * `cause` and the last attempt of a retried call, so a wrapped failure is told by its own kind.
25
- * The description holds only the kind, a status, a provider error code and a limit.
32
+ * The description holds only the kind, an HTTP status, a provider error code and a limit.
26
33
  */
27
34
  export declare function describeModelFailure(error: unknown): IModelFailureDescription;
@@ -1,5 +1,5 @@
1
1
  import * as plugins from './plugins.js';
2
- import { isModelLimitError } from './smartai.limits.js';
2
+ import { isModelLimitError, readJsonRecord } from './smartai.limits.js';
3
3
  /** How many causes are followed before a failure counts as unknown. */
4
4
  const MAX_CAUSE_DEPTH = 8;
5
5
  /** The longest provider code that is kept. */
@@ -36,21 +36,68 @@ function readProviderCode(value) {
36
36
  const code = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value;
37
37
  return typeof code === 'string' && isProviderCode(code) ? code : undefined;
38
38
  }
39
+ /** The media type of a response stream. */
40
+ const EVENT_STREAM_MEDIA_TYPE = 'text/event-stream';
41
+ /**
42
+ * Whether the call's answer was a response stream. The AI SDK reads a stream only from a successful
43
+ * answer; when the stream reports an error before output began, the provider throws an
44
+ * APICallError that carries the stream's headers and a status derived from the error's code.
45
+ */
46
+ function answeredWithStream(error) {
47
+ const contentType = Object.entries(error.responseHeaders ?? {})
48
+ .find(([name]) => name.toLowerCase() === 'content-type')?.[1];
49
+ return contentType?.trim().toLowerCase().startsWith(EVENT_STREAM_MEDIA_TYPE) === true;
50
+ }
51
+ function parseJson(text) {
52
+ if (text === undefined)
53
+ return undefined;
54
+ try {
55
+ return JSON.parse(text);
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
61
+ /** The `type` Anthropic and OpenAI Responses give an error event; it marks the event and names no failure. */
62
+ const ERROR_EVENT_TYPE = 'error';
63
+ /**
64
+ * The code in a provider's stream error frame. The error record is the frame's `response.error`
65
+ * (an OpenAI `response.failed` event), its `error` (an Anthropic or OpenAI error event) or the frame
66
+ * itself (a bare Anthropic error, an OpenAI Chat error or a flat OpenAI Responses error event). A
67
+ * record names its failure by `code` and, when it has none, by `type`, which is the only name
68
+ * Anthropic gives an error (`overloaded_error`); the `type` of a flat error event is the event's.
69
+ */
70
+ function readFrameCode(frame) {
71
+ const event = readJsonRecord(frame);
72
+ if (event === undefined)
73
+ return undefined;
74
+ const record = readJsonRecord(readJsonRecord(event.response)?.error) ?? readJsonRecord(event.error) ?? event;
75
+ const code = record.code ?? event.code;
76
+ if (code !== undefined && code !== null)
77
+ return code;
78
+ return record.type === ERROR_EVENT_TYPE ? undefined : record.type;
79
+ }
80
+ function describeStreamFailure(code) {
81
+ const providerCode = readProviderCode(code);
82
+ return providerCode === undefined ? { kind: 'stream' } : { kind: 'stream', providerCode };
83
+ }
39
84
  function describeKnownFailure(error) {
40
85
  if (isModelLimitError(error))
41
86
  return { kind: 'limit', limit: error.limit };
42
87
  if (plugins.isAbortError(error))
43
88
  return { kind: 'aborted' };
44
89
  if (plugins.StreamProviderError.isInstance(error) || plugins.isProviderStreamError(error)) {
45
- const status = readStatus(error.statusCode);
46
- const providerCode = readProviderCode(error.code);
47
- return {
48
- kind: 'stream',
49
- ...(status === undefined ? {} : { status }),
50
- ...(providerCode === undefined ? {} : { providerCode }),
51
- };
90
+ return describeStreamFailure(error.code ?? readFrameCode(error.data));
52
91
  }
53
92
  if (plugins.APICallError.isInstance(error)) {
93
+ if (answeredWithStream(error)) {
94
+ // A stream error frame always brings a status the SDK derived from its code; a stream error
95
+ // without one is an answer the SDK could not read as the API it called, such as Chat
96
+ // Completions chunks on the Responses API.
97
+ if (error.statusCode === undefined)
98
+ return { kind: 'response' };
99
+ return describeStreamFailure(readFrameCode(error.data ?? parseJson(error.responseBody)));
100
+ }
54
101
  const status = readStatus(error.statusCode);
55
102
  if (status === undefined)
56
103
  return { kind: 'network' };
@@ -61,7 +108,7 @@ function describeKnownFailure(error) {
61
108
  /**
62
109
  * Names why a model call failed, from the error a call threw or a stream reported. It follows
63
110
  * `cause` and the last attempt of a retried call, so a wrapped failure is told by its own kind.
64
- * The description holds only the kind, a status, a provider error code and a limit.
111
+ * The description holds only the kind, an HTTP status, a provider error code and a limit.
65
112
  */
66
113
  export function describeModelFailure(error) {
67
114
  const visited = new Set();
@@ -77,4 +124,4 @@ export function describeModelFailure(error) {
77
124
  }
78
125
  return { kind: 'unknown' };
79
126
  }
80
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5mYWlsdXJlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX21vZGVscy9zbWFydGFpLmZhaWx1cmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxpQkFBaUIsRUFBd0IsTUFBTSxxQkFBcUIsQ0FBQztBQXdCOUUsdUVBQXVFO0FBQ3ZFLE1BQU0sZUFBZSxHQUFHLENBQUMsQ0FBQztBQUUxQiw4Q0FBOEM7QUFDOUMsTUFBTSx3QkFBd0IsR0FBRyxFQUFFLENBQUM7QUFFcEM7Ozs7R0FJRztBQUNILE1BQU0sbUJBQW1CLEdBQUcsaUZBQWlGLENBQUM7QUFFOUcsb0RBQW9EO0FBQ3BELE1BQU0sdUJBQXVCLEdBQUcsbUZBQW1GLENBQUM7QUFFcEgsOEVBQThFO0FBQzlFLE1BQU0sb0JBQW9CLEdBQUcsRUFBRSxDQUFDO0FBRWhDLHFGQUFxRjtBQUNyRixNQUFNLDRCQUE0QixHQUFHLENBQUMsQ0FBQztBQUV2Qyx1R0FBdUc7QUFDdkcsU0FBUyxVQUFVLENBQUMsSUFBWTtJQUM5QixJQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsb0JBQW9CO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDckQsT0FBTyxJQUFJLENBQUMsTUFBTSxHQUFHLDRCQUE0QixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDckcsQ0FBQztBQUVELHVHQUF1RztBQUN2RyxTQUFTLGNBQWMsQ0FBQyxJQUFZO0lBQ2xDLE9BQU8sSUFBSSxDQUFDLE1BQU0sSUFBSSx3QkFBd0I7V0FDekMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztXQUM5QixDQUFDLHVCQUF1QixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7V0FDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7QUFDNUMsQ0FBQztBQUVELFNBQVMsVUFBVSxDQUFDLEtBQXlCO0lBQzNDLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLE1BQU0sQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLElBQUksS0FBSyxJQUFJLEdBQUcsSUFBSSxLQUFLLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUNsSCxDQUFDO0FBRUQsU0FBUyxnQkFBZ0IsQ0FBQyxLQUFrQztJQUMxRCxNQUFNLElBQUksR0FBRyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDOUYsT0FBTyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUM3RSxDQUFDO0FBRUQsU0FBUyxvQkFBb0IsQ0FBQyxLQUFjO0lBQzFDLElBQUksaUJBQWlCLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztJQUMzRSxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsS0FBSyxDQUFDO1FBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsQ0FBQztJQUM1RCxJQUFJLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLElBQUksT0FBTyxDQUFDLHFCQUFxQixDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7UUFDMUYsTUFBTSxNQUFNLEdBQUcsVUFBVSxDQUFDLEtBQUssQ0FBQyxVQUFVLENBQUMsQ0FBQztRQUM1QyxNQUFNLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEQsT0FBTztZQUNMLElBQUksRUFBRSxRQUFRO1lBQ2QsR0FBRyxDQUFDLE1BQU0sS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztZQUMzQyxHQUFHLENBQUMsWUFBWSxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLFlBQVksRUFBRSxDQUFDO1NBQ3hELENBQUM7SUFDSixDQUFDO0lBQ0QsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzNDLE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxLQUFLLENBQUMsVUFBVSxDQUFDLENBQUM7UUFDNUMsSUFBSSxNQUFNLEtBQUssU0FBUztZQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7UUFDckQsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUMvRCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxNQUFNLFVBQVUsb0JBQW9CLENBQUMsS0FBYztJQUNqRCxNQUFNLE9BQU8sR0FBRyxJQUFJLEdBQUcsRUFBVyxDQUFDO0lBQ25DLElBQUksT0FBTyxHQUFHLEtBQUssQ0FBQztJQUNwQixLQUFLLElBQUksS0FBSyxHQUFHLENBQUMsRUFBRSxLQUFLLElBQUksZUFBZSxJQUFJLE9BQU8sS0FBSyxTQUFTLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEtBQUssSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUMzRyxPQUFPLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ3JCLE1BQU0sV0FBVyxHQUFHLG9CQUFvQixDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2xELElBQUksV0FBVztZQUFFLE9BQU8sV0FBVyxDQUFDO1FBQ3BDLE9BQU8sR0FBRyxPQUFPLENBQUMsVUFBVSxDQUFDLFVBQVUsQ0FBQyxPQUFPLENBQUM7WUFDOUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTO1lBQ25CLENBQUMsQ0FBQyxPQUFPLFlBQVksS0FBSyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDM0QsQ0FBQztJQUNELE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7QUFDN0IsQ0FBQyJ9
127
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5mYWlsdXJlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX21vZGVscy9zbWFydGFpLmZhaWx1cmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxjQUFjLEVBQXdCLE1BQU0scUJBQXFCLENBQUM7QUErQjlGLHVFQUF1RTtBQUN2RSxNQUFNLGVBQWUsR0FBRyxDQUFDLENBQUM7QUFFMUIsOENBQThDO0FBQzlDLE1BQU0sd0JBQXdCLEdBQUcsRUFBRSxDQUFDO0FBRXBDOzs7O0dBSUc7QUFDSCxNQUFNLG1CQUFtQixHQUFHLGlGQUFpRixDQUFDO0FBRTlHLG9EQUFvRDtBQUNwRCxNQUFNLHVCQUF1QixHQUFHLG1GQUFtRixDQUFDO0FBRXBILDhFQUE4RTtBQUM5RSxNQUFNLG9CQUFvQixHQUFHLEVBQUUsQ0FBQztBQUVoQyxxRkFBcUY7QUFDckYsTUFBTSw0QkFBNEIsR0FBRyxDQUFDLENBQUM7QUFFdkMsdUdBQXVHO0FBQ3ZHLFNBQVMsVUFBVSxDQUFDLElBQVk7SUFDOUIsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLG9CQUFvQjtRQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3JELE9BQU8sSUFBSSxDQUFDLE1BQU0sR0FBRyw0QkFBNEIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0FBQ3JHLENBQUM7QUFFRCx1R0FBdUc7QUFDdkcsU0FBUyxjQUFjLENBQUMsSUFBWTtJQUNsQyxPQUFPLElBQUksQ0FBQyxNQUFNLElBQUksd0JBQXdCO1dBQ3pDLG1CQUFtQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7V0FDOUIsQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1dBQ25DLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0FBQzVDLENBQUM7QUFFRCxTQUFTLFVBQVUsQ0FBQyxLQUF5QjtJQUMzQyxPQUFPLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxNQUFNLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssSUFBSSxHQUFHLElBQUksS0FBSyxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7QUFDbEgsQ0FBQztBQUVELFNBQVMsZ0JBQWdCLENBQUMsS0FBYztJQUN0QyxNQUFNLElBQUksR0FBRyxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUM7SUFDOUYsT0FBTyxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQztBQUM3RSxDQUFDO0FBRUQsMkNBQTJDO0FBQzNDLE1BQU0sdUJBQXVCLEdBQUcsbUJBQW1CLENBQUM7QUFFcEQ7Ozs7R0FJRztBQUNILFNBQVMsa0JBQWtCLENBQUMsS0FBMkI7SUFDckQsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsZUFBZSxJQUFJLEVBQUUsQ0FBQztTQUM1RCxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxFQUFFLEtBQUssY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNoRSxPQUFPLFdBQVcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxVQUFVLENBQUMsdUJBQXVCLENBQUMsS0FBSyxJQUFJLENBQUM7QUFDeEYsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLElBQXdCO0lBQ3pDLElBQUksSUFBSSxLQUFLLFNBQVM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUN6QyxJQUFJLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDMUIsQ0FBQztJQUFDLE1BQU0sQ0FBQztRQUNQLE9BQU8sU0FBUyxDQUFDO0lBQ25CLENBQUM7QUFDSCxDQUFDO0FBRUQsOEdBQThHO0FBQzlHLE1BQU0sZ0JBQWdCLEdBQUcsT0FBTyxDQUFDO0FBRWpDOzs7Ozs7R0FNRztBQUNILFNBQVMsYUFBYSxDQUFDLEtBQWM7SUFDbkMsTUFBTSxLQUFLLEdBQUcsY0FBYyxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ3BDLElBQUksS0FBSyxLQUFLLFNBQVM7UUFBRSxPQUFPLFNBQVMsQ0FBQztJQUMxQyxNQUFNLE1BQU0sR0FBRyxjQUFjLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsRUFBRSxLQUFLLENBQUMsSUFBSSxjQUFjLENBQUMsS0FBSyxDQUFDLEtBQUssQ0FBQyxJQUFJLEtBQUssQ0FBQztJQUM3RyxNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxJQUFJLEtBQUssQ0FBQyxJQUFJLENBQUM7SUFDdkMsSUFBSSxJQUFJLEtBQUssU0FBUyxJQUFJLElBQUksS0FBSyxJQUFJO1FBQUUsT0FBTyxJQUFJLENBQUM7SUFDckQsT0FBTyxNQUFNLENBQUMsSUFBSSxLQUFLLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUM7QUFDcEUsQ0FBQztBQUVELFNBQVMscUJBQXFCLENBQUMsSUFBYTtJQUMxQyxNQUFNLFlBQVksR0FBRyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM1QyxPQUFPLFlBQVksS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLENBQUM7QUFDNUYsQ0FBQztBQUVELFNBQVMsb0JBQW9CLENBQUMsS0FBYztJQUMxQyxJQUFJLGlCQUFpQixDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDM0UsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLEtBQUssQ0FBQztRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDNUQsSUFBSSxPQUFPLENBQUMsbUJBQW1CLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxJQUFJLE9BQU8sQ0FBQyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQzFGLE9BQU8scUJBQXFCLENBQUMsS0FBSyxDQUFDLElBQUksSUFBSSxhQUFhLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDeEUsQ0FBQztJQUNELElBQUksT0FBTyxDQUFDLFlBQVksQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUMzQyxJQUFJLGtCQUFrQixDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDOUIsNEZBQTRGO1lBQzVGLHFGQUFxRjtZQUNyRiwyQ0FBMkM7WUFDM0MsSUFBSSxLQUFLLENBQUMsVUFBVSxLQUFLLFNBQVM7Z0JBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsQ0FBQztZQUNoRSxPQUFPLHFCQUFxQixDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsSUFBSSxJQUFJLFNBQVMsQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQzNGLENBQUM7UUFDRCxNQUFNLE1BQU0sR0FBRyxVQUFVLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzVDLElBQUksTUFBTSxLQUFLLFNBQVM7WUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO1FBQ3JELE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxVQUFVLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFDL0QsQ0FBQztJQUNELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLG9CQUFvQixDQUFDLEtBQWM7SUFDakQsTUFBTSxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQVcsQ0FBQztJQUNuQyxJQUFJLE9BQU8sR0FBRyxLQUFLLENBQUM7SUFDcEIsS0FBSyxJQUFJLEtBQUssR0FBRyxDQUFDLEVBQUUsS0FBSyxJQUFJLGVBQWUsSUFBSSxPQUFPLEtBQUssU0FBUyxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsRUFBRSxLQUFLLElBQUksQ0FBQyxFQUFFLENBQUM7UUFDM0csT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNyQixNQUFNLFdBQVcsR0FBRyxvQkFBb0IsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsRCxJQUFJLFdBQVc7WUFBRSxPQUFPLFdBQVcsQ0FBQztRQUNwQyxPQUFPLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDO1lBQzlDLENBQUMsQ0FBQyxPQUFPLENBQUMsU0FBUztZQUNuQixDQUFDLENBQUMsT0FBTyxZQUFZLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO0lBQzNELENBQUM7SUFDRCxPQUFPLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxDQUFDO0FBQzdCLENBQUMifQ==
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Task Venture Capital GmbH",
3
3
  "license": "MIT",
4
4
  "name": "@modelprofile.com/flexharness-models",
5
- "version": "9.3.0",
5
+ "version": "9.3.1",
6
6
  "type": "module",
7
7
  "description": "Provider-independent model contracts, prompt caching and explicit model registration.",
8
8
  "main": "./dist_ts_models/index.js",
package/readme.md CHANGED
@@ -28,8 +28,11 @@ limit. `createModelLimitInfo`, `isModelLimitError`, `isModelLimitInfo`, `describ
28
28
  `describeModelFailure(error)` returns an `IModelFailureDescription` that names why a model call
29
29
  failed and never holds provider response text, headers or credentials. Its `kind`
30
30
  (`TModelFailureKind`) is `limit` (with the `limit`), `http` (with the answer's `status`), `stream`
31
- (an error reported after the response stream began, with the `status` and `providerCode` the
32
- provider states), `response` (an answer that could not be processed, with its `status`),
31
+ (an error a successful response stream reported before or after output began, with the
32
+ `providerCode` the provider states, its error `code` or, for an error with none such as
33
+ Anthropic's, its error `type` (`overloaded_error`), and no `status`: the answer succeeded, and
34
+ the HTTP-equivalent status the AI SDK derives from the code, such as 500 for `server_error`, is
35
+ no HTTP answer), `response` (an answer that could not be processed, with its `status`),
33
36
  `network` (no answer arrived), `aborted` (an abort or timeout signal) or `unknown`. A provider code
34
37
  is kept only in a shape providers name errors in (snake_case words, optionally dotted, such as
35
38
  `insufficient_quota`; upper-case status names such as `RESOURCE_EXHAUSTED`; numeric codes) of at
@@ -28,8 +28,11 @@ limit. `createModelLimitInfo`, `isModelLimitError`, `isModelLimitInfo`, `describ
28
28
  `describeModelFailure(error)` returns an `IModelFailureDescription` that names why a model call
29
29
  failed and never holds provider response text, headers or credentials. Its `kind`
30
30
  (`TModelFailureKind`) is `limit` (with the `limit`), `http` (with the answer's `status`), `stream`
31
- (an error reported after the response stream began, with the `status` and `providerCode` the
32
- provider states), `response` (an answer that could not be processed, with its `status`),
31
+ (an error a successful response stream reported before or after output began, with the
32
+ `providerCode` the provider states, its error `code` or, for an error with none such as
33
+ Anthropic's, its error `type` (`overloaded_error`), and no `status`: the answer succeeded, and
34
+ the HTTP-equivalent status the AI SDK derives from the code, such as 500 for `server_error`, is
35
+ no HTTP answer), `response` (an answer that could not be processed, with its `status`),
33
36
  `network` (no answer arrived), `aborted` (an abort or timeout signal) or `unknown`. A provider code
34
37
  is kept only in a shape providers name errors in (snake_case words, optionally dotted, such as
35
38
  `insufficient_quota`; upper-case status names such as `RESOURCE_EXHAUSTED`; numeric codes) of at
@@ -1,10 +1,11 @@
1
1
  import * as plugins from './plugins.js';
2
- import { isModelLimitError, type IModelLimitInfo } from './smartai.limits.js';
2
+ import { isModelLimitError, readJsonRecord, type IModelLimitInfo } from './smartai.limits.js';
3
3
 
4
4
  /**
5
5
  * `limit`: a usage or rate limit refused the call; `limit` describes it.
6
6
  * `http`: the provider answered with an HTTP error status.
7
- * `stream`: the provider reported an error inside a response stream that had already begun.
7
+ * `stream`: the provider answered with a response stream and reported an error inside it, before or
8
+ * after output began.
8
9
  * `response`: the provider answered, but its response could not be read or processed.
9
10
  * `network`: no response arrived because the connection failed.
10
11
  * `aborted`: an abort signal ended the call, a timeout signal included.
@@ -15,9 +16,15 @@ export type TModelFailureKind = 'limit' | 'http' | 'stream' | 'response' | 'netw
15
16
  /** Why a model call failed, without provider response text, headers or credentials. */
16
17
  export interface IModelFailureDescription {
17
18
  kind: TModelFailureKind;
18
- /** The HTTP status the provider answered with or, for a stream error, the status it states. */
19
+ /**
20
+ * The HTTP status the provider answered with. A `stream` failure has none: its answer was a
21
+ * success, and the status the AI SDK derives from the error's code is no HTTP answer.
22
+ */
19
23
  status?: number;
20
- /** The provider's machine-readable error code of a stream error, for example `server_error`. */
24
+ /**
25
+ * The provider's machine-readable name of a stream error: its code, for example `server_error`,
26
+ * or, for an error without one, its type, for example Anthropic's `overloaded_error`.
27
+ */
21
28
  providerCode?: string;
22
29
  /** The limit of a `limit` failure. */
23
30
  limit?: Readonly<IModelLimitInfo>;
@@ -63,24 +70,72 @@ function readStatus(value: number | undefined): number | undefined {
63
70
  return typeof value === 'number' && Number.isInteger(value) && value >= 100 && value <= 599 ? value : undefined;
64
71
  }
65
72
 
66
- function readProviderCode(value: string | number | undefined): string | undefined {
73
+ function readProviderCode(value: unknown): string | undefined {
67
74
  const code = typeof value === 'number' && Number.isSafeInteger(value) ? String(value) : value;
68
75
  return typeof code === 'string' && isProviderCode(code) ? code : undefined;
69
76
  }
70
77
 
78
+ /** The media type of a response stream. */
79
+ const EVENT_STREAM_MEDIA_TYPE = 'text/event-stream';
80
+
81
+ /**
82
+ * Whether the call's answer was a response stream. The AI SDK reads a stream only from a successful
83
+ * answer; when the stream reports an error before output began, the provider throws an
84
+ * APICallError that carries the stream's headers and a status derived from the error's code.
85
+ */
86
+ function answeredWithStream(error: plugins.APICallError): boolean {
87
+ const contentType = Object.entries(error.responseHeaders ?? {})
88
+ .find(([name]) => name.toLowerCase() === 'content-type')?.[1];
89
+ return contentType?.trim().toLowerCase().startsWith(EVENT_STREAM_MEDIA_TYPE) === true;
90
+ }
91
+
92
+ function parseJson(text: string | undefined): unknown {
93
+ if (text === undefined) return undefined;
94
+ try {
95
+ return JSON.parse(text);
96
+ } catch {
97
+ return undefined;
98
+ }
99
+ }
100
+
101
+ /** The `type` Anthropic and OpenAI Responses give an error event; it marks the event and names no failure. */
102
+ const ERROR_EVENT_TYPE = 'error';
103
+
104
+ /**
105
+ * The code in a provider's stream error frame. The error record is the frame's `response.error`
106
+ * (an OpenAI `response.failed` event), its `error` (an Anthropic or OpenAI error event) or the frame
107
+ * itself (a bare Anthropic error, an OpenAI Chat error or a flat OpenAI Responses error event). A
108
+ * record names its failure by `code` and, when it has none, by `type`, which is the only name
109
+ * Anthropic gives an error (`overloaded_error`); the `type` of a flat error event is the event's.
110
+ */
111
+ function readFrameCode(frame: unknown): unknown {
112
+ const event = readJsonRecord(frame);
113
+ if (event === undefined) return undefined;
114
+ const record = readJsonRecord(readJsonRecord(event.response)?.error) ?? readJsonRecord(event.error) ?? event;
115
+ const code = record.code ?? event.code;
116
+ if (code !== undefined && code !== null) return code;
117
+ return record.type === ERROR_EVENT_TYPE ? undefined : record.type;
118
+ }
119
+
120
+ function describeStreamFailure(code: unknown): IModelFailureDescription {
121
+ const providerCode = readProviderCode(code);
122
+ return providerCode === undefined ? { kind: 'stream' } : { kind: 'stream', providerCode };
123
+ }
124
+
71
125
  function describeKnownFailure(error: unknown): IModelFailureDescription | undefined {
72
126
  if (isModelLimitError(error)) return { kind: 'limit', limit: error.limit };
73
127
  if (plugins.isAbortError(error)) return { kind: 'aborted' };
74
128
  if (plugins.StreamProviderError.isInstance(error) || plugins.isProviderStreamError(error)) {
75
- const status = readStatus(error.statusCode);
76
- const providerCode = readProviderCode(error.code);
77
- return {
78
- kind: 'stream',
79
- ...(status === undefined ? {} : { status }),
80
- ...(providerCode === undefined ? {} : { providerCode }),
81
- };
129
+ return describeStreamFailure(error.code ?? readFrameCode(error.data));
82
130
  }
83
131
  if (plugins.APICallError.isInstance(error)) {
132
+ if (answeredWithStream(error)) {
133
+ // A stream error frame always brings a status the SDK derived from its code; a stream error
134
+ // without one is an answer the SDK could not read as the API it called, such as Chat
135
+ // Completions chunks on the Responses API.
136
+ if (error.statusCode === undefined) return { kind: 'response' };
137
+ return describeStreamFailure(readFrameCode(error.data ?? parseJson(error.responseBody)));
138
+ }
84
139
  const status = readStatus(error.statusCode);
85
140
  if (status === undefined) return { kind: 'network' };
86
141
  return { kind: status >= 400 ? 'http' : 'response', status };
@@ -91,7 +146,7 @@ function describeKnownFailure(error: unknown): IModelFailureDescription | undefi
91
146
  /**
92
147
  * Names why a model call failed, from the error a call threw or a stream reported. It follows
93
148
  * `cause` and the last attempt of a retried call, so a wrapped failure is told by its own kind.
94
- * The description holds only the kind, a status, a provider error code and a limit.
149
+ * The description holds only the kind, an HTTP status, a provider error code and a limit.
95
150
  */
96
151
  export function describeModelFailure(error: unknown): IModelFailureDescription {
97
152
  const visited = new Set<unknown>();