@providerkit/core 0.22.0 → 0.24.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 +29 -7
- package/dist/auth/flows.d.ts.map +1 -1
- package/dist/auth/flows.js +5 -5
- package/dist/auth/flows.js.map +1 -1
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/oauth.d.ts +12 -0
- package/dist/auth/oauth.d.ts.map +1 -1
- package/dist/auth/oauth.js +19 -1
- package/dist/auth/oauth.js.map +1 -1
- package/dist/auth/token-source.d.ts +9 -3
- package/dist/auth/token-source.d.ts.map +1 -1
- package/dist/auth/token-source.js +24 -9
- package/dist/auth/token-source.js.map +1 -1
- package/dist/auth/with-token.d.ts +32 -0
- package/dist/auth/with-token.d.ts.map +1 -0
- package/dist/auth/with-token.js +88 -0
- package/dist/auth/with-token.js.map +1 -0
- package/dist/context.d.ts +39 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +119 -0
- package/dist/context.js.map +1 -1
- package/dist/errors.d.ts +12 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +44 -5
- package/dist/errors.js.map +1 -1
- package/dist/fallback.d.ts +45 -1
- package/dist/fallback.d.ts.map +1 -1
- package/dist/fallback.js +138 -75
- package/dist/fallback.js.map +1 -1
- package/dist/gate.d.ts +29 -0
- package/dist/gate.d.ts.map +1 -0
- package/dist/gate.js +96 -0
- package/dist/gate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/key-pool.d.ts +21 -4
- package/dist/key-pool.d.ts.map +1 -1
- package/dist/key-pool.js +43 -12
- package/dist/key-pool.js.map +1 -1
- package/dist/presets.d.ts +7 -2
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +12 -2
- package/dist/presets.js.map +1 -1
- package/dist/providers/anthropic.d.ts +7 -0
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +83 -26
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/factory.d.ts +26 -0
- package/dist/providers/factory.d.ts.map +1 -1
- package/dist/providers/factory.js +41 -9
- package/dist/providers/factory.js.map +1 -1
- package/dist/providers/gemini.d.ts +6 -0
- package/dist/providers/gemini.d.ts.map +1 -1
- package/dist/providers/gemini.js +102 -27
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/openai.d.ts +7 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +66 -15
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/responses.d.ts +11 -0
- package/dist/providers/responses.d.ts.map +1 -1
- package/dist/providers/responses.js +101 -53
- package/dist/providers/responses.js.map +1 -1
- package/dist/rate-limit.d.ts +10 -0
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +7 -1
- package/dist/rate-limit.js.map +1 -1
- package/dist/registry.d.ts +105 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +291 -0
- package/dist/registry.js.map +1 -0
- package/dist/retry.d.ts +11 -0
- package/dist/retry.d.ts.map +1 -1
- package/dist/retry.js +25 -12
- package/dist/retry.js.map +1 -1
- package/dist/tool-args.d.ts +22 -0
- package/dist/tool-args.d.ts.map +1 -1
- package/dist/tool-args.js +28 -0
- package/dist/tool-args.js.map +1 -1
- package/dist/tools.d.ts +8 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +2 -2
- package/dist/tools.js.map +1 -1
- package/dist/transport.d.ts +34 -4
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +77 -16
- package/dist/transport.js.map +1 -1
- package/dist/turn.d.ts +55 -0
- package/dist/turn.d.ts.map +1 -0
- package/dist/turn.js +125 -0
- package/dist/turn.js.map +1 -0
- package/dist/types.d.ts +21 -22
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -53
- package/dist/types.js.map +1 -1
- package/dist/watchdog.d.ts +2 -0
- package/dist/watchdog.d.ts.map +1 -1
- package/dist/watchdog.js +25 -6
- package/dist/watchdog.js.map +1 -1
- package/package.json +1 -1
- package/src/auth/flows.ts +5 -4
- package/src/auth/index.ts +1 -0
- package/src/auth/oauth.ts +23 -1
- package/src/auth/token-source.ts +30 -11
- package/src/auth/with-token.ts +100 -0
- package/src/context.ts +137 -2
- package/src/errors.ts +46 -6
- package/src/fallback.ts +173 -75
- package/src/gate.ts +152 -0
- package/src/index.ts +3 -0
- package/src/key-pool.ts +45 -13
- package/src/presets.ts +12 -2
- package/src/providers/anthropic.ts +93 -25
- package/src/providers/factory.ts +66 -21
- package/src/providers/gemini.ts +102 -28
- package/src/providers/openai.ts +71 -14
- package/src/providers/responses.ts +116 -51
- package/src/rate-limit.ts +31 -3
- package/src/registry.ts +420 -0
- package/src/retry.ts +41 -10
- package/src/tool-args.ts +44 -0
- package/src/tools.ts +10 -2
- package/src/transport.ts +97 -17
- package/src/turn.ts +166 -0
- package/src/types.ts +21 -62
- package/src/watchdog.ts +25 -6
package/dist/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { RateLimitReading } from "./rate-limit.ts";
|
|
1
2
|
/**
|
|
2
3
|
* How hard the model thinks before answering. Absent = the provider's own
|
|
3
4
|
* default (never sent). Passed through verbatim on OpenAI-shape
|
|
@@ -50,8 +51,9 @@ export interface FilePart {
|
|
|
50
51
|
export type ContentPart = TextPart | ImagePart | FilePart;
|
|
51
52
|
/**
|
|
52
53
|
* A tool the model asked to run. `arguments` is the RAW JSON string, not a
|
|
53
|
-
* parsed object: it arrives in fragments and can be truncated mid-stream
|
|
54
|
-
*
|
|
54
|
+
* parsed object: it arrives in fragments and can be truncated mid-stream.
|
|
55
|
+
* `collectTurn` joins the fragments; healing and validating them is the
|
|
56
|
+
* consumer's job (see tools/define).
|
|
55
57
|
*/
|
|
56
58
|
export interface ToolCall {
|
|
57
59
|
id: string;
|
|
@@ -126,7 +128,7 @@ export interface ToolDefinition {
|
|
|
126
128
|
inputSchema: JsonObjectSchema;
|
|
127
129
|
}
|
|
128
130
|
export type FinishReason = "stop" | "length" | "tool_calls" | "content_filter";
|
|
129
|
-
/** Incremental tool-call fragment, assembled by `index`
|
|
131
|
+
/** Incremental tool-call fragment, assembled by `index`. `collectTurn` does it. */
|
|
130
132
|
export interface ToolCallDelta {
|
|
131
133
|
index: number;
|
|
132
134
|
id?: string;
|
|
@@ -274,9 +276,10 @@ export interface StreamOptions {
|
|
|
274
276
|
*/
|
|
275
277
|
jsonWithTools?: JsonWithTools;
|
|
276
278
|
/**
|
|
277
|
-
* Stable id for the conversation or job this call belongs to. Sent
|
|
278
|
-
*
|
|
279
|
-
*
|
|
279
|
+
* Stable id for the conversation or job this call belongs to. Sent in the
|
|
280
|
+
* `sessionHeader` of endpoints that name one (OpenCode Go routes and caches
|
|
281
|
+
* by it), and as `prompt_cache_key` on the Responses shape. A call without
|
|
282
|
+
* one sends the provider's `instanceSessionId` in that header instead.
|
|
280
283
|
*/
|
|
281
284
|
sessionId?: string;
|
|
282
285
|
/**
|
|
@@ -287,6 +290,18 @@ export interface StreamOptions {
|
|
|
287
290
|
* works, on the watchdog's progress clock alone.
|
|
288
291
|
*/
|
|
289
292
|
onActivity?: () => void;
|
|
293
|
+
/**
|
|
294
|
+
* Called once per response whose headers report how full a subscription
|
|
295
|
+
* window is, whatever its status: `utilization` (1 is full), `resetAtMs`,
|
|
296
|
+
* and `window` when the headers name it. A caller sees the window fill
|
|
297
|
+
* turn by turn, instead of learning about it from the 429 that ends a run.
|
|
298
|
+
*
|
|
299
|
+
* Every adapter forwards it. Two backends send the headers today: Claude
|
|
300
|
+
* subscriptions (Anthropic's unified 5h/7d headers) and the ChatGPT backend
|
|
301
|
+
* (`x-codex-*`). A response that reports no window never calls it, which
|
|
302
|
+
* covers API-key accounts on every vendor and all of Gemini.
|
|
303
|
+
*/
|
|
304
|
+
onRateLimit?: (reading: RateLimitReading) => void;
|
|
290
305
|
/**
|
|
291
306
|
* `false` asks for at most one tool call per turn, for tools that must run
|
|
292
307
|
* in order, or a backend lane that refuses parallel calls. Absent or `true`
|
|
@@ -309,22 +324,6 @@ export interface Provider {
|
|
|
309
324
|
readonly model: string;
|
|
310
325
|
createStream(messages: ChatMessage[], tools: ToolDefinition[], opts?: StreamOptions): AsyncIterable<ProviderChunk>;
|
|
311
326
|
}
|
|
312
|
-
export interface Completion {
|
|
313
|
-
text: string;
|
|
314
|
-
/** Present when the stream identifies the endpoint that answered. */
|
|
315
|
-
provider?: string;
|
|
316
|
-
reasoning: string;
|
|
317
|
-
/** Present only where the provider sent one. See ChatMessage.reasoningDetails. */
|
|
318
|
-
reasoningDetails?: unknown[];
|
|
319
|
-
/** Present only where the provider sent them. See ChatMessage.reasoningItems. */
|
|
320
|
-
reasoningItems?: ReasoningItems;
|
|
321
|
-
usage: TokenUsage;
|
|
322
|
-
finishReason: FinishReason | null;
|
|
323
|
-
model: string;
|
|
324
|
-
}
|
|
325
|
-
/** Drain a no-tools stream into a Completion. Usage chunks are cumulative on
|
|
326
|
-
* some providers and final-only on others: the LAST one wins. */
|
|
327
|
-
export declare function drainStream(stream: AsyncIterable<ProviderChunk>, model: string): Promise<Completion>;
|
|
328
327
|
/**
|
|
329
328
|
* Prepare a history for a thinking-DISABLED turn: strip `reasoning` from every
|
|
330
329
|
* assistant message.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
// The seam — provider-neutral messages, tools and chunks. Every adapter
|
|
2
|
-
// (OpenAI-compatible, Anthropic, Responses, Gemini) translates to and from
|
|
3
|
-
// exactly these shapes, so a caller never sees a vendor's dialect.
|
|
4
|
-
//
|
|
5
|
-
// Deliberately NOT OpenAI's parameter types. Using one vendor's wire shape as
|
|
6
|
-
// the lingua franca forces the other adapters to round-trip through a dialect
|
|
7
|
-
// that isn't theirs, and every quirk of that dialect then leaks into callers
|
|
8
|
-
// who never asked for it.
|
|
9
1
|
/**
|
|
10
2
|
* How hard the model thinks before answering. Absent = the provider's own
|
|
11
3
|
* default (never sent). Passed through verbatim on OpenAI-shape
|
|
@@ -136,51 +128,6 @@ function bracketed(text) {
|
|
|
136
128
|
}
|
|
137
129
|
return text.slice(start);
|
|
138
130
|
}
|
|
139
|
-
/** Drain a no-tools stream into a Completion. Usage chunks are cumulative on
|
|
140
|
-
* some providers and final-only on others: the LAST one wins. */
|
|
141
|
-
export async function drainStream(stream, model) {
|
|
142
|
-
let text = "";
|
|
143
|
-
let reasoning = "";
|
|
144
|
-
// Not concatenated: this half of the record is a payload the provider owns,
|
|
145
|
-
// and it arrives whole on one delta rather than in fragments. Dropped here,
|
|
146
|
-
// a drained turn replays only half its own reasoning on the next round —
|
|
147
|
-
// which is the failure `reasoningDetails` exists to prevent.
|
|
148
|
-
let reasoningDetails;
|
|
149
|
-
let reasoningItems;
|
|
150
|
-
let usage = EMPTY_USAGE;
|
|
151
|
-
let finishReason = null;
|
|
152
|
-
let source;
|
|
153
|
-
for await (const chunk of stream) {
|
|
154
|
-
if (chunk.source)
|
|
155
|
-
source = chunk.source;
|
|
156
|
-
if (chunk.type === "delta") {
|
|
157
|
-
if (chunk.content)
|
|
158
|
-
text += chunk.content;
|
|
159
|
-
if (chunk.reasoning)
|
|
160
|
-
reasoning += chunk.reasoning;
|
|
161
|
-
if (chunk.reasoningDetails?.length)
|
|
162
|
-
reasoningDetails = chunk.reasoningDetails;
|
|
163
|
-
if (chunk.reasoningItems?.items.length)
|
|
164
|
-
reasoningItems = chunk.reasoningItems;
|
|
165
|
-
}
|
|
166
|
-
else if (chunk.type === "usage" && chunk.usage) {
|
|
167
|
-
usage = chunk.usage;
|
|
168
|
-
}
|
|
169
|
-
else if (chunk.type === "finish" && chunk.finishReason) {
|
|
170
|
-
finishReason = chunk.finishReason;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
return {
|
|
174
|
-
text,
|
|
175
|
-
reasoning,
|
|
176
|
-
...(reasoningDetails ? { reasoningDetails } : {}),
|
|
177
|
-
...(reasoningItems ? { reasoningItems } : {}),
|
|
178
|
-
usage,
|
|
179
|
-
finishReason,
|
|
180
|
-
model: source?.model ?? model,
|
|
181
|
-
...(source ? { provider: source.provider } : {}),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
131
|
/**
|
|
185
132
|
* Prepare a history for a thinking-DISABLED turn: strip `reasoning` from every
|
|
186
133
|
* assistant message.
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;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;AAqKD,MAAM,CAAC,MAAM,WAAW,GAAe;IACrC,WAAW,EAAE,CAAC;IACd,iBAAiB,EAAE,CAAC;IACpB,YAAY,EAAE,CAAC;CAChB,CAAC;AAyCF;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;IACnE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC7B,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,WAAW,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,OAAO,SAAS,EAAE,CAAC;YACnB,yEAAyE;YACzE,sEAAsE;YACtE,0EAA0E;YAC1E,4EAA4E;YAC5E,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;QACnF,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,IAAY,EAAE,SAAiB;IAClD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;iFACiF;AACjF,MAAM,OAAO,eAAgB,SAAQ,KAAK;IAC/B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,KAAc;QACtC,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,KAAK,CAAC,mCAAmC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9D,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;CACF;AAED;;;;;;;;kEAQkE;AAClE,MAAM,YAAY,GAAG,iEAAiE,CAAC;AAEvF,SAAS,WAAW,CAAC,IAAY;IAC/B,KAAK,MAAM,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,GAAG,EAAE,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9E,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACpE,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;uEAMuE;AACvE,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACrB,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,IAAI,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;iBAClB,IAAI,IAAI,KAAK,GAAG;gBAAE,QAAQ,GAAG,KAAK,CAAC;QAC1C,CAAC;aAAM,IAAI,IAAI,KAAK,GAAG;YAAE,QAAQ,GAAG,IAAI,CAAC;aACpC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG;YAAE,KAAK,EAAE,CAAC;aAC1C,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACtC,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AA0GD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgC;IAC7D,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;YAAE,OAAO,OAAO,CAAC;QACjD,IACE,OAAO,CAAC,SAAS,KAAK,SAAS;YAC/B,OAAO,CAAC,gBAAgB,KAAK,SAAS;YACtC,OAAO,CAAC,cAAc,KAAK,SAAS,EACpC,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,wEAAwE;QACxE,uEAAuE;QACvE,0EAA0E;QAC1E,qBAAqB;QACrB,MAAM,EACJ,SAAS,EAAE,KAAK,EAChB,gBAAgB,EAAE,QAAQ,EAC1B,cAAc,EAAE,MAAM,EACtB,GAAG,IAAI,EACR,GAAG,OAAO,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,SAAS,CAAC,IAAe;IACvC,OAAO,QAAQ,IAAI,CAAC,QAAQ,WAAW,IAAI,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC"}
|
package/dist/watchdog.d.ts
CHANGED
|
@@ -99,6 +99,8 @@ export interface WatchdogOptions {
|
|
|
99
99
|
* copies. The result is still a `Provider`, so it composes unchanged with
|
|
100
100
|
* `withStreamRetry` and `streamWithBackupModels`. Timeouts and empty turns
|
|
101
101
|
* without a `length` finish are transient, so wrapping it in a retry is safe.
|
|
102
|
+
* The one timeout a replay would make worse, no response at all within the
|
|
103
|
+
* progress window, says so itself with `shouldRetry: false`.
|
|
102
104
|
*/
|
|
103
105
|
export declare function withWatchdog(provider: Provider, opts?: WatchdogOptions): Provider;
|
|
104
106
|
//# sourceMappingURL=watchdog.d.ts.map
|
package/dist/watchdog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../src/watchdog.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"watchdog.d.ts","sourceRoot":"","sources":["../src/watchdog.ts"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAEV,QAAQ,EACR,aAAa,EAId,MAAM,YAAY,CAAC;AAEpB;sBACsB;AACtB,eAAO,MAAM,cAAc,QAAS,CAAC;AAErC;2DAC2D;AAC3D,eAAO,MAAM,kBAAkB,SAAU,CAAC;AAE1C,MAAM,WAAW,WAAW;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,iFAAiF;IACjF,OAAO,IAAI,IAAI,CAAC;IAChB;mEAC+D;IAC/D,WAAW,IAAI,IAAI,CAAC;IACpB;;;;OAIG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;IAChC,8DAA8D;IAC9D,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,kBAAuB,GAAG,WAAW,CAiFtE;AAED;;;;GAIG;AACH,wBAAuB,WAAW,CAAC,CAAC,EAClC,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GACvB,cAAc,CAAC,CAAC,CAAC,CAWnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAuB,cAAc,CAAC,CAAC,SAAS,aAAa,EAC3D,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,IAAI,GAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,cAAc,CAAC,CAAC,CAAC,CAgDnB;AAED,MAAM,WAAW,eAAe;IAC9B;wCACoC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;gFAC4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,YAAY,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,GAAE,eAAoB,GAAG,QAAQ,CA8CrF"}
|
package/dist/watchdog.js
CHANGED
|
@@ -24,10 +24,14 @@
|
|
|
24
24
|
// scratch, which re-ran the reasoning into the same deadline.
|
|
25
25
|
//
|
|
26
26
|
// When either fires, the watchdog aborts ITS OWN controller and the caller's
|
|
27
|
-
// signal is only bridged in
|
|
28
|
-
// distinguishable from our timeout. One is their cancel and is never retried
|
|
29
|
-
//
|
|
30
|
-
//
|
|
27
|
+
// signal is only bridged in, which is what keeps a person's Stop
|
|
28
|
+
// distinguishable from our timeout. One is their cancel and is never retried.
|
|
29
|
+
// The other is ours and transient, with one exception: a progress timeout
|
|
30
|
+
// before the response started carries `shouldRetry: false`. That wait was a
|
|
31
|
+
// backend thinking with its headers held back, and a replay sends the same
|
|
32
|
+
// prompt into the same 300s and pays for the reasoning again, up to three
|
|
33
|
+
// times (cc-proxy never replays slow headers). Its kind stays `timeout`, so a
|
|
34
|
+
// fallback pool still moves on to its next candidate.
|
|
31
35
|
import { ProviderError } from "./errors.js";
|
|
32
36
|
/** No byte at all for this long, once the response has started, and the
|
|
33
37
|
* stream is dead. */
|
|
@@ -49,6 +53,9 @@ export function streamWatch(opts = {}) {
|
|
|
49
53
|
let firstChunk = null;
|
|
50
54
|
let fired = null;
|
|
51
55
|
let disposed = false;
|
|
56
|
+
// The first sign of life (the headers, a keep-alive, a chunk). The idle
|
|
57
|
+
// clock waits for it, and a timeout that fires before it is not replayed.
|
|
58
|
+
let responded = false;
|
|
52
59
|
// The bridge is structural rather than an event listener: AbortSignal.any
|
|
53
60
|
// aborts synchronously when an input is ALREADY aborted, which is the race
|
|
54
61
|
// no listener can catch (the event fired before we subscribed). It is also
|
|
@@ -57,7 +64,13 @@ export function streamWatch(opts = {}) {
|
|
|
57
64
|
const signal = callerSignal ? AbortSignal.any([callerSignal, timeout.signal]) : timeout.signal;
|
|
58
65
|
function deadline(ms, what) {
|
|
59
66
|
const timer = setTimeout(() => {
|
|
60
|
-
|
|
67
|
+
// Only the progress clock runs before the response starts, so this is
|
|
68
|
+
// the backend that thinks with its headers held back (see the header).
|
|
69
|
+
fired = responded
|
|
70
|
+
? new ProviderError(provider, "timeout", `stream went ${ms / 1000}s without ${what}`)
|
|
71
|
+
: new ProviderError(provider, "timeout", `no response in ${ms / 1000}s`, {
|
|
72
|
+
shouldRetry: false,
|
|
73
|
+
});
|
|
61
74
|
timeout.abort(fired);
|
|
62
75
|
}, ms);
|
|
63
76
|
// An orphaned watch — its consumer gone, dispose never called — must not
|
|
@@ -71,6 +84,7 @@ export function streamWatch(opts = {}) {
|
|
|
71
84
|
let progress = deadline(progressMs, "a chunk");
|
|
72
85
|
const live = () => !disposed && !signal.aborted;
|
|
73
86
|
function sawActivity() {
|
|
87
|
+
responded = true;
|
|
74
88
|
clearTimeout(idle);
|
|
75
89
|
if (live())
|
|
76
90
|
idle = deadline(idleMs, "a byte");
|
|
@@ -89,7 +103,10 @@ export function streamWatch(opts = {}) {
|
|
|
89
103
|
classify(err) {
|
|
90
104
|
// Our deadline, not theirs — and not the caller's Stop.
|
|
91
105
|
if (fired && !(callerSignal?.aborted ?? false)) {
|
|
92
|
-
return new ProviderError(fired.provider, "timeout", fired.message, {
|
|
106
|
+
return new ProviderError(fired.provider, "timeout", fired.message, {
|
|
107
|
+
shouldRetry: fired.shouldRetry,
|
|
108
|
+
cause: err,
|
|
109
|
+
});
|
|
93
110
|
}
|
|
94
111
|
return err;
|
|
95
112
|
},
|
|
@@ -205,6 +222,8 @@ export async function* requireContent(provider, chunks, opts = {}) {
|
|
|
205
222
|
* copies. The result is still a `Provider`, so it composes unchanged with
|
|
206
223
|
* `withStreamRetry` and `streamWithBackupModels`. Timeouts and empty turns
|
|
207
224
|
* without a `length` finish are transient, so wrapping it in a retry is safe.
|
|
225
|
+
* The one timeout a replay would make worse, no response at all within the
|
|
226
|
+
* progress window, says so itself with `shouldRetry: false`.
|
|
208
227
|
*/
|
|
209
228
|
export function withWatchdog(provider, opts = {}) {
|
|
210
229
|
return {
|
package/dist/watchdog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchdog.js","sourceRoot":"","sources":["../src/watchdog.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,8BAA8B;AAC9B,EAAE;AACF,uBAAuB;AACvB,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,wEAAwE;AACxE,EAAE;AACF,6EAA6E;AAC7E,EAAE;AACF,uEAAuE;AACvE,2EAA2E;AAC3E,wEAAwE;AACxE,gFAAgF;AAChF,8EAA8E;AAC9E,6EAA6E;AAC7E,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,2EAA2E;AAC3E,gEAAgE;AAChE,EAAE;AACF,6EAA6E;AAC7E,
|
|
1
|
+
{"version":3,"file":"watchdog.js","sourceRoot":"","sources":["../src/watchdog.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/auth/flows.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
accountFromToken,
|
|
9
9
|
asRecord,
|
|
10
10
|
authError,
|
|
11
|
+
authFetch,
|
|
11
12
|
generatePKCE,
|
|
12
13
|
jwtClaims,
|
|
13
14
|
num,
|
|
@@ -308,7 +309,7 @@ export function copilotHeaders(appName: string, turn: "user" | "agent"): Record<
|
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
async function mintCopilotToken(host: AuthHost, githubToken: string): Promise<Credential> {
|
|
311
|
-
const res = await (host
|
|
312
|
+
const res = await authFetch(host, COPILOT_TOKEN_URL, {
|
|
312
313
|
headers: {
|
|
313
314
|
Accept: "application/json",
|
|
314
315
|
Authorization: `Bearer ${githubToken}`,
|
|
@@ -361,7 +362,7 @@ async function refreshCopilot(host: AuthHost, credential: Credential): Promise<C
|
|
|
361
362
|
* lookup did, so any failure means no name. */
|
|
362
363
|
async function githubLogin(host: AuthHost, githubToken: string): Promise<string | undefined> {
|
|
363
364
|
try {
|
|
364
|
-
const res = await (host
|
|
365
|
+
const res = await authFetch(host, GITHUB_USER_URL, {
|
|
365
366
|
headers: { Accept: "application/json", Authorization: `Bearer ${githubToken}` },
|
|
366
367
|
});
|
|
367
368
|
return res.ok ? str(asRecord(await res.json()).login) : undefined;
|
|
@@ -405,7 +406,7 @@ const META_KEY_URL = "https://api.meta.ai/muse-code/key";
|
|
|
405
406
|
const META_KEY_LIFETIME_MS = 24 * 60 * 60 * 1000;
|
|
406
407
|
|
|
407
408
|
async function mintMetaKey(host: AuthHost, identityToken: string): Promise<Credential> {
|
|
408
|
-
const res = await (host
|
|
409
|
+
const res = await authFetch(host, META_KEY_URL, {
|
|
409
410
|
method: "POST",
|
|
410
411
|
headers: {
|
|
411
412
|
Accept: "application/json",
|
|
@@ -494,7 +495,7 @@ function openrouterFlow(host: AuthHost): AuthFlow {
|
|
|
494
495
|
redirectUri: OPENROUTER.redirectUri,
|
|
495
496
|
signal,
|
|
496
497
|
});
|
|
497
|
-
const res = await (host
|
|
498
|
+
const res = await authFetch(host, OPENROUTER.keysUrl, {
|
|
498
499
|
method: "POST",
|
|
499
500
|
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
500
501
|
body: JSON.stringify({ code, code_verifier: verifier, code_challenge_method: "S256" }),
|
package/src/auth/index.ts
CHANGED
package/src/auth/oauth.ts
CHANGED
|
@@ -25,6 +25,8 @@ export interface AuthHost {
|
|
|
25
25
|
}): Promise<string>;
|
|
26
26
|
/** Replaces `fetch`, for tests. */
|
|
27
27
|
fetchImpl?: typeof fetch;
|
|
28
|
+
/** How long one request to a sign-in server may take, in ms. Default 30 s. */
|
|
29
|
+
timeoutMs?: number;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/** The stored result of a sign-in. */
|
|
@@ -76,6 +78,26 @@ export function authError(
|
|
|
76
78
|
/** Refresh this long before the server's own expiry. */
|
|
77
79
|
export const REFRESH_SKEW_MS = 5 * 60 * 1000;
|
|
78
80
|
|
|
81
|
+
/** The deadline cc-proxy puts on its own token requests. */
|
|
82
|
+
const AUTH_TIMEOUT_MS = 30_000;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* `fetch` with a deadline, for every request in `auth/`.
|
|
86
|
+
*
|
|
87
|
+
* A sign-in server that accepts the connection and never answers would hold
|
|
88
|
+
* the one shared refresh forever, and every call waiting on it with it. No
|
|
89
|
+
* clock above this one cuts it: the watchdog only starts once the model request
|
|
90
|
+
* is sent. The timeout rejects with a `TimeoutError`, which is not a refusal,
|
|
91
|
+
* so `tokenSource` keeps the stored credential.
|
|
92
|
+
*/
|
|
93
|
+
export function authFetch(host: AuthHost, url: string, init: RequestInit = {}): Promise<Response> {
|
|
94
|
+
const deadline = AbortSignal.timeout(host.timeoutMs ?? AUTH_TIMEOUT_MS);
|
|
95
|
+
return (host.fetchImpl ?? fetch)(url, {
|
|
96
|
+
...init,
|
|
97
|
+
signal: init.signal ? AbortSignal.any([init.signal, deadline]) : deadline,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
79
101
|
export const str = (value: unknown): string | undefined =>
|
|
80
102
|
typeof value === "string" && value.length > 0 ? value : undefined;
|
|
81
103
|
|
|
@@ -119,7 +141,7 @@ export async function postToken(
|
|
|
119
141
|
opts: { encode: "json" | "form"; allowErrorBody?: boolean },
|
|
120
142
|
): Promise<Record<string, unknown>> {
|
|
121
143
|
const json = opts.encode === "json";
|
|
122
|
-
const res = await (host
|
|
144
|
+
const res = await authFetch(host, url, {
|
|
123
145
|
method: "POST",
|
|
124
146
|
headers: {
|
|
125
147
|
"Content-Type": json ? "application/json" : "application/x-www-form-urlencoded",
|
package/src/auth/token-source.ts
CHANGED
|
@@ -16,12 +16,16 @@ export interface TokenSourceOptions<C extends Credential = Credential> {
|
|
|
16
16
|
|
|
17
17
|
export interface TokenSource<C extends Credential = Credential> {
|
|
18
18
|
/** A credential whose `accessToken` is good now. Throws a `ProviderError`
|
|
19
|
-
* coded `refresh_dead` when the user must sign in again.
|
|
20
|
-
|
|
19
|
+
* coded `refresh_dead` when the user must sign in again.
|
|
20
|
+
*
|
|
21
|
+
* Pass `rejected`, the access token a server just answered 401 to, to renew
|
|
22
|
+
* it even though it has not expired. A server can refuse a token early: it
|
|
23
|
+
* was revoked, or another client rotated it. */
|
|
24
|
+
getToken(opts?: { rejected?: string }): Promise<C>;
|
|
21
25
|
}
|
|
22
26
|
|
|
23
27
|
/**
|
|
24
|
-
* Refresh when the stored credential has expired, and only then.
|
|
28
|
+
* Refresh when the stored credential has expired or was rejected, and only then.
|
|
25
29
|
*
|
|
26
30
|
* - Concurrent calls share one refresh and the result is saved before use.
|
|
27
31
|
* - When the server rejects the refresh, the stored credential is reloaded. If
|
|
@@ -36,7 +40,20 @@ export interface TokenSource<C extends Credential = Credential> {
|
|
|
36
40
|
export function tokenSource<C extends Credential>(options: TokenSourceOptions<C>): TokenSource<C> {
|
|
37
41
|
let inFlight: Promise<C> | undefined;
|
|
38
42
|
|
|
39
|
-
async function
|
|
43
|
+
async function loadSignedIn(): Promise<C> {
|
|
44
|
+
const credential = await options.load();
|
|
45
|
+
if (!credential) throw authError("refresh_dead", "Not signed in. Sign in first.");
|
|
46
|
+
return credential;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function refreshStored(rejected: string | undefined): Promise<C> {
|
|
50
|
+
// Read again inside the single flight. A caller whose read raced a refresh
|
|
51
|
+
// that had just saved would otherwise start a second one with the refresh
|
|
52
|
+
// token the first already spent. Five calls refused on one token is the
|
|
53
|
+
// normal case for that race, and a server that rotates refresh tokens may
|
|
54
|
+
// take a reused one as stolen and end the whole sign-in.
|
|
55
|
+
const stale = await loadSignedIn();
|
|
56
|
+
if (isUsable(stale, rejected)) return stale;
|
|
40
57
|
try {
|
|
41
58
|
const fresh = await options.refresh(stale);
|
|
42
59
|
await options.save(fresh);
|
|
@@ -50,13 +67,10 @@ export function tokenSource<C extends Credential>(options: TokenSourceOptions<C>
|
|
|
50
67
|
}
|
|
51
68
|
|
|
52
69
|
return {
|
|
53
|
-
async getToken() {
|
|
54
|
-
const credential = await
|
|
55
|
-
if (
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
if (Date.now() < credential.expiresAt) return credential;
|
|
59
|
-
inFlight ??= refreshStored(credential).finally(() => {
|
|
70
|
+
async getToken({ rejected } = {}) {
|
|
71
|
+
const credential = await loadSignedIn();
|
|
72
|
+
if (isUsable(credential, rejected)) return credential;
|
|
73
|
+
inFlight ??= refreshStored(rejected).finally(() => {
|
|
60
74
|
inFlight = undefined;
|
|
61
75
|
});
|
|
62
76
|
return inFlight;
|
|
@@ -64,6 +78,11 @@ export function tokenSource<C extends Credential>(options: TokenSourceOptions<C>
|
|
|
64
78
|
};
|
|
65
79
|
}
|
|
66
80
|
|
|
81
|
+
/** Unexpired, and not the token a server just refused. */
|
|
82
|
+
function isUsable(credential: Credential, rejected: string | undefined): boolean {
|
|
83
|
+
return Date.now() < credential.expiresAt && credential.accessToken !== rejected;
|
|
84
|
+
}
|
|
85
|
+
|
|
67
86
|
/** A refresh the server turned down for good, as opposed to one that never
|
|
68
87
|
* reached it or came back unreadable. */
|
|
69
88
|
function isRejected(error: unknown): error is ProviderError {
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// A provider that signs every call with the stored sign-in, and renews the token
|
|
2
|
+
// once when a server refuses it before it expired. The sibling of
|
|
3
|
+
// `withKeyPool`: that one rotates between keys, this one renews the one token.
|
|
4
|
+
//
|
|
5
|
+
// Type-only imports from the sign-in side, so core's root can export this
|
|
6
|
+
// without pulling the flows in.
|
|
7
|
+
import { ProviderError } from "../errors.ts";
|
|
8
|
+
import type { PresetProviderConfig } from "../providers/factory.ts";
|
|
9
|
+
import type { Provider } from "../types.ts";
|
|
10
|
+
import type { Credential } from "./oauth.ts";
|
|
11
|
+
import type { TokenSource } from "./token-source.ts";
|
|
12
|
+
|
|
13
|
+
/** The credential half of `PresetProviderConfig`, so `build` can spread it in. */
|
|
14
|
+
export type TokenAuth = Pick<PresetProviderConfig, "apiKey" | "baseUrl" | "headers">;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Give any provider the signed-in user's token, read fresh on every call.
|
|
18
|
+
*
|
|
19
|
+
* ```ts
|
|
20
|
+
* const provider = withToken(token, (auth) => createPresetProvider("github-copilot", auth));
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* `build` runs once per attempt and must do no I/O, like `withKeyPool`'s
|
|
24
|
+
* factory. It gets the access token as `apiKey`, the credential's own host as
|
|
25
|
+
* `baseUrl` (Copilot gives each plan its own) and its `ChatGPT-Account-Id`
|
|
26
|
+
* header, which the Codex backend refuses the request without. To add headers
|
|
27
|
+
* of your own, merge them with `auth.headers` rather than replacing them. On
|
|
28
|
+
* a preset that sends a session id (`chatgpt`), pass `instanceSessionId` too:
|
|
29
|
+
* a provider built per call mints a new id per call, and the backend's prompt
|
|
30
|
+
* cache misses on every one.
|
|
31
|
+
*
|
|
32
|
+
* A 401 before any output renews the token and replays the call once: a token
|
|
33
|
+
* can be refused before its expiry, and without this the caller sees an auth
|
|
34
|
+
* failure that a fallback pool benches for an hour. A second 401 is a real one
|
|
35
|
+
* and surfaces. A 401 after output never replays (invariant 1).
|
|
36
|
+
*
|
|
37
|
+
* Only 401 counts. A 400 or 403 on a model call is a bad request or a plan
|
|
38
|
+
* that lacks the model, and a new token fixes neither.
|
|
39
|
+
*/
|
|
40
|
+
export function withToken<C extends Credential>(
|
|
41
|
+
source: TokenSource<C>,
|
|
42
|
+
build: (auth: TokenAuth) => Provider,
|
|
43
|
+
): Provider {
|
|
44
|
+
// Building does no I/O, so a throwaway instance reads `id` and `model`
|
|
45
|
+
// without a token.
|
|
46
|
+
const identity = build({ apiKey: "" });
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
id: identity.id,
|
|
50
|
+
model: identity.model,
|
|
51
|
+
|
|
52
|
+
async *createStream(messages, tools, opts = {}) {
|
|
53
|
+
const credential = await unlessAborted(source.getToken(), opts.signal);
|
|
54
|
+
let emitted = false;
|
|
55
|
+
try {
|
|
56
|
+
for await (const chunk of build(authOf(credential)).createStream(messages, tools, opts)) {
|
|
57
|
+
emitted = true;
|
|
58
|
+
yield chunk;
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (emitted || !(error instanceof ProviderError) || error.status !== 401) throw error;
|
|
63
|
+
}
|
|
64
|
+
const renewed = await unlessAborted(
|
|
65
|
+
source.getToken({ rejected: credential.accessToken }),
|
|
66
|
+
opts.signal,
|
|
67
|
+
);
|
|
68
|
+
yield* build(authOf(renewed)).createStream(messages, tools, opts);
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function authOf(credential: Credential): TokenAuth {
|
|
74
|
+
return {
|
|
75
|
+
apiKey: credential.accessToken,
|
|
76
|
+
...(credential.baseUrl ? { baseUrl: credential.baseUrl } : {}),
|
|
77
|
+
...(credential.chatgptAccountId
|
|
78
|
+
? { headers: { "ChatGPT-Account-Id": credential.chatgptAccountId } }
|
|
79
|
+
: {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Wait for the token, or reject with the caller's abort, whichever is first.
|
|
85
|
+
*
|
|
86
|
+
* The refresh itself is never aborted. It is shared by every caller, and once
|
|
87
|
+
* its POST is out the server may already have rotated the refresh token, so
|
|
88
|
+
* abandoning it before the save loses the only copy of the new one. And
|
|
89
|
+
* without the race an unanswered refresh would outlive the watchdog, which
|
|
90
|
+
* aborts the signal but never races a read.
|
|
91
|
+
*/
|
|
92
|
+
function unlessAborted<T>(pending: Promise<T>, signal: AbortSignal | undefined): Promise<T> {
|
|
93
|
+
if (!signal) return pending;
|
|
94
|
+
return new Promise<T>((resolve, reject) => {
|
|
95
|
+
const onAbort = () => reject(signal.reason);
|
|
96
|
+
if (signal.aborted) onAbort();
|
|
97
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
98
|
+
pending.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
99
|
+
});
|
|
100
|
+
}
|