@duetso/agent 0.3.11 → 0.3.13
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/dist/package.json +6 -6
- package/dist/src/cli/connect.d.ts +7 -0
- package/dist/src/cli/connect.d.ts.map +1 -1
- package/dist/src/cli/connect.js +46 -27
- package/dist/src/cli/connect.js.map +1 -1
- package/dist/src/cli/train.d.ts.map +1 -1
- package/dist/src/cli/train.js +14 -2
- package/dist/src/cli/train.js.map +1 -1
- package/dist/src/connected-providers/capability-probe.d.ts.map +1 -1
- package/dist/src/connected-providers/capability-probe.js +13 -8
- package/dist/src/connected-providers/capability-probe.js.map +1 -1
- package/dist/src/connected-providers/fake-issuer.d.ts +16 -1
- package/dist/src/connected-providers/fake-issuer.d.ts.map +1 -1
- package/dist/src/connected-providers/fake-issuer.js +89 -42
- package/dist/src/connected-providers/fake-issuer.js.map +1 -1
- package/dist/src/connected-providers/refresh.d.ts.map +1 -1
- package/dist/src/connected-providers/refresh.js +9 -2
- package/dist/src/connected-providers/refresh.js.map +1 -1
- package/dist/src/connected-providers/registry.d.ts +8 -2
- package/dist/src/connected-providers/registry.d.ts.map +1 -1
- package/dist/src/connected-providers/registry.js +18 -3
- package/dist/src/connected-providers/registry.js.map +1 -1
- package/dist/src/connected-providers/tokens.d.ts.map +1 -1
- package/dist/src/connected-providers/tokens.js +10 -6
- package/dist/src/connected-providers/tokens.js.map +1 -1
- package/dist/src/core/structured-output.d.ts.map +1 -1
- package/dist/src/core/structured-output.js +2 -1
- package/dist/src/core/structured-output.js.map +1 -1
- package/dist/src/memory/observational.d.ts +0 -1
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +44 -24
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/model-resolution/catalog.d.ts.map +1 -1
- package/dist/src/model-resolution/catalog.js +6 -9
- package/dist/src/model-resolution/catalog.js.map +1 -1
- package/dist/src/model-resolution/duet-gateway.d.ts +52 -45
- package/dist/src/model-resolution/duet-gateway.d.ts.map +1 -1
- package/dist/src/model-resolution/duet-gateway.js +192 -264
- package/dist/src/model-resolution/duet-gateway.js.map +1 -1
- package/dist/src/model-resolution/models.d.ts +24 -0
- package/dist/src/model-resolution/models.d.ts.map +1 -0
- package/dist/src/model-resolution/models.js +63 -0
- package/dist/src/model-resolution/models.js.map +1 -0
- package/dist/src/model-resolution/resolver.d.ts +1 -1
- package/dist/src/model-resolution/resolver.d.ts.map +1 -1
- package/dist/src/model-resolution/resolver.js +18 -17
- package/dist/src/model-resolution/resolver.js.map +1 -1
- package/dist/src/model-routing/advisor.js +9 -1
- package/dist/src/model-routing/advisor.js.map +1 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +4 -0
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/package.json +6 -6
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createProvider, envApiKeyAuth, } from "@earendil-works/pi-ai";
|
|
2
|
+
import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
|
|
3
|
+
import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
|
|
4
|
+
import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
|
|
5
|
+
import { getEnvApiKey } from "@earendil-works/pi-ai/compat";
|
|
6
|
+
import { builtinProviders, getBuiltinModels } from "@earendil-works/pi-ai/providers/all";
|
|
2
7
|
import { isConnectedProviderId } from "../connected-providers/store.js";
|
|
3
8
|
import { activeDuetTier } from "../model-routing/active-tier.js";
|
|
4
9
|
import { connectedProviderApiKey, refreshConnectedTokenInBackground, } from "../connected-providers/tokens.js";
|
|
@@ -19,70 +24,15 @@ export function duetTierHeaders() {
|
|
|
19
24
|
}
|
|
20
25
|
const DEFAULT_DUET_GATEWAY_BASE_URL = "https://gateway.duet.so";
|
|
21
26
|
const OPENAI_MODEL_PREFIX = "openai/";
|
|
27
|
+
const ANTHROPIC_MODEL_PREFIX = "anthropic/";
|
|
28
|
+
const DUET_GATEWAY_PROVIDER_ID = "duet-gateway";
|
|
22
29
|
const VERCEL_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh";
|
|
23
|
-
const
|
|
24
|
-
// Published USD-per-million-token prices (input / output / cacheRead / cacheWrite),
|
|
25
|
-
// verified against the OpenRouter listing 2026-07-18. Clones inherit their
|
|
26
|
-
// sibling's cost and synthesized passthroughs zero it out, so every model that
|
|
27
|
-
// resolves through an override below must carry its real price here or cost
|
|
28
|
-
// accounting (sidebar, usage events, advisor-preview) silently lies.
|
|
29
|
-
const GPT_5_6_GATEWAY_CAPABILITIES = {
|
|
30
|
-
input: ["text", "image"],
|
|
31
|
-
contextWindow: 1_050_000,
|
|
32
|
-
maxTokens: 128_000,
|
|
33
|
-
};
|
|
34
|
-
const OPENAI_GATEWAY_MODEL_OVERRIDES = {
|
|
35
|
-
"openai/gpt-5.6-sol": {
|
|
36
|
-
...GPT_5_6_GATEWAY_CAPABILITIES,
|
|
37
|
-
cost: { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
38
|
-
},
|
|
39
|
-
"openai/gpt-5.6-terra": {
|
|
40
|
-
...GPT_5_6_GATEWAY_CAPABILITIES,
|
|
41
|
-
cost: { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 3.125 },
|
|
42
|
-
},
|
|
43
|
-
"openai/gpt-5.6-luna": {
|
|
44
|
-
...GPT_5_6_GATEWAY_CAPABILITIES,
|
|
45
|
-
cost: { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1.25 },
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
// Vercel exposes GLM 5.2's maximum reasoning mode, but the pinned pi-ai
|
|
49
|
-
// catalog does not yet describe it. Keep the product-level `xhigh` setting
|
|
50
|
-
// wire-faithful until that metadata ships upstream.
|
|
51
|
-
const VERCEL_GATEWAY_MODEL_OVERRIDES = {
|
|
52
|
-
"zai/glm-5.2": {
|
|
53
|
-
thinkingLevelMap: { xhigh: "max" },
|
|
54
|
-
compat: { forceAdaptiveThinking: true },
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
const KIMI_K3_CAPABILITIES = {
|
|
58
|
-
input: ["text", "image"],
|
|
59
|
-
contextWindow: 1_000_000,
|
|
60
|
-
maxTokens: 131_072,
|
|
61
|
-
cost: { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 },
|
|
62
|
-
// K3 currently exposes one reasoning setting. Mapping app-level `high` to
|
|
63
|
-
// `max` keeps the selection honest on both supported transports.
|
|
64
|
-
thinkingLevelMap: { off: null, minimal: null, low: null, medium: null, high: "max", xhigh: null },
|
|
65
|
-
};
|
|
66
|
-
const FABLE_5_COST = {
|
|
67
|
-
cost: { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
|
|
68
|
-
};
|
|
69
|
-
// Sonnet 5 introductory pricing (through 2026-08-31); bump to 3/15/0.3/3.75 after.
|
|
70
|
-
const SONNET_5_COST = {
|
|
71
|
-
cost: { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
|
|
72
|
-
};
|
|
30
|
+
const VERCEL_GATEWAY_PROVIDER_ID = "vercel-ai-gateway";
|
|
73
31
|
/**
|
|
74
32
|
* The Duet gateway proxies Vercel's AI Gateway path layout and authenticates
|
|
75
|
-
* with a `DUET_API_KEY` token scoped to a single org.
|
|
76
|
-
*
|
|
77
|
-
* model definitions and only swaps `baseUrl` to point at Duet.
|
|
78
|
-
*
|
|
79
|
-
* `DUET_GATEWAY_BASE_URL` can point model traffic at a dedicated gateway
|
|
80
|
-
* origin. When it is unset, model traffic goes directly to
|
|
33
|
+
* with a `DUET_API_KEY` token scoped to a single org. `DUET_GATEWAY_BASE_URL`
|
|
34
|
+
* points model traffic at a different origin; unset, it goes to
|
|
81
35
|
* `https://gateway.duet.so`.
|
|
82
|
-
*
|
|
83
|
-
* Auth flows through pi-ai's existing vercel-ai-gateway path, which reads
|
|
84
|
-
* `AI_GATEWAY_API_KEY` — the CLI shims that env var from `DUET_API_KEY` at
|
|
85
|
-
* startup so users only need to set the duet token.
|
|
86
36
|
*/
|
|
87
37
|
export const DUET_GATEWAY_API_KEY_ENV = "DUET_API_KEY";
|
|
88
38
|
export const DUET_GATEWAY_BASE_URL_ENV = "DUET_GATEWAY_BASE_URL";
|
|
@@ -92,246 +42,224 @@ export function getDuetGatewayBaseUrl() {
|
|
|
92
42
|
return stripTrailingSlash(override);
|
|
93
43
|
return DEFAULT_DUET_GATEWAY_BASE_URL;
|
|
94
44
|
}
|
|
45
|
+
const DUET_GATEWAY = {
|
|
46
|
+
id: "duet-gateway",
|
|
47
|
+
name: "Duet Gateway",
|
|
48
|
+
origin: getDuetGatewayBaseUrl,
|
|
49
|
+
auth: () => ({ apiKey: envApiKeyAuth("Duet", [DUET_GATEWAY_API_KEY_ENV]) }),
|
|
50
|
+
// Every duet-gateway model resolves through this route — parent step,
|
|
51
|
+
// classifier, vision fallback, subagents and memory alike — so the routing
|
|
52
|
+
// tier is claimed here rather than at each call site.
|
|
53
|
+
headers: duetTierHeaders,
|
|
54
|
+
};
|
|
55
|
+
const VERCEL_GATEWAY = {
|
|
56
|
+
id: VERCEL_GATEWAY_PROVIDER_ID,
|
|
57
|
+
name: "Vercel AI Gateway",
|
|
58
|
+
origin: () => VERCEL_GATEWAY_BASE_URL,
|
|
59
|
+
// The transports and the model list are ours to decide; how Vercel
|
|
60
|
+
// authenticates stays the catalog's to declare.
|
|
61
|
+
auth: () => builtinGatewayAuth(),
|
|
62
|
+
// The tier claim means something only to the Duet proxy, which bills
|
|
63
|
+
// against it; Vercel would reject an unknown header's cost to no purpose.
|
|
64
|
+
headers: () => undefined,
|
|
65
|
+
};
|
|
66
|
+
export const GATEWAY_ROUTES = [DUET_GATEWAY, VERCEL_GATEWAY];
|
|
67
|
+
/** The route serving `providerId`, or undefined when it is not a gateway. */
|
|
68
|
+
export function gatewayRoute(providerId) {
|
|
69
|
+
return GATEWAY_ROUTES.find((route) => route.id === providerId);
|
|
70
|
+
}
|
|
71
|
+
function builtinGatewayAuth() {
|
|
72
|
+
const builtin = builtinProviders().find(({ id }) => id === VERCEL_GATEWAY_PROVIDER_ID);
|
|
73
|
+
if (!builtin)
|
|
74
|
+
throw new Error(`Model catalog has no ${VERCEL_GATEWAY_PROVIDER_ID} provider`);
|
|
75
|
+
return builtin.auth;
|
|
76
|
+
}
|
|
95
77
|
/**
|
|
96
|
-
*
|
|
78
|
+
* The catalog rebased onto one gateway route, built once per route.
|
|
97
79
|
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* semantics, so the TUI never sees reasoning/thinking events.
|
|
80
|
+
* The catalog is the only source of a model's capabilities; nothing here
|
|
81
|
+
* declares a model. That is what lets a newly served gateway model work with
|
|
82
|
+
* no code change, and it is why a capability we send on the wire must either
|
|
83
|
+
* come from the catalog or be named in `gatewayCapabilityGaps` below.
|
|
103
84
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
85
|
+
* `resolveGatewayModel` runs on every model resolution — parent step,
|
|
86
|
+
* classifier, each subagent — so rebasing two hundred catalog entries per call
|
|
87
|
+
* is pure waste. Route id and origin are the rebase's only inputs, so together
|
|
88
|
+
* they are the key; tests move the origin between cases, which is why this is
|
|
89
|
+
* not a plain once-flag.
|
|
109
90
|
*/
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
id: modelId,
|
|
121
|
-
baseUrl: getDuetGatewayBaseUrlForModel(upstream),
|
|
122
|
-
...(tierHeaders && { headers: { ...upstream.headers, ...tierHeaders } }),
|
|
123
|
-
};
|
|
91
|
+
const gatewayModelsByRoute = new Map();
|
|
92
|
+
function gatewayModels(route) {
|
|
93
|
+
const origin = route.origin();
|
|
94
|
+
const key = `${route.id}@${origin}`;
|
|
95
|
+
const cached = gatewayModelsByRoute.get(key);
|
|
96
|
+
if (cached)
|
|
97
|
+
return cached;
|
|
98
|
+
const models = getBuiltinModels(VERCEL_GATEWAY_PROVIDER_ID).map((model) => rebaseOntoGateway(model, route, origin));
|
|
99
|
+
gatewayModelsByRoute.set(key, models);
|
|
100
|
+
return models;
|
|
124
101
|
}
|
|
125
102
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* `
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
103
|
+
* The transport a gateway model runs over.
|
|
104
|
+
*
|
|
105
|
+
* Both gateways serve the whole catalog over the same protocol surface — pi-ai
|
|
106
|
+
* declares every `vercel-ai-gateway` model as `anthropic-messages` — so the
|
|
107
|
+
* choice is per model, not per gateway, and `duet-gateway` and
|
|
108
|
+
* `vercel-ai-gateway` make it identically.
|
|
109
|
+
*
|
|
110
|
+
* Anthropic's own models keep the Anthropic transport, which is native for
|
|
111
|
+
* them. Everything else that accepts images moves to OpenAI completions,
|
|
112
|
+
* because the two transports disagree about where a tool result's image goes:
|
|
113
|
+
* the Anthropic serializer leaves it inside the `tool_result` block, and
|
|
114
|
+
* Vercel's translation to an OpenAI-shaped provider has nowhere to put it, so
|
|
115
|
+
* it arrives as text. Measured against `moonshotai/kimi-k3`, one 512x512 PNG
|
|
116
|
+
* costs 147,554 tokens that way and 650 when the OpenAI serializer hoists it
|
|
117
|
+
* into the following user message — which it already does, unprompted.
|
|
118
|
+
*
|
|
119
|
+
* OpenAI's own models stay on the Responses transport: routing them through
|
|
120
|
+
* either compatible path drops reasoning stream semantics and silently ignores
|
|
121
|
+
* `reasoningEffort`.
|
|
134
122
|
*/
|
|
135
|
-
function
|
|
136
|
-
if (
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
synthesizePassthroughModel(modelId, "anthropic-messages");
|
|
142
|
-
return applyVercelGatewayModelOverrides(modelId, upstream);
|
|
123
|
+
function gatewayApi(model) {
|
|
124
|
+
if (model.id.startsWith(OPENAI_MODEL_PREFIX))
|
|
125
|
+
return "openai-responses";
|
|
126
|
+
if (model.id.startsWith(ANTHROPIC_MODEL_PREFIX))
|
|
127
|
+
return model.api;
|
|
128
|
+
return model.input.includes("image") ? "openai-completions" : model.api;
|
|
143
129
|
}
|
|
144
|
-
/**
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Point a catalog model at a gateway: its own transport, that gateway's origin,
|
|
132
|
+
* and the capabilities the catalog is missing.
|
|
133
|
+
*
|
|
134
|
+
* `provider` is deliberately rewritten for the Duet gateway so cost and usage
|
|
135
|
+
* telemetry attribute the call to the Duet proxy rather than to Vercel.
|
|
136
|
+
*/
|
|
137
|
+
function rebaseOntoGateway(model, route, origin) {
|
|
138
|
+
const api = gatewayApi(model);
|
|
139
|
+
const gaps = gatewayCapabilityGaps(model.id);
|
|
149
140
|
return {
|
|
150
141
|
...model,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
},
|
|
156
|
-
|
|
142
|
+
// A gap fills in what the catalog omits; it must not take the two nested
|
|
143
|
+
// records with it, or the day the catalog ships one field here silently
|
|
144
|
+
// drops the others.
|
|
145
|
+
thinkingLevelMap: { ...model.thinkingLevelMap, ...gaps?.thinkingLevelMap },
|
|
146
|
+
compat: { ...model.compat, ...gaps?.compat },
|
|
147
|
+
api,
|
|
148
|
+
// Rewritten so cost and usage telemetry attribute the call to the route
|
|
149
|
+
// that served it rather than to the catalog it was declared in.
|
|
150
|
+
provider: route.id,
|
|
151
|
+
baseUrl: gatewayBaseUrlForApi(origin, api),
|
|
157
152
|
};
|
|
158
153
|
}
|
|
159
154
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
155
|
+
* Capabilities both gateways serve that pi-ai's catalog does not yet describe.
|
|
156
|
+
*
|
|
157
|
+
* An entry here is a bug report against the catalog, not a place to declare a
|
|
158
|
+
* model: it exists only while a capability we send on the wire has no catalog
|
|
159
|
+
* field behind it, and it is deleted the moment one ships. Anything beyond
|
|
160
|
+
* that — a price, a context window, a transport — belongs to the catalog, and
|
|
161
|
+
* a second copy of it here would silently go stale.
|
|
166
162
|
*/
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
baseUrl: isOpenAI ? OPENAI_BASE_URL : VERCEL_GATEWAY_BASE_URL,
|
|
175
|
-
reasoning: true,
|
|
176
|
-
input: isOpenAI ? ["text", "image"] : ["text"],
|
|
177
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
178
|
-
contextWindow: 256_000,
|
|
179
|
-
maxTokens: 64_000,
|
|
180
|
-
};
|
|
163
|
+
function gatewayCapabilityGaps(modelId) {
|
|
164
|
+
// GLM 5.2's maximum reasoning mode is reached with `"max"`; without the map
|
|
165
|
+
// the product's `xhigh` setting reaches the wire as a level GLM ignores.
|
|
166
|
+
if (modelId === "zai/glm-5.2") {
|
|
167
|
+
return { thinkingLevelMap: { xhigh: "max" }, compat: { forceAdaptiveThinking: true } };
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
181
170
|
}
|
|
182
171
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
172
|
+
* A gateway as a first-class pi-ai provider.
|
|
173
|
+
*
|
|
174
|
+
* `auth` is why this is a provider rather than a bag of rewritten models: a
|
|
175
|
+
* credential is resolved per provider, and `duet-gateway` is not an id pi-ai's
|
|
176
|
+
* env-key map knows. Declaring it on the provider is what lets any call
|
|
177
|
+
* resolve the token without being handed one.
|
|
178
|
+
*
|
|
179
|
+
* All three transports are declared because serialization belongs to the
|
|
180
|
+
* provider: rewriting a model's `api` to one the provider does not implement
|
|
181
|
+
* yields a model that claims Responses and is sent as Anthropic, silently
|
|
182
|
+
* dropping reasoning effort and mangling tool-result images.
|
|
188
183
|
*/
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
to: "gpt-5.6-terra",
|
|
201
|
-
overrides: OPENAI_GATEWAY_MODEL_OVERRIDES["openai/gpt-5.6-terra"],
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
// Drop once pi-ai ships openai-codex:gpt-5.6-luna.
|
|
205
|
-
from: "gpt-5.5",
|
|
206
|
-
to: "gpt-5.6-luna",
|
|
207
|
-
overrides: OPENAI_GATEWAY_MODEL_OVERRIDES["openai/gpt-5.6-luna"],
|
|
208
|
-
},
|
|
209
|
-
],
|
|
210
|
-
"vercel-ai-gateway": [
|
|
211
|
-
// Opus 5 keeps Opus 4.8's published contract and pricing, so no overrides.
|
|
212
|
-
{ from: "anthropic/claude-opus-4.8", to: "anthropic/claude-opus-5" },
|
|
213
|
-
{ from: "anthropic/claude-opus-4.8", to: "anthropic/claude-fable-5", overrides: FABLE_5_COST },
|
|
214
|
-
{
|
|
215
|
-
from: "anthropic/claude-opus-4.8",
|
|
216
|
-
to: "anthropic/claude-sonnet-5",
|
|
217
|
-
overrides: SONNET_5_COST,
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
from: "moonshotai/kimi-k2.6",
|
|
221
|
-
to: "moonshotai/kimi-k3",
|
|
222
|
-
overrides: {
|
|
223
|
-
...KIMI_K3_CAPABILITIES,
|
|
224
|
-
compat: { forceAdaptiveThinking: true },
|
|
225
|
-
},
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
openrouter: [
|
|
229
|
-
// Without this, `openrouter:anthropic/claude-opus-5` resolves to undefined
|
|
230
|
-
// and OPENROUTER-only users cannot run the default model at all.
|
|
231
|
-
{ from: "anthropic/claude-opus-4.8", to: "anthropic/claude-opus-5" },
|
|
232
|
-
{
|
|
233
|
-
// OpenRouter serves gpt-5.6-luna; pi-ai's catalog just hasn't shipped
|
|
234
|
-
// the route (David, 2026-07-24). Drop once it does.
|
|
235
|
-
from: "openai/gpt-5.5",
|
|
236
|
-
to: "openai/gpt-5.6-luna",
|
|
237
|
-
overrides: OPENAI_GATEWAY_MODEL_OVERRIDES["openai/gpt-5.6-luna"],
|
|
184
|
+
export function gatewayProvider(route) {
|
|
185
|
+
return createProvider({
|
|
186
|
+
id: route.id,
|
|
187
|
+
name: route.name,
|
|
188
|
+
baseUrl: route.origin(),
|
|
189
|
+
auth: route.auth(),
|
|
190
|
+
models: gatewayModels(route),
|
|
191
|
+
api: {
|
|
192
|
+
"anthropic-messages": anthropicMessagesApi(),
|
|
193
|
+
"openai-completions": openAICompletionsApi(),
|
|
194
|
+
"openai-responses": openAIResponsesApi(),
|
|
238
195
|
},
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
to: "moonshotai/kimi-k3",
|
|
242
|
-
overrides: KIMI_K3_CAPABILITIES,
|
|
243
|
-
},
|
|
244
|
-
{
|
|
245
|
-
from: "openai/gpt-5.4",
|
|
246
|
-
to: "openai/gpt-5.6-sol",
|
|
247
|
-
overrides: OPENAI_GATEWAY_MODEL_OVERRIDES["openai/gpt-5.6-sol"],
|
|
248
|
-
},
|
|
249
|
-
{
|
|
250
|
-
from: "openai/gpt-5.4",
|
|
251
|
-
to: "openai/gpt-5.6-terra",
|
|
252
|
-
overrides: OPENAI_GATEWAY_MODEL_OVERRIDES["openai/gpt-5.6-terra"],
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
};
|
|
196
|
+
});
|
|
197
|
+
}
|
|
256
198
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
199
|
+
* Resolve a `<gateway>:<modelId>` string to a Model.
|
|
200
|
+
*
|
|
201
|
+
* An id the catalog has not shipped still resolves: a gateway serves a model
|
|
202
|
+
* the moment its vendor lists it, and waiting for a catalog release to use it
|
|
203
|
+
* is the coupling this module exists to avoid. The synthesized entry is
|
|
204
|
+
* deliberately conservative so an unknown model never 400s on an
|
|
205
|
+
* over-advertised window.
|
|
262
206
|
*/
|
|
263
|
-
export function
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
return undefined;
|
|
273
|
-
const compat = clone.overrides?.compat
|
|
274
|
-
? { ...sibling.compat, ...clone.overrides.compat }
|
|
275
|
-
: sibling.compat;
|
|
276
|
-
return { ...sibling, ...clone.overrides, compat, id: modelId, name: modelId };
|
|
207
|
+
export function resolveGatewayModel(route, modelId) {
|
|
208
|
+
const declared = gatewayModels(route).find((model) => model.id === modelId);
|
|
209
|
+
const model = declared ?? rebaseOntoGateway(synthesizePassthroughModel(modelId), route, route.origin());
|
|
210
|
+
// Applied here rather than in the rebase: the rebased list is memoized per
|
|
211
|
+
// route, but the routing tier changes within a session — a mid-turn `/model`
|
|
212
|
+
// switch retargets attribution — so a header baked in at build time would
|
|
213
|
+
// report whichever tier happened to be active when the list was first built.
|
|
214
|
+
const headers = route.headers();
|
|
215
|
+
return headers ? { ...model, headers: { ...model.headers, ...headers } } : model;
|
|
277
216
|
}
|
|
278
217
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
* over the anthropic-messages transport, which drops OpenAI reasoning stream
|
|
283
|
-
* semantics AND silently ignores `reasoningEffort` — so a memory model resolved
|
|
284
|
-
* here (e.g. the default gpt-5.6-luna) could never run at the low effort the
|
|
285
|
-
* observer/reflectors request. This mirrors the deliberate openai-responses
|
|
286
|
-
* routing documented on `resolveDuetGatewayModel`. Auth is unaffected:
|
|
287
|
-
* `resolveProviderApiKey`/pi-ai key resolution keys off `provider`, which stays
|
|
288
|
-
* `vercel-ai-gateway`, so `AI_GATEWAY_API_KEY` still applies.
|
|
218
|
+
* Build a minimal spec for a gateway model the catalog has not shipped yet.
|
|
219
|
+
* OpenAI ids keep the Responses transport and its native vision support; every
|
|
220
|
+
* other unknown id is assumed text-only until the catalog says otherwise.
|
|
289
221
|
*/
|
|
290
|
-
function
|
|
291
|
-
const upstream = resolveOpenAIResponsesModel(modelId);
|
|
222
|
+
function synthesizePassthroughModel(modelId) {
|
|
292
223
|
return {
|
|
293
|
-
...upstream,
|
|
294
|
-
provider: "vercel-ai-gateway",
|
|
295
224
|
id: modelId,
|
|
296
225
|
name: modelId,
|
|
297
|
-
baseUrl
|
|
226
|
+
// `provider`, `baseUrl` and `api` are required fields the rebase then
|
|
227
|
+
// decides for real; only `input` survives, and it is what tells the rebase
|
|
228
|
+
// which transport can carry this model.
|
|
229
|
+
provider: VERCEL_GATEWAY_PROVIDER_ID,
|
|
230
|
+
baseUrl: VERCEL_GATEWAY_BASE_URL,
|
|
231
|
+
api: "anthropic-messages",
|
|
232
|
+
reasoning: true,
|
|
233
|
+
input: modelId.startsWith(OPENAI_MODEL_PREFIX) ? ["text", "image"] : ["text"],
|
|
234
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
235
|
+
contextWindow: 256_000,
|
|
236
|
+
maxTokens: 64_000,
|
|
298
237
|
};
|
|
299
238
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
return { ...upstream, ...OPENAI_GATEWAY_MODEL_OVERRIDES[modelId] };
|
|
305
|
-
}
|
|
306
|
-
function getDuetGatewayBaseUrlForModel(model) {
|
|
307
|
-
if (model.api === "openai-completions" || model.api === "openai-responses") {
|
|
308
|
-
return `${getDuetGatewayBaseUrl()}/v1`;
|
|
309
|
-
}
|
|
310
|
-
return getDuetGatewayBaseUrl();
|
|
239
|
+
/** OpenAI-compatible transports live under the gateway's `/v1` route. */
|
|
240
|
+
function gatewayBaseUrlForApi(origin, api) {
|
|
241
|
+
const openAiCompatible = api === "openai-completions" || api === "openai-responses";
|
|
242
|
+
return openAiCompatible ? `${origin}/v1` : origin;
|
|
311
243
|
}
|
|
312
244
|
function stripTrailingSlash(url) {
|
|
313
245
|
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
314
246
|
}
|
|
315
247
|
/**
|
|
316
|
-
*
|
|
317
|
-
* `duet-gateway` provider that pi-ai's built-in env-key map does not
|
|
318
|
-
* know about.
|
|
248
|
+
* The API key to send for a provider, resolved the way Duet stores keys.
|
|
319
249
|
*
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* any direct `complete()` call would silently send an empty API key
|
|
326
|
-
* and fail with `Could not resolve authentication method` even when
|
|
327
|
-
* `DUET_API_KEY` is set.
|
|
250
|
+
* Two kinds of caller need this. Connected providers (ChatGPT, Copilot) keep
|
|
251
|
+
* their token in Duet's own store, which pi-ai cannot see at all. And the
|
|
252
|
+
* api-dispatch `complete()` used for structured output does no provider auth
|
|
253
|
+
* resolution whatsoever, so it needs the key handed to it — including for
|
|
254
|
+
* `duet-gateway`, an id pi-ai's env-key map does not know.
|
|
328
255
|
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
256
|
+
* A gateway rebase deliberately rewrites `model.provider` to
|
|
257
|
+
* `"duet-gateway"` so cost and usage telemetry attribute the call to the Duet
|
|
258
|
+
* proxy rather than to Vercel; that rename is what puts the id outside pi-ai's
|
|
259
|
+
* map in the first place.
|
|
332
260
|
*/
|
|
333
261
|
export function resolveProviderApiKey(provider) {
|
|
334
|
-
if (provider ===
|
|
262
|
+
if (provider === DUET_GATEWAY_PROVIDER_ID) {
|
|
335
263
|
return process.env[DUET_GATEWAY_API_KEY_ENV];
|
|
336
264
|
}
|
|
337
265
|
if (isConnectedProviderId(provider)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duet-gateway.js","sourceRoot":"","sources":["../../../src/model-resolution/duet-gateway.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"duet-gateway.js","sourceRoot":"","sources":["../../../src/model-resolution/duet-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MutableModels } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { StreamFn } from "@earendil-works/pi-agent-core";
|
|
3
|
+
/**
|
|
4
|
+
* The model registry every dispatch goes through: pi-ai's built-in providers,
|
|
5
|
+
* the gateway routes, and connected providers with Duet's own credential path
|
|
6
|
+
* declared.
|
|
7
|
+
*
|
|
8
|
+
* The agent loop is handed a `StreamFn` rather than resolving a transport
|
|
9
|
+
* itself, so this is the one place that decides who can serve a model.
|
|
10
|
+
*
|
|
11
|
+
* Rebuilt per call rather than cached. The expensive part — rebasing two
|
|
12
|
+
* hundred catalog entries per route — is memoized behind `gatewayProvider`,
|
|
13
|
+
* which leaves assembly at ~0.05ms; a cache here would save that and buy a
|
|
14
|
+
* staleness bug, because its key would have to track every origin and
|
|
15
|
+
* credential the registry closes over.
|
|
16
|
+
*/
|
|
17
|
+
export declare function duetModels(): MutableModels;
|
|
18
|
+
/**
|
|
19
|
+
* Dispatch for the agent loop. The model object carries its own provider,
|
|
20
|
+
* baseUrl and tier headers by the time it arrives here, so this only has to
|
|
21
|
+
* pick the transport the model declares.
|
|
22
|
+
*/
|
|
23
|
+
export declare const duetStreamFn: StreamFn;
|
|
24
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../src/model-resolution/models.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,uBAAuB,CAAC;AAErE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAK9D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,IAAI,aAAa,CAQ1C;AA8BD;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,QACyB,CAAC"}
|