@providerkit/core 0.17.2 → 0.21.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/README.md +12 -4
- package/dist/auth/device.d.ts +34 -0
- package/dist/auth/device.d.ts.map +1 -0
- package/dist/auth/device.js +85 -0
- package/dist/auth/device.js.map +1 -0
- package/dist/auth/flows.d.ts +36 -0
- package/dist/auth/flows.d.ts.map +1 -0
- package/dist/auth/flows.js +392 -0
- package/dist/auth/flows.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth.d.ts +92 -0
- package/dist/auth/oauth.d.ts.map +1 -0
- package/dist/auth/oauth.js +148 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/token-source.d.ts +30 -0
- package/dist/auth/token-source.d.ts.map +1 -0
- package/dist/auth/token-source.js +61 -0
- package/dist/auth/token-source.js.map +1 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +17 -1
- package/dist/context.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -0
- package/dist/errors.js.map +1 -1
- package/dist/presets.d.ts +78 -23
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +61 -22
- package/dist/presets.js.map +1 -1
- package/dist/providers/anthropic.d.ts +3 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +10 -1
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/factory.d.ts +8 -2
- package/dist/providers/factory.d.ts.map +1 -1
- package/dist/providers/factory.js +46 -9
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +3 -0
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/openai.d.ts +8 -2
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +41 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/responses.d.ts +7 -1
- package/dist/providers/responses.d.ts.map +1 -1
- package/dist/providers/responses.js +48 -6
- package/dist/providers/responses.js.map +1 -1
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +13 -5
- package/dist/types.js.map +1 -1
- package/package.json +5 -1
- package/src/auth/device.ts +137 -0
- package/src/auth/flows.ts +526 -0
- package/src/auth/index.ts +7 -0
- package/src/auth/oauth.ts +226 -0
- package/src/auth/token-source.ts +76 -0
- package/src/context.ts +16 -1
- package/src/errors.ts +3 -0
- package/src/presets.ts +90 -24
- package/src/providers/anthropic.ts +17 -1
- package/src/providers/factory.ts +76 -11
- package/src/providers/gemini.ts +7 -0
- package/src/providers/openai.ts +50 -2
- package/src/providers/responses.ts +60 -6
- package/src/types.ts +49 -5
package/dist/types.js
CHANGED
|
@@ -146,6 +146,7 @@ export async function drainStream(stream, model) {
|
|
|
146
146
|
// a drained turn replays only half its own reasoning on the next round —
|
|
147
147
|
// which is the failure `reasoningDetails` exists to prevent.
|
|
148
148
|
let reasoningDetails;
|
|
149
|
+
let reasoningItems;
|
|
149
150
|
let usage = EMPTY_USAGE;
|
|
150
151
|
let finishReason = null;
|
|
151
152
|
let source;
|
|
@@ -159,6 +160,8 @@ export async function drainStream(stream, model) {
|
|
|
159
160
|
reasoning += chunk.reasoning;
|
|
160
161
|
if (chunk.reasoningDetails?.length)
|
|
161
162
|
reasoningDetails = chunk.reasoningDetails;
|
|
163
|
+
if (chunk.reasoningItems?.items.length)
|
|
164
|
+
reasoningItems = chunk.reasoningItems;
|
|
162
165
|
}
|
|
163
166
|
else if (chunk.type === "usage" && chunk.usage) {
|
|
164
167
|
usage = chunk.usage;
|
|
@@ -171,6 +174,7 @@ export async function drainStream(stream, model) {
|
|
|
171
174
|
text,
|
|
172
175
|
reasoning,
|
|
173
176
|
...(reasoningDetails ? { reasoningDetails } : {}),
|
|
177
|
+
...(reasoningItems ? { reasoningItems } : {}),
|
|
174
178
|
usage,
|
|
175
179
|
finishReason,
|
|
176
180
|
model: source?.model ?? model,
|
|
@@ -193,12 +197,16 @@ export function stripReasoning(messages) {
|
|
|
193
197
|
return messages.map((message) => {
|
|
194
198
|
if (message.role !== "assistant")
|
|
195
199
|
return message;
|
|
196
|
-
if (message.reasoning === undefined &&
|
|
200
|
+
if (message.reasoning === undefined &&
|
|
201
|
+
message.reasoningDetails === undefined &&
|
|
202
|
+
message.reasoningItems === undefined) {
|
|
197
203
|
return message;
|
|
198
|
-
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
|
|
204
|
+
}
|
|
205
|
+
// Every half goes. `reasoningDetails` and `reasoningItems` are the same
|
|
206
|
+
// chain of thought in the provider's own words, so leaving them behind
|
|
207
|
+
// carries into a thinking-off turn exactly what stripping `reasoning` was
|
|
208
|
+
// meant to keep out.
|
|
209
|
+
const { reasoning: _text, reasoningDetails: _payload, reasoningItems: _items, ...rest } = message;
|
|
202
210
|
return rest;
|
|
203
211
|
});
|
|
204
212
|
}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,mEAAmE;AACnE,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,6EAA6E;AAC7E,0BAA0B;AAE1B;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAU,CAAC;AAGzE,2EAA2E;AAC3E,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC;AACpD,CAAC;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@providerkit/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "The layer under your agent loop: one seam for every LLM provider, plus the failure handling you only learn in production.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./jev": {
|
|
18
18
|
"types": "./dist/jev.d.ts",
|
|
19
19
|
"default": "./dist/jev.js"
|
|
20
|
+
},
|
|
21
|
+
"./auth": {
|
|
22
|
+
"types": "./dist/auth/index.d.ts",
|
|
23
|
+
"default": "./dist/auth/index.js"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"types": "./dist/index.d.ts",
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// RFC 8628 device authorization: the sign-in that shows a code instead of
|
|
2
|
+
// bouncing through a redirect. Kimi, GitHub Copilot, xAI and Meta all use it.
|
|
3
|
+
import { SignInError, authError, num, postToken, str, webUrl, type AuthHost } from "./oauth.ts";
|
|
4
|
+
|
|
5
|
+
/** RFC 8628: a `slow_down` answer adds this to the poll interval. */
|
|
6
|
+
const SLOW_DOWN_STEP_MS = 5000;
|
|
7
|
+
/** The poll never goes faster than this, whatever the vendor says. */
|
|
8
|
+
const MIN_INTERVAL_MS = 1000;
|
|
9
|
+
/** A vendor that leaves out `expires_in` gets this. */
|
|
10
|
+
const DEFAULT_EXPIRES_SEC = 900;
|
|
11
|
+
|
|
12
|
+
/** Where a vendor's device flow lives, and what it wants on the wire. */
|
|
13
|
+
export interface DeviceEndpoint {
|
|
14
|
+
/** POST here to get a user code. */
|
|
15
|
+
deviceUrl: string;
|
|
16
|
+
/** POST here, again and again, until the user approves. */
|
|
17
|
+
tokenUrl: string;
|
|
18
|
+
clientId: string;
|
|
19
|
+
/** Sent with the code request, for vendors that want scopes. */
|
|
20
|
+
scope?: string;
|
|
21
|
+
/** Extra fields on the code request only (xAI wants a `referrer`). */
|
|
22
|
+
extra?: Record<string, string>;
|
|
23
|
+
encode: "form" | "json";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** What the user must do, and how long we keep asking. */
|
|
27
|
+
export interface DevicePrompt {
|
|
28
|
+
/** Show it as is: it must match what the vendor's page displays. */
|
|
29
|
+
userCode: string;
|
|
30
|
+
/** The approval page, with the code filled in when the vendor offers that. */
|
|
31
|
+
verificationUrl: string;
|
|
32
|
+
deviceCode: string;
|
|
33
|
+
intervalMs: number;
|
|
34
|
+
expiresAt: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Step 1: ask the vendor for a code the user can approve on the web. */
|
|
38
|
+
export async function requestDeviceCode(
|
|
39
|
+
host: AuthHost,
|
|
40
|
+
endpoint: DeviceEndpoint,
|
|
41
|
+
): Promise<DevicePrompt> {
|
|
42
|
+
const body = await postToken(
|
|
43
|
+
host,
|
|
44
|
+
endpoint.deviceUrl,
|
|
45
|
+
{
|
|
46
|
+
client_id: endpoint.clientId,
|
|
47
|
+
...(endpoint.scope ? { scope: endpoint.scope } : {}),
|
|
48
|
+
...endpoint.extra,
|
|
49
|
+
},
|
|
50
|
+
{ encode: endpoint.encode },
|
|
51
|
+
);
|
|
52
|
+
const userCode = str(body.user_code);
|
|
53
|
+
const deviceCode = str(body.device_code);
|
|
54
|
+
// The complete URL carries the code, so there is nothing to type.
|
|
55
|
+
const verificationUrl =
|
|
56
|
+
webUrl(str(body.verification_uri_complete)) ?? webUrl(str(body.verification_uri));
|
|
57
|
+
if (!userCode || !deviceCode || !verificationUrl) {
|
|
58
|
+
throw authError(
|
|
59
|
+
"device_response_invalid",
|
|
60
|
+
"The sign-in server's reply has no code, device code or http(s) approval page.",
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
userCode,
|
|
65
|
+
verificationUrl,
|
|
66
|
+
deviceCode,
|
|
67
|
+
intervalMs: Math.max(MIN_INTERVAL_MS, (num(body.interval) ?? 5) * 1000),
|
|
68
|
+
expiresAt: Date.now() + (num(body.expires_in) ?? DEFAULT_EXPIRES_SEC) * 1000,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Step 2: poll until the user approves, and return the raw token body. Turning
|
|
74
|
+
* it into a credential is the caller's job, because the bodies differ: GitHub
|
|
75
|
+
* answers with a bare token and no expiry, Meta with an identity token that
|
|
76
|
+
* still has to be traded for a key.
|
|
77
|
+
*/
|
|
78
|
+
export async function pollDeviceToken(
|
|
79
|
+
host: AuthHost,
|
|
80
|
+
endpoint: DeviceEndpoint,
|
|
81
|
+
prompt: DevicePrompt,
|
|
82
|
+
signal: AbortSignal,
|
|
83
|
+
): Promise<Record<string, unknown>> {
|
|
84
|
+
let waitMs = prompt.intervalMs;
|
|
85
|
+
while (Date.now() < prompt.expiresAt) {
|
|
86
|
+
await sleep(waitMs, signal);
|
|
87
|
+
// Pending is the normal answer for most of the loop, and GitHub sends it
|
|
88
|
+
// as a 200 with an `error` field, so the body decides, never the status.
|
|
89
|
+
const body = await postToken(
|
|
90
|
+
host,
|
|
91
|
+
endpoint.tokenUrl,
|
|
92
|
+
{
|
|
93
|
+
client_id: endpoint.clientId,
|
|
94
|
+
device_code: prompt.deviceCode,
|
|
95
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
96
|
+
},
|
|
97
|
+
{ encode: endpoint.encode, allowErrorBody: true },
|
|
98
|
+
);
|
|
99
|
+
if (str(body.access_token)) return body;
|
|
100
|
+
|
|
101
|
+
switch (str(body.error)) {
|
|
102
|
+
case "authorization_pending":
|
|
103
|
+
break;
|
|
104
|
+
case "slow_down":
|
|
105
|
+
// Honour the longer of our step and an interval the server names.
|
|
106
|
+
waitMs = Math.max(waitMs + SLOW_DOWN_STEP_MS, (num(body.interval) ?? 0) * 1000);
|
|
107
|
+
break;
|
|
108
|
+
case "access_denied":
|
|
109
|
+
case "authorization_denied":
|
|
110
|
+
throw new SignInError("denied");
|
|
111
|
+
case "expired_token":
|
|
112
|
+
throw new SignInError("expired");
|
|
113
|
+
default:
|
|
114
|
+
throw authError(
|
|
115
|
+
"token_refused",
|
|
116
|
+
`The sign-in server refused the request: ${str(body.error_description) ?? str(body.error) ?? "no reason given"}`,
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
throw new SignInError("expired");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** A delay that a cancelled sign-in cuts short. */
|
|
124
|
+
function sleep(ms: number, signal: AbortSignal): Promise<void> {
|
|
125
|
+
return new Promise((resolve, reject) => {
|
|
126
|
+
if (signal.aborted) return reject(new SignInError("cancelled"));
|
|
127
|
+
const onAbort = () => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
reject(new SignInError("cancelled"));
|
|
130
|
+
};
|
|
131
|
+
const timer = setTimeout(() => {
|
|
132
|
+
signal.removeEventListener("abort", onAbort);
|
|
133
|
+
resolve();
|
|
134
|
+
}, ms);
|
|
135
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
136
|
+
});
|
|
137
|
+
}
|