@plurnk/plurnk-providers 1.5.0 → 1.6.0
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/.env.defaults +36 -22
- package/SPEC.md +133 -59
- package/dist/AiSdkProvider.d.ts +19 -26
- package/dist/AiSdkProvider.d.ts.map +1 -1
- package/dist/AiSdkProvider.js +318 -106
- package/dist/AiSdkProvider.js.map +1 -1
- package/dist/Mock.d.ts +4 -9
- package/dist/Mock.d.ts.map +1 -1
- package/dist/Mock.js +36 -9
- package/dist/Mock.js.map +1 -1
- package/dist/Pool.d.ts +2 -21
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +19 -14
- package/dist/Pool.js.map +1 -1
- package/dist/accounting.d.ts +5 -2
- package/dist/accounting.d.ts.map +1 -1
- package/dist/accounting.js +100 -16
- package/dist/accounting.js.map +1 -1
- package/dist/aiSdkTransport.d.ts +9 -2
- package/dist/aiSdkTransport.d.ts.map +1 -1
- package/dist/aiSdkTransport.js +160 -62
- package/dist/aiSdkTransport.js.map +1 -1
- package/dist/catalogProvider.d.ts +7 -3
- package/dist/catalogProvider.d.ts.map +1 -1
- package/dist/catalogProvider.js +30 -24
- package/dist/catalogProvider.js.map +1 -1
- package/dist/compatibleProvider.d.ts.map +1 -1
- package/dist/compatibleProvider.js +18 -7
- package/dist/compatibleProvider.js.map +1 -1
- package/dist/cost.d.ts +10 -10
- package/dist/cost.d.ts.map +1 -1
- package/dist/cost.js +90 -42
- package/dist/cost.js.map +1 -1
- package/dist/env.d.ts +5 -1
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +30 -10
- package/dist/env.js.map +1 -1
- package/dist/errors.d.ts +14 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +58 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/ollama.js +3 -3
- package/dist/ollama.js.map +1 -1
- package/dist/sdkModels.d.ts +6 -2
- package/dist/sdkModels.d.ts.map +1 -1
- package/dist/sdkModels.js +38 -5
- package/dist/sdkModels.js.map +1 -1
- package/dist/types.d.ts +33 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/usage.d.ts +21 -5
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +164 -83
- package/dist/usage.js.map +1 -1
- package/package.json +7 -6
- package/src/AiSdkProvider.test.ts +788 -191
- package/src/AiSdkProvider.ts +381 -124
- package/src/Mock.test.ts +37 -12
- package/src/Mock.ts +45 -14
- package/src/Pool.test.ts +19 -6
- package/src/Pool.ts +20 -16
- package/src/ProviderRegistry.test.ts +16 -11
- package/src/accounting.test.ts +58 -22
- package/src/accounting.ts +120 -18
- package/src/aiSdkTransport.test.ts +42 -49
- package/src/aiSdkTransport.ts +174 -62
- package/src/boundaries.test.ts +1 -0
- package/src/catalogProvider.test.ts +258 -22
- package/src/catalogProvider.ts +42 -27
- package/src/compatibleProvider.test.ts +6 -3
- package/src/compatibleProvider.ts +20 -7
- package/src/cost.test.ts +55 -36
- package/src/cost.ts +111 -50
- package/src/defaults.test.ts +13 -3
- package/src/env.test.ts +54 -5
- package/src/env.ts +43 -18
- package/src/errors.test.ts +47 -2
- package/src/errors.ts +67 -3
- package/src/index.ts +21 -5
- package/src/ollama.test.ts +4 -1
- package/src/ollama.ts +3 -3
- package/src/sdkModels.test.ts +76 -4
- package/src/sdkModels.ts +45 -7
- package/src/types.ts +77 -38
- package/src/usage.test.ts +112 -116
- package/src/usage.ts +209 -93
package/src/aiSdkTransport.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { createOpenAICompatible, type ProviderErrorStructure } from "@ai-sdk/openai-compatible";
|
|
2
2
|
import { APICallError, generateText, streamText, type JSONValue, type LanguageModel, type LanguageModelUsage } from "ai";
|
|
3
|
-
import { prepareRetries } from "ai/internal";
|
|
4
3
|
import { z } from "zod/v4";
|
|
5
4
|
import type { ChatMessage, ProviderAttemptFinishReason, ProviderChargeEvidence, ProviderUsage, TokenLogprob } from "./types.ts";
|
|
6
5
|
import { normalizeUsage, type RawUsage } from "./usage.ts";
|
|
7
6
|
import { emitWarningOnce } from "./warnings.ts";
|
|
7
|
+
import { ProviderTimeoutError, providerTimeoutOf } from "./errors.ts";
|
|
8
8
|
|
|
9
9
|
const errorSchema = z.object({
|
|
10
10
|
error: z.object({
|
|
@@ -15,18 +15,30 @@ const errorSchema = z.object({
|
|
|
15
15
|
}).passthrough(),
|
|
16
16
|
}).passthrough();
|
|
17
17
|
|
|
18
|
+
const retryDirective = (
|
|
19
|
+
status: number | undefined,
|
|
20
|
+
headers: Headers | Readonly<Record<string, string>>,
|
|
21
|
+
): boolean | null => {
|
|
22
|
+
const raw = headers instanceof Headers
|
|
23
|
+
? headers.get("x-should-retry")
|
|
24
|
+
: Object.entries(headers).find(([name]) => name.toLowerCase() === "x-should-retry")?.[1];
|
|
25
|
+
const directive = raw?.trim().toLowerCase();
|
|
26
|
+
if (directive === "false") return false;
|
|
27
|
+
if (directive === "true") return true;
|
|
28
|
+
if (status !== undefined && status >= 520 && status <= 527) return false;
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
18
32
|
const errorStructure: ProviderErrorStructure<z.infer<typeof errorSchema>> = {
|
|
19
33
|
errorSchema,
|
|
20
34
|
errorToMessage: ({ error }) => error.message,
|
|
21
35
|
isRetryable(response) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (directive === "true") return true;
|
|
25
|
-
if (response.status >= 520 && response.status <= 527) return false;
|
|
26
|
-
return response.status === 408
|
|
36
|
+
return retryDirective(response.status, response.headers) ?? (
|
|
37
|
+
response.status === 408
|
|
27
38
|
|| response.status === 409
|
|
28
39
|
|| response.status === 429
|
|
29
|
-
|| response.status >= 500
|
|
40
|
+
|| response.status >= 500
|
|
41
|
+
);
|
|
30
42
|
},
|
|
31
43
|
};
|
|
32
44
|
|
|
@@ -41,30 +53,29 @@ const baseUrl = (completionUrl: string): string => {
|
|
|
41
53
|
|
|
42
54
|
const usageOf = (
|
|
43
55
|
usage: LanguageModelUsage,
|
|
44
|
-
|
|
45
|
-
contentText: string,
|
|
46
|
-
): ProviderUsage => normalizeUsage({
|
|
56
|
+
): ProviderUsage | undefined => normalizeUsage({
|
|
47
57
|
prompt_tokens: usage.inputTokens,
|
|
48
58
|
completion_tokens: usage.outputTokens,
|
|
49
59
|
total_tokens: usage.totalTokens,
|
|
50
|
-
prompt_tokens_details: {
|
|
60
|
+
prompt_tokens_details: {
|
|
61
|
+
cached_tokens: usage.inputTokenDetails.cacheReadTokens,
|
|
62
|
+
cache_write_tokens: usage.inputTokenDetails.cacheWriteTokens,
|
|
63
|
+
},
|
|
51
64
|
completion_tokens_details: usage.outputTokenDetails.reasoningTokens !== undefined
|
|
52
65
|
? { reasoning_tokens: usage.outputTokenDetails.reasoningTokens }
|
|
53
66
|
: undefined,
|
|
54
|
-
}
|
|
67
|
+
});
|
|
55
68
|
|
|
56
69
|
const wireUsageOf = (
|
|
57
70
|
values: readonly unknown[],
|
|
58
|
-
|
|
59
|
-
contentText: string,
|
|
60
|
-
): ProviderUsage | null => {
|
|
71
|
+
): ProviderUsage | undefined => {
|
|
61
72
|
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
62
73
|
const usage = recordOf(values[index])?.usage;
|
|
63
74
|
if (usage !== null && typeof usage === "object") {
|
|
64
|
-
return normalizeUsage(usage as RawUsage
|
|
75
|
+
return normalizeUsage(usage as RawUsage);
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
|
-
return
|
|
78
|
+
return undefined;
|
|
68
79
|
};
|
|
69
80
|
|
|
70
81
|
const wireUsageEvidenceOf = (values: readonly unknown[]): unknown => {
|
|
@@ -75,6 +86,14 @@ const wireUsageEvidenceOf = (values: readonly unknown[]): unknown => {
|
|
|
75
86
|
return undefined;
|
|
76
87
|
};
|
|
77
88
|
|
|
89
|
+
const wireChargeEvidenceOf = (values: readonly unknown[]): unknown => {
|
|
90
|
+
for (let index = values.length - 1; index >= 0; index -= 1) {
|
|
91
|
+
const record = recordOf(values[index]);
|
|
92
|
+
if (record !== null && record.charge !== undefined) return record.charge;
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
};
|
|
96
|
+
|
|
78
97
|
const finishReasonOf = (reason: string | undefined): ProviderAttemptFinishReason => {
|
|
79
98
|
switch (reason?.toLowerCase()) {
|
|
80
99
|
case "stop":
|
|
@@ -119,7 +138,7 @@ const metadataOf = (values: readonly unknown[]): Record<string, unknown> => {
|
|
|
119
138
|
const record = recordOf(value);
|
|
120
139
|
if (record === null) continue;
|
|
121
140
|
for (const [key, item] of Object.entries(record)) {
|
|
122
|
-
if (key !== "choices" && key !== "usage") metadata[key] = item;
|
|
141
|
+
if (key !== "choices" && key !== "usage" && key !== "charge") metadata[key] = item;
|
|
123
142
|
}
|
|
124
143
|
}
|
|
125
144
|
return metadata;
|
|
@@ -134,8 +153,8 @@ export type AiSdkTransportRequest = {
|
|
|
134
153
|
signal?: AbortSignal;
|
|
135
154
|
fetch?: typeof globalThis.fetch;
|
|
136
155
|
fetchTimeoutMs: number;
|
|
156
|
+
firstContentTimeoutMs?: number;
|
|
137
157
|
streamIdleTimeoutMs?: number;
|
|
138
|
-
retryAttempts: number;
|
|
139
158
|
streaming: boolean;
|
|
140
159
|
captureRawBody: boolean;
|
|
141
160
|
};
|
|
@@ -147,7 +166,7 @@ export type AiSdkTransportResponse = {
|
|
|
147
166
|
reasoningProjected: boolean;
|
|
148
167
|
finishReason: ProviderAttemptFinishReason;
|
|
149
168
|
rawFinishReason?: string;
|
|
150
|
-
usage
|
|
169
|
+
usage?: ProviderUsage;
|
|
151
170
|
metadata: Record<string, unknown>;
|
|
152
171
|
reasoningEncrypted: Array<{
|
|
153
172
|
id: string | null;
|
|
@@ -162,6 +181,7 @@ export type AiSdkTransportResponse = {
|
|
|
162
181
|
export type AiSdkModelRequest = Omit<AiSdkTransportRequest, "url" | "model" | "body" | "fetch"> & {
|
|
163
182
|
languageModel: LanguageModel;
|
|
164
183
|
providerOptions?: Record<string, Record<string, JSONValue | undefined>>;
|
|
184
|
+
systemProviderOptions?: Record<string, Record<string, JSONValue | undefined>>;
|
|
165
185
|
temperature?: number;
|
|
166
186
|
topP?: number;
|
|
167
187
|
topK?: number;
|
|
@@ -173,42 +193,68 @@ export type AiSdkModelRequest = Omit<AiSdkTransportRequest, "url" | "model" | "b
|
|
|
173
193
|
reasoning?: "minimal" | "low" | "medium" | "high" | "xhigh" | "none" | "provider-default";
|
|
174
194
|
};
|
|
175
195
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
196
|
+
const transportTimeout = (
|
|
197
|
+
cause: unknown,
|
|
198
|
+
request: AiSdkModelRequest,
|
|
199
|
+
): ProviderTimeoutError | null => {
|
|
200
|
+
const owned = providerTimeoutOf(cause);
|
|
201
|
+
if (owned !== null) return owned;
|
|
202
|
+
|
|
203
|
+
const seen = new Set<unknown>();
|
|
204
|
+
let current = cause;
|
|
205
|
+
while (typeof current === "object" && current !== null && !seen.has(current)) {
|
|
206
|
+
if ((current as { name?: string }).name === "TimeoutError") break;
|
|
207
|
+
seen.add(current);
|
|
208
|
+
current = (current as { cause?: unknown }).cause;
|
|
209
|
+
}
|
|
210
|
+
if (typeof current !== "object" || current === null) return null;
|
|
211
|
+
|
|
212
|
+
const message = String((current as { message?: unknown }).message ?? "");
|
|
213
|
+
if (/first chunk timeout/i.test(message)) {
|
|
214
|
+
return new ProviderTimeoutError("first_content", request.firstContentTimeoutMs ?? 0, cause);
|
|
215
|
+
}
|
|
216
|
+
if (/chunk timeout/i.test(message)) {
|
|
217
|
+
return new ProviderTimeoutError("stream_idle", request.streamIdleTimeoutMs ?? 0, cause);
|
|
218
|
+
}
|
|
219
|
+
return new ProviderTimeoutError("attempt", request.fetchTimeoutMs, cause);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
const streamFailureValues = new WeakMap<object, readonly unknown[]>();
|
|
223
|
+
|
|
224
|
+
const applyRetryDirective = (error: unknown): unknown => {
|
|
225
|
+
if (!APICallError.isInstance(error)) return error;
|
|
226
|
+
const directed = retryDirective(error.statusCode, error.responseHeaders ?? {});
|
|
227
|
+
if (directed === null || directed === error.isRetryable) return error;
|
|
228
|
+
return new APICallError({
|
|
229
|
+
message: error.message,
|
|
230
|
+
url: error.url,
|
|
231
|
+
requestBodyValues: error.requestBodyValues,
|
|
232
|
+
statusCode: error.statusCode,
|
|
233
|
+
responseHeaders: error.responseHeaders,
|
|
234
|
+
responseBody: error.responseBody,
|
|
235
|
+
cause: error,
|
|
236
|
+
isRetryable: directed,
|
|
237
|
+
data: error.data,
|
|
238
|
+
});
|
|
239
|
+
};
|
|
181
240
|
|
|
182
241
|
const executeModel = async (
|
|
183
242
|
request: AiSdkModelRequest,
|
|
184
243
|
): Promise<AiSdkTransportResponse> => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
} catch (cause) {
|
|
201
|
-
if (operationSignal.aborted) throw operationSignal.reason;
|
|
202
|
-
if (!isStreamIdleTimeout(cause)) throw cause;
|
|
203
|
-
throw new APICallError({
|
|
204
|
-
message: cause.message,
|
|
205
|
-
url: "model:generation",
|
|
206
|
-
requestBodyValues: {},
|
|
207
|
-
cause,
|
|
208
|
-
isRetryable: true,
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
});
|
|
244
|
+
try {
|
|
245
|
+
return await executeModelOnce(request);
|
|
246
|
+
} catch (cause) {
|
|
247
|
+
if (request.signal?.aborted) throw request.signal.reason;
|
|
248
|
+
const timeout = transportTimeout(cause, request);
|
|
249
|
+
if (timeout === null) throw applyRetryDirective(cause);
|
|
250
|
+
throw new APICallError({
|
|
251
|
+
message: timeout.message,
|
|
252
|
+
url: "model:generation",
|
|
253
|
+
requestBodyValues: {},
|
|
254
|
+
cause: timeout,
|
|
255
|
+
isRetryable: true,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
212
258
|
};
|
|
213
259
|
|
|
214
260
|
const executeModelOnce = async (
|
|
@@ -217,6 +263,7 @@ const executeModelOnce = async (
|
|
|
217
263
|
const {
|
|
218
264
|
languageModel: model,
|
|
219
265
|
providerOptions,
|
|
266
|
+
systemProviderOptions,
|
|
220
267
|
temperature,
|
|
221
268
|
topP,
|
|
222
269
|
topK,
|
|
@@ -244,9 +291,12 @@ const executeModelOnce = async (
|
|
|
244
291
|
if (request.messages.slice(instructionCount).some((message) => message.role === "system")) {
|
|
245
292
|
throw new Error("provider messages: system instructions must precede conversational messages");
|
|
246
293
|
}
|
|
247
|
-
const instructions = request.messages.slice(0, instructionCount).map(({ content }) => ({
|
|
294
|
+
const instructions = request.messages.slice(0, instructionCount).map(({ content }, index) => ({
|
|
248
295
|
role: "system" as const,
|
|
249
296
|
content,
|
|
297
|
+
...(systemProviderOptions !== undefined && index === instructionCount - 1
|
|
298
|
+
? { providerOptions: systemProviderOptions }
|
|
299
|
+
: {}),
|
|
250
300
|
}));
|
|
251
301
|
const messages = request.messages.slice(instructionCount);
|
|
252
302
|
const common = {
|
|
@@ -255,12 +305,21 @@ const executeModelOnce = async (
|
|
|
255
305
|
messages: messages.length > 0
|
|
256
306
|
? messages
|
|
257
307
|
: [{ role: "user" as const, content: "" }],
|
|
258
|
-
|
|
308
|
+
// AiSdkProvider owns retries so every physical request is independently
|
|
309
|
+
// observed and accounted. The SDK transport executes exactly once.
|
|
310
|
+
maxRetries: 0,
|
|
259
311
|
abortSignal: request.signal,
|
|
260
312
|
headers: request.headers,
|
|
261
313
|
timeout: {
|
|
262
|
-
totalMs: request.fetchTimeoutMs,
|
|
263
|
-
...(request.
|
|
314
|
+
...(request.fetchTimeoutMs > 0 ? { totalMs: request.fetchTimeoutMs } : {}),
|
|
315
|
+
...(request.streaming
|
|
316
|
+
&& request.firstContentTimeoutMs !== undefined
|
|
317
|
+
&& request.firstContentTimeoutMs > 0
|
|
318
|
+
? { firstChunkMs: request.firstContentTimeoutMs }
|
|
319
|
+
: {}),
|
|
320
|
+
...(request.streaming
|
|
321
|
+
&& request.streamIdleTimeoutMs !== undefined
|
|
322
|
+
&& request.streamIdleTimeoutMs > 0
|
|
264
323
|
? { chunkMs: request.streamIdleTimeoutMs }
|
|
265
324
|
: {}),
|
|
266
325
|
},
|
|
@@ -285,12 +344,14 @@ const executeModelOnce = async (
|
|
|
285
344
|
reasoningProjected: evidence.reasoningProjected,
|
|
286
345
|
finishReason: finishReasonOf(rawFinishReason),
|
|
287
346
|
...(rawFinishReason === undefined ? {} : { rawFinishReason }),
|
|
288
|
-
usage: wireUsageOf(values
|
|
289
|
-
?? usageOf(result.usage, reasoningText, result.text),
|
|
347
|
+
usage: wireUsageOf(values) ?? usageOf(result.usage),
|
|
290
348
|
metadata: metadataOf(values),
|
|
291
349
|
reasoningEncrypted: evidence.reasoningEncrypted,
|
|
292
350
|
logprobs: evidence.logprobs,
|
|
293
351
|
chargeEvidence: {
|
|
352
|
+
...(wireChargeEvidenceOf(values) === undefined
|
|
353
|
+
? {}
|
|
354
|
+
: { charge: wireChargeEvidenceOf(values) }),
|
|
294
355
|
...(accountingUsage === undefined ? {} : { usage: accountingUsage }),
|
|
295
356
|
...(result.providerMetadata === undefined
|
|
296
357
|
? {}
|
|
@@ -317,7 +378,12 @@ const executeModelOnce = async (
|
|
|
317
378
|
if (part.type === "raw") rawChunks.push(part.rawValue);
|
|
318
379
|
if (part.type === "error") streamError ??= part.error;
|
|
319
380
|
}
|
|
320
|
-
if (streamError !== undefined)
|
|
381
|
+
if (streamError !== undefined) {
|
|
382
|
+
if (typeof streamError === "object" && streamError !== null) {
|
|
383
|
+
streamFailureValues.set(streamError, [...rawChunks, streamError]);
|
|
384
|
+
}
|
|
385
|
+
throw streamError;
|
|
386
|
+
}
|
|
321
387
|
const evidence = extractEvidence(rawChunks);
|
|
322
388
|
const accountingUsage = wireUsageEvidenceOf(rawChunks);
|
|
323
389
|
const content = await result.text;
|
|
@@ -334,12 +400,14 @@ const executeModelOnce = async (
|
|
|
334
400
|
reasoningProjected: evidence.reasoningProjected,
|
|
335
401
|
finishReason: finishReasonOf(rawFinishReason),
|
|
336
402
|
...(rawFinishReason === undefined ? {} : { rawFinishReason }),
|
|
337
|
-
usage: wireUsageOf(rawChunks
|
|
338
|
-
?? usageOf(await result.usage, reasoningText, content),
|
|
403
|
+
usage: wireUsageOf(rawChunks) ?? usageOf(await result.usage),
|
|
339
404
|
metadata: metadataOf(rawChunks),
|
|
340
405
|
reasoningEncrypted: evidence.reasoningEncrypted,
|
|
341
406
|
logprobs: evidence.logprobs,
|
|
342
407
|
chargeEvidence: {
|
|
408
|
+
...(wireChargeEvidenceOf(rawChunks) === undefined
|
|
409
|
+
? {}
|
|
410
|
+
: { charge: wireChargeEvidenceOf(rawChunks) }),
|
|
343
411
|
...(accountingUsage === undefined ? {} : { usage: accountingUsage }),
|
|
344
412
|
...(providerMetadata === undefined ? {} : { providerMetadata }),
|
|
345
413
|
response: {
|
|
@@ -378,13 +446,57 @@ export const executeOpenAICompatible = async (
|
|
|
378
446
|
messages: request.messages,
|
|
379
447
|
signal: request.signal,
|
|
380
448
|
fetchTimeoutMs: request.fetchTimeoutMs,
|
|
449
|
+
firstContentTimeoutMs: request.firstContentTimeoutMs,
|
|
381
450
|
streamIdleTimeoutMs: request.streamIdleTimeoutMs,
|
|
382
|
-
retryAttempts: request.retryAttempts,
|
|
383
451
|
streaming: request.streaming,
|
|
384
452
|
captureRawBody: request.captureRawBody,
|
|
385
453
|
});
|
|
386
454
|
};
|
|
387
455
|
|
|
456
|
+
const responseBodyValues = (error: APICallError): readonly unknown[] => {
|
|
457
|
+
if (error.responseBody === undefined || error.responseBody.length === 0) return [];
|
|
458
|
+
try {
|
|
459
|
+
return [JSON.parse(error.responseBody)];
|
|
460
|
+
} catch {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
export type AiSdkTransportFailureEvidence = {
|
|
466
|
+
readonly usage?: ProviderUsage;
|
|
467
|
+
readonly chargeEvidence: ProviderChargeEvidence;
|
|
468
|
+
readonly status?: number;
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
export const transportFailureEvidence = (
|
|
472
|
+
error: unknown,
|
|
473
|
+
): AiSdkTransportFailureEvidence => {
|
|
474
|
+
const values = typeof error === "object" && error !== null
|
|
475
|
+
? streamFailureValues.get(error) ?? (APICallError.isInstance(error) ? responseBodyValues(error) : [])
|
|
476
|
+
: [];
|
|
477
|
+
const usage = wireUsageOf(values);
|
|
478
|
+
const usageEvidence = wireUsageEvidenceOf(values);
|
|
479
|
+
const charge = wireChargeEvidenceOf(values);
|
|
480
|
+
const wireStatus = values
|
|
481
|
+
.map(recordOf)
|
|
482
|
+
.find((record) => Number.isInteger(record?.status))?.status;
|
|
483
|
+
const apiStatus = APICallError.isInstance(error) ? error.statusCode : undefined;
|
|
484
|
+
const status = Number.isInteger(apiStatus) && (apiStatus as number) >= 100 && (apiStatus as number) <= 599
|
|
485
|
+
? apiStatus as number
|
|
486
|
+
: Number.isInteger(wireStatus) && (wireStatus as number) >= 100 && (wireStatus as number) <= 599
|
|
487
|
+
? wireStatus as number
|
|
488
|
+
: undefined;
|
|
489
|
+
return {
|
|
490
|
+
...(usage === undefined ? {} : { usage }),
|
|
491
|
+
chargeEvidence: {
|
|
492
|
+
...(charge === undefined ? {} : { charge }),
|
|
493
|
+
...(usageEvidence === undefined ? {} : { usage: usageEvidence }),
|
|
494
|
+
response: {},
|
|
495
|
+
},
|
|
496
|
+
...(status === undefined ? {} : { status }),
|
|
497
|
+
};
|
|
498
|
+
};
|
|
499
|
+
|
|
388
500
|
const extractEvidence = (values: unknown[]): {
|
|
389
501
|
reasoningEncrypted: AiSdkTransportResponse["reasoningEncrypted"];
|
|
390
502
|
logprobs: TokenLogprob[];
|
package/src/boundaries.test.ts
CHANGED
|
@@ -28,6 +28,7 @@ test("provider source does not import the PLURNK parser", () => {
|
|
|
28
28
|
|
|
29
29
|
test("the OpenAI-compatible entrypoint excludes Node-owned provider machinery", () => {
|
|
30
30
|
const allowed = new Set([
|
|
31
|
+
"accounting.ts",
|
|
31
32
|
"AiSdkProvider.ts",
|
|
32
33
|
"aiSdkTransport.ts",
|
|
33
34
|
"cost.ts",
|