@nexface/agent 0.1.1-alpha.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/THIRD_PARTY_LICENSES.txt +245 -0
- package/dist/agent.d.ts +19 -0
- package/dist/agent.js +1509 -0
- package/dist/agent.js.map +1 -0
- package/dist/browser-prompt.d.ts +5 -0
- package/dist/browser-prompt.generated.d.ts +1 -0
- package/dist/browser-prompt.generated.js +50 -0
- package/dist/browser-prompt.generated.js.map +1 -0
- package/dist/browser-prompt.js +9 -0
- package/dist/browser-prompt.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.js +55 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/model/binding.d.ts +22 -0
- package/dist/model/binding.js +31 -0
- package/dist/model/binding.js.map +1 -0
- package/dist/model/error.d.ts +6 -0
- package/dist/model/error.js +10 -0
- package/dist/model/error.js.map +1 -0
- package/dist/model/internal-adapter.d.ts +8 -0
- package/dist/model/internal-adapter.js +13 -0
- package/dist/model/internal-adapter.js.map +1 -0
- package/dist/model/types.d.ts +9 -0
- package/dist/model/types.js +2 -0
- package/dist/model/types.js.map +1 -0
- package/dist/models/885.js +630 -0
- package/dist/models/956.js +5 -0
- package/dist/models/_chunks/35-e1813138.js +8304 -0
- package/dist/models/_chunks/879-7e580bb2.js +1189 -0
- package/dist/models/_chunks/958-bedced75.js +453 -0
- package/dist/models/_chunks/anthropic-messages~1-a4b25b48.js +8057 -0
- package/dist/models/_chunks/deferred-tools-90f3c977.js +37 -0
- package/dist/models/_chunks/error-body-8bee35c2.js +134 -0
- package/dist/models/_chunks/google-generative-ai~1-78bb2822.js +22104 -0
- package/dist/models/_chunks/openai-completions~1-7677fb83.js +1285 -0
- package/dist/models/_chunks/openai-responses~1-cc0a71cd.js +958 -0
- package/dist/models/anthropic-messages.d.ts +4 -0
- package/dist/models/anthropic-messages.js +32 -0
- package/dist/models/google-generative-ai.d.ts +4 -0
- package/dist/models/google-generative-ai.js +32 -0
- package/dist/models/openai-completions.d.ts +5 -0
- package/dist/models/openai-completions.js +16 -0
- package/dist/models/openai-responses.d.ts +5 -0
- package/dist/models/openai-responses.js +16 -0
- package/dist/models/rslib-runtime.js +59 -0
- package/dist/models/types.d.ts +26 -0
- package/dist/tool-bridge.d.ts +36 -0
- package/dist/tool-bridge.js +299 -0
- package/dist/tool-bridge.js.map +1 -0
- package/dist/types.d.ts +129 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import { defineAgentModelConfig } from "../model/binding.js";
|
|
2
|
+
|
|
3
|
+
// Generic event stream class for async iteration
|
|
4
|
+
class EventStream {
|
|
5
|
+
queue = [];
|
|
6
|
+
waiting = [];
|
|
7
|
+
done = false;
|
|
8
|
+
finalResultPromise;
|
|
9
|
+
resolveFinalResult;
|
|
10
|
+
isComplete;
|
|
11
|
+
extractResult;
|
|
12
|
+
constructor(isComplete, extractResult) {
|
|
13
|
+
this.isComplete = isComplete;
|
|
14
|
+
this.extractResult = extractResult;
|
|
15
|
+
this.finalResultPromise = new Promise((resolve) => {
|
|
16
|
+
this.resolveFinalResult = resolve;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
push(event) {
|
|
20
|
+
if (this.done)
|
|
21
|
+
return;
|
|
22
|
+
if (this.isComplete(event)) {
|
|
23
|
+
this.done = true;
|
|
24
|
+
this.resolveFinalResult(this.extractResult(event));
|
|
25
|
+
}
|
|
26
|
+
// Deliver to waiting consumer or queue it
|
|
27
|
+
const waiter = this.waiting.shift();
|
|
28
|
+
if (waiter) {
|
|
29
|
+
waiter({ value: event, done: false });
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.queue.push(event);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
end(result) {
|
|
36
|
+
this.done = true;
|
|
37
|
+
if (result !== undefined) {
|
|
38
|
+
this.resolveFinalResult(result);
|
|
39
|
+
}
|
|
40
|
+
// Notify all waiting consumers that we're done
|
|
41
|
+
while (this.waiting.length > 0) {
|
|
42
|
+
const waiter = this.waiting.shift();
|
|
43
|
+
waiter({ value: undefined, done: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async *[Symbol.asyncIterator]() {
|
|
47
|
+
while (true) {
|
|
48
|
+
if (this.queue.length > 0) {
|
|
49
|
+
yield this.queue.shift();
|
|
50
|
+
}
|
|
51
|
+
else if (this.done) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
const result = await new Promise((resolve) => this.waiting.push(resolve));
|
|
56
|
+
if (result.done)
|
|
57
|
+
return;
|
|
58
|
+
yield result.value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
result() {
|
|
63
|
+
return this.finalResultPromise;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
class AssistantMessageEventStream extends EventStream {
|
|
67
|
+
constructor() {
|
|
68
|
+
super((event) => event.type === "done" || event.type === "error", (event) => {
|
|
69
|
+
if (event.type === "done") {
|
|
70
|
+
return event.message;
|
|
71
|
+
}
|
|
72
|
+
else if (event.type === "error") {
|
|
73
|
+
return event.error;
|
|
74
|
+
}
|
|
75
|
+
throw new Error("Unexpected event type for final result");
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/** Factory function for AssistantMessageEventStream (for use in extensions) */
|
|
80
|
+
function createAssistantMessageEventStream() {
|
|
81
|
+
return new AssistantMessageEventStream();
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=event-stream.js.map
|
|
84
|
+
|
|
85
|
+
function createSetupErrorMessage(model, error) {
|
|
86
|
+
return {
|
|
87
|
+
role: "assistant",
|
|
88
|
+
content: [],
|
|
89
|
+
api: model.api,
|
|
90
|
+
provider: model.provider,
|
|
91
|
+
model: model.id,
|
|
92
|
+
usage: {
|
|
93
|
+
input: 0,
|
|
94
|
+
output: 0,
|
|
95
|
+
cacheRead: 0,
|
|
96
|
+
cacheWrite: 0,
|
|
97
|
+
totalTokens: 0,
|
|
98
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
99
|
+
},
|
|
100
|
+
stopReason: "error",
|
|
101
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
102
|
+
timestamp: Date.now(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function hasResult(source) {
|
|
106
|
+
return typeof source.result === "function";
|
|
107
|
+
}
|
|
108
|
+
async function forwardStream(target, source) {
|
|
109
|
+
for await (const event of source) {
|
|
110
|
+
target.push(event);
|
|
111
|
+
}
|
|
112
|
+
target.end(hasResult(source) ? await source.result() : undefined);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Returns a stream synchronously while running async setup (auth resolution,
|
|
116
|
+
* lazy module loading) behind it. Setup failures terminate the stream with an
|
|
117
|
+
* error event.
|
|
118
|
+
*/
|
|
119
|
+
function lazyStream(model, setup) {
|
|
120
|
+
const outer = new AssistantMessageEventStream();
|
|
121
|
+
setup()
|
|
122
|
+
.then((inner) => forwardStream(outer, inner))
|
|
123
|
+
.catch((error) => {
|
|
124
|
+
const message = createSetupErrorMessage(model, error);
|
|
125
|
+
outer.push({ type: "error", reason: "error", error: message });
|
|
126
|
+
outer.end(message);
|
|
127
|
+
});
|
|
128
|
+
return outer;
|
|
129
|
+
}
|
|
130
|
+
function lazyApi(load, capabilities) {
|
|
131
|
+
const api = {
|
|
132
|
+
stream: (model, context, options) => lazyStream(model, async () => (await load()).stream(model, context, options)),
|
|
133
|
+
streamSimple: (model, context, options) => lazyStream(model, async () => (await load()).streamSimple(model, context, options)),
|
|
134
|
+
};
|
|
135
|
+
if (capabilities?.fetchDeferred) {
|
|
136
|
+
api.fetchDeferred = (model, handle, options) => lazyStream(model, async () => {
|
|
137
|
+
const implementation = await load();
|
|
138
|
+
if (!implementation.fetchDeferred)
|
|
139
|
+
throw new Error("API does not support deferred responses");
|
|
140
|
+
return implementation.fetchDeferred(model, handle, options);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
if (capabilities?.cancelDeferred) {
|
|
144
|
+
api.cancelDeferred = async (model, handle, options) => {
|
|
145
|
+
const implementation = await load();
|
|
146
|
+
if (!implementation.cancelDeferred)
|
|
147
|
+
throw new Error("API cannot cancel deferred responses");
|
|
148
|
+
await implementation.cancelDeferred(model, handle, options);
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
return api;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=lazy.js.map
|
|
154
|
+
|
|
155
|
+
const AGENT_ERROR_CODES = [
|
|
156
|
+
"AGENT_RUN_ACTIVE",
|
|
157
|
+
"AGENT_INTERRUPT_PENDING",
|
|
158
|
+
"AGENT_RUN_LIMIT_REACHED",
|
|
159
|
+
"AGENT_TOOL_RUNTIME_UNAVAILABLE",
|
|
160
|
+
"BROWSER_SKILL_REQUIRED",
|
|
161
|
+
"CONTEXT_SNAPSHOT_TOO_LARGE",
|
|
162
|
+
"INVALID_MODEL_RESPONSE",
|
|
163
|
+
"INVALID_RUN_INPUT",
|
|
164
|
+
"INVALID_SKILL_CONFIG",
|
|
165
|
+
"INVALID_TOOL_BATCH",
|
|
166
|
+
"UNKNOWN_MODEL_TOOL"
|
|
167
|
+
];
|
|
168
|
+
const AGENT_MODEL_ERROR_CODES = [
|
|
169
|
+
"AGENT_MODEL_SWITCH_BLOCKED",
|
|
170
|
+
"AGENT_RUN_BLOCKED",
|
|
171
|
+
"INVALID_MODEL_RESPONSE",
|
|
172
|
+
"MODEL_CONFIG_INVALID",
|
|
173
|
+
"MODEL_NOT_FOUND",
|
|
174
|
+
"MODEL_REQUEST_FAILED",
|
|
175
|
+
"MODEL_INPUT_UNSUPPORTED"
|
|
176
|
+
];
|
|
177
|
+
const AGENT_ERROR_CODE_SET = new Set(AGENT_ERROR_CODES);
|
|
178
|
+
const AGENT_MODEL_ERROR_CODE_SET = new Set(AGENT_MODEL_ERROR_CODES);
|
|
179
|
+
function isAgentError(error) {
|
|
180
|
+
if (!error || typeof error !== "object") return false;
|
|
181
|
+
const candidate = error;
|
|
182
|
+
if (candidate.type !== "agent_error" || typeof candidate.code !== "string" || typeof candidate.message !== "string") {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
if (candidate.domain === "agent") {
|
|
186
|
+
return AGENT_ERROR_CODE_SET.has(candidate.code);
|
|
187
|
+
}
|
|
188
|
+
if (candidate.domain === "model") {
|
|
189
|
+
return AGENT_MODEL_ERROR_CODE_SET.has(candidate.code);
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
class AgentError extends Error {
|
|
194
|
+
code;
|
|
195
|
+
details;
|
|
196
|
+
domain;
|
|
197
|
+
type = "agent_error";
|
|
198
|
+
constructor(code, message, details, domain = "agent"){
|
|
199
|
+
super(message), this.code = code, this.details = details, this.domain = domain;
|
|
200
|
+
this.name = "AgentError";
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
class AgentModelError extends AgentError {
|
|
206
|
+
code;
|
|
207
|
+
constructor(code, message){
|
|
208
|
+
super(code, message, undefined, "model"), this.code = code;
|
|
209
|
+
this.name = "AgentModelError";
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
function parseArguments(value) {
|
|
215
|
+
try {
|
|
216
|
+
const parsed = JSON.parse(value);
|
|
217
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
218
|
+
} catch {
|
|
219
|
+
return {};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function genericAssistant(message, model) {
|
|
223
|
+
return {
|
|
224
|
+
role: "assistant",
|
|
225
|
+
api: model.api,
|
|
226
|
+
provider: model.provider,
|
|
227
|
+
model: model.id,
|
|
228
|
+
content: [
|
|
229
|
+
...message.content ? [
|
|
230
|
+
{
|
|
231
|
+
type: "text",
|
|
232
|
+
text: message.content
|
|
233
|
+
}
|
|
234
|
+
] : [],
|
|
235
|
+
...message.toolCalls.map((call)=>({
|
|
236
|
+
type: "toolCall",
|
|
237
|
+
id: call.id,
|
|
238
|
+
name: call.name,
|
|
239
|
+
arguments: parseArguments(call.arguments)
|
|
240
|
+
}))
|
|
241
|
+
],
|
|
242
|
+
usage: {
|
|
243
|
+
input: 0,
|
|
244
|
+
output: 0,
|
|
245
|
+
cacheRead: 0,
|
|
246
|
+
cacheWrite: 0,
|
|
247
|
+
totalTokens: 0,
|
|
248
|
+
cost: {
|
|
249
|
+
input: 0,
|
|
250
|
+
output: 0,
|
|
251
|
+
cacheRead: 0,
|
|
252
|
+
cacheWrite: 0,
|
|
253
|
+
total: 0
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
stopReason: message.toolCalls.length ? "toolUse" : "stop",
|
|
257
|
+
timestamp: Date.now()
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function toolIsError(content) {
|
|
261
|
+
try {
|
|
262
|
+
const parsed = JSON.parse(content);
|
|
263
|
+
return "error" in parsed;
|
|
264
|
+
} catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function createPiContext(messages, tools, model, replay) {
|
|
269
|
+
// pi-ai exposes one trusted systemPrompt rather than chronological system messages.
|
|
270
|
+
// Keep canonical checkpoints append-only, then consolidate them in their original order.
|
|
271
|
+
const system = [];
|
|
272
|
+
const piMessages = [];
|
|
273
|
+
messages.forEach((message, index)=>{
|
|
274
|
+
if (message.role === "system" || message.role === "application_context") {
|
|
275
|
+
system.push(message.content);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (message.role === "user") {
|
|
279
|
+
const content = typeof message.content === "string" ? message.content : message.content.map((part)=>{
|
|
280
|
+
if (part.type === "text") return part;
|
|
281
|
+
if (!model.input.includes("image")) throw new AgentModelError("MODEL_INPUT_UNSUPPORTED", "The configured model does not support image input.");
|
|
282
|
+
if (part.source.type !== "data") throw new AgentModelError("MODEL_INPUT_UNSUPPORTED", "Only inline image data is supported.");
|
|
283
|
+
return {
|
|
284
|
+
type: "image",
|
|
285
|
+
data: part.source.value,
|
|
286
|
+
mimeType: part.source.mimeType
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
piMessages.push({
|
|
290
|
+
role: "user",
|
|
291
|
+
content,
|
|
292
|
+
timestamp: Date.now()
|
|
293
|
+
});
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
if (message.role === "assistant") {
|
|
297
|
+
piMessages.push(replay.get(index) ?? genericAssistant(message, model));
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
if (message.role === "tool") {
|
|
301
|
+
piMessages.push({
|
|
302
|
+
role: "toolResult",
|
|
303
|
+
toolCallId: message.toolCallId,
|
|
304
|
+
toolName: message.name,
|
|
305
|
+
content: [
|
|
306
|
+
{
|
|
307
|
+
type: "text",
|
|
308
|
+
text: message.content
|
|
309
|
+
}
|
|
310
|
+
],
|
|
311
|
+
isError: toolIsError(message.content),
|
|
312
|
+
timestamp: Date.now()
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
const piTools = tools.map((tool)=>({
|
|
317
|
+
name: tool.name,
|
|
318
|
+
description: tool.description,
|
|
319
|
+
parameters: tool.inputSchema
|
|
320
|
+
}));
|
|
321
|
+
return {
|
|
322
|
+
...system.length ? {
|
|
323
|
+
systemPrompt: system.join("\n\n")
|
|
324
|
+
} : {},
|
|
325
|
+
messages: piMessages,
|
|
326
|
+
tools: piTools
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function fromPiAssistant(message) {
|
|
330
|
+
const text = message.content.filter((block)=>block.type === "text").map((block)=>block.text).join("");
|
|
331
|
+
return {
|
|
332
|
+
content: text || null,
|
|
333
|
+
toolCalls: message.content.filter((block)=>block.type === "toolCall").map((block)=>({
|
|
334
|
+
id: block.id,
|
|
335
|
+
name: block.name,
|
|
336
|
+
arguments: JSON.stringify(block.arguments)
|
|
337
|
+
})),
|
|
338
|
+
usage: {
|
|
339
|
+
promptTokens: message.usage.input,
|
|
340
|
+
cachedPromptTokens: message.usage.cacheRead,
|
|
341
|
+
cacheMissPromptTokens: Math.max(0, message.usage.input - message.usage.cacheRead),
|
|
342
|
+
completionTokens: message.usage.output,
|
|
343
|
+
totalTokens: message.usage.totalTokens
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
const DEFAULT_CAPABILITIES = {
|
|
352
|
+
reasoning: false,
|
|
353
|
+
input: [
|
|
354
|
+
"text"
|
|
355
|
+
],
|
|
356
|
+
contextWindow: 128000,
|
|
357
|
+
maxOutputTokens: 16384
|
|
358
|
+
};
|
|
359
|
+
const KEYLESS_GATEWAY_API_KEY = "nexface-keyless-gateway";
|
|
360
|
+
function invalid(message) {
|
|
361
|
+
throw new AgentModelError("MODEL_CONFIG_INVALID", message);
|
|
362
|
+
}
|
|
363
|
+
function assertNonEmpty(value, name) {
|
|
364
|
+
if (typeof value !== "string" || !value.trim()) invalid(`${name} must be a non-empty string.`);
|
|
365
|
+
}
|
|
366
|
+
function normalizedBaseURL(value, fallback) {
|
|
367
|
+
const candidate = value ?? fallback;
|
|
368
|
+
assertNonEmpty(candidate, "baseURL");
|
|
369
|
+
const browserOrigin = globalThis.location?.origin;
|
|
370
|
+
let url;
|
|
371
|
+
try {
|
|
372
|
+
url = browserOrigin ? new URL(candidate, browserOrigin) : new URL(candidate);
|
|
373
|
+
} catch {
|
|
374
|
+
return invalid("baseURL must be absolute outside a browser environment.");
|
|
375
|
+
}
|
|
376
|
+
if (url.search || url.hash) invalid("baseURL must not contain a query or hash.");
|
|
377
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
378
|
+
invalid("baseURL must use http or https.");
|
|
379
|
+
}
|
|
380
|
+
if (url.username || url.password) invalid("baseURL must not contain credentials.");
|
|
381
|
+
url.pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
382
|
+
return url.toString().replace(/\/$/, "");
|
|
383
|
+
}
|
|
384
|
+
function hasAuthenticationHeader(headers) {
|
|
385
|
+
const authenticationHeaders = new Set([
|
|
386
|
+
"authorization",
|
|
387
|
+
"x-api-key",
|
|
388
|
+
"x-goog-api-key"
|
|
389
|
+
]);
|
|
390
|
+
for (const [name, value] of Object.entries(headers ?? {})){
|
|
391
|
+
if (!authenticationHeaders.has(name.toLowerCase())) continue;
|
|
392
|
+
assertNonEmpty(value, `headers.${name}`);
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
function assertFiniteNumber(value, name, minimum) {
|
|
398
|
+
if (value !== undefined && (!Number.isFinite(value) || value < minimum)) {
|
|
399
|
+
invalid(`${name} must be a finite number greater than or equal to ${minimum}.`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function stripKeylessSecret(input, init) {
|
|
403
|
+
const headers = new Headers(init?.headers);
|
|
404
|
+
for (const name of [
|
|
405
|
+
"authorization",
|
|
406
|
+
"x-api-key",
|
|
407
|
+
"x-goog-api-key"
|
|
408
|
+
]){
|
|
409
|
+
const value = headers.get(name);
|
|
410
|
+
if (value === KEYLESS_GATEWAY_API_KEY || value === `Bearer ${KEYLESS_GATEWAY_API_KEY}`) {
|
|
411
|
+
headers.delete(name);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
let nextInput = input;
|
|
415
|
+
const rawURL = input instanceof Request ? input.url : String(input);
|
|
416
|
+
try {
|
|
417
|
+
const url = new URL(rawURL, globalThis.location?.origin);
|
|
418
|
+
for (const name of [
|
|
419
|
+
"key",
|
|
420
|
+
"api_key"
|
|
421
|
+
]){
|
|
422
|
+
if (url.searchParams.get(name) === KEYLESS_GATEWAY_API_KEY) url.searchParams.delete(name);
|
|
423
|
+
}
|
|
424
|
+
nextInput = input instanceof Request ? new Request(url, input) : url;
|
|
425
|
+
} catch {
|
|
426
|
+
// Keep non-URL RequestInfo unchanged.
|
|
427
|
+
}
|
|
428
|
+
return [
|
|
429
|
+
nextInput,
|
|
430
|
+
{
|
|
431
|
+
...init,
|
|
432
|
+
headers
|
|
433
|
+
}
|
|
434
|
+
];
|
|
435
|
+
}
|
|
436
|
+
function createKeylessFetch(fetchImpl) {
|
|
437
|
+
return async (input, init)=>{
|
|
438
|
+
const [nextInput, nextInit] = stripKeylessSecret(input, init);
|
|
439
|
+
return fetchImpl(nextInput, nextInit);
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
async function consumeStream(stream, onTextDelta) {
|
|
443
|
+
let result;
|
|
444
|
+
for await (const event of stream){
|
|
445
|
+
if (event.type === "text_delta") onTextDelta?.(event.delta);
|
|
446
|
+
if (event.type === "done") result = event.message;
|
|
447
|
+
if (event.type === "error") {
|
|
448
|
+
if (event.reason === "aborted") {
|
|
449
|
+
throw new DOMException("The model request was aborted.", "AbortError");
|
|
450
|
+
}
|
|
451
|
+
throw new AgentModelError("MODEL_REQUEST_FAILED", "The model request failed.");
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (!result) {
|
|
455
|
+
throw new AgentModelError("INVALID_MODEL_RESPONSE", "Model stream ended without a final message.");
|
|
456
|
+
}
|
|
457
|
+
return result;
|
|
458
|
+
}
|
|
459
|
+
class PiModelBinding {
|
|
460
|
+
info;
|
|
461
|
+
#replay = new Map();
|
|
462
|
+
#model;
|
|
463
|
+
#stream;
|
|
464
|
+
#options;
|
|
465
|
+
constructor(info, model, stream, options){
|
|
466
|
+
this.info = info;
|
|
467
|
+
this.#model = model;
|
|
468
|
+
this.#stream = stream;
|
|
469
|
+
this.#options = options;
|
|
470
|
+
}
|
|
471
|
+
async complete(request) {
|
|
472
|
+
const raw = await consumeStream(this.#stream.streamSimple(this.#model, createPiContext(request.messages, request.tools, this.#model, this.#replay), {
|
|
473
|
+
...this.#options,
|
|
474
|
+
signal: request.signal,
|
|
475
|
+
sessionId: request.agentId
|
|
476
|
+
}), request.onTextDelta);
|
|
477
|
+
const response = fromPiAssistant(raw);
|
|
478
|
+
const replayIndex = request.messages.length;
|
|
479
|
+
let accepted = false;
|
|
480
|
+
return {
|
|
481
|
+
...response,
|
|
482
|
+
accept: ()=>{
|
|
483
|
+
if (accepted) return;
|
|
484
|
+
accepted = true;
|
|
485
|
+
this.#replay.set(replayIndex, raw);
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function resolveModel(definition, config, baseURL) {
|
|
491
|
+
const catalogModel = definition.resolveCatalogModel?.(config.model);
|
|
492
|
+
if (definition.resolveCatalogModel && !catalogModel) {
|
|
493
|
+
throw new AgentModelError("MODEL_NOT_FOUND", `Model "${config.model}" was not found for provider "${definition.provider}".`);
|
|
494
|
+
}
|
|
495
|
+
const capabilities = {
|
|
496
|
+
...DEFAULT_CAPABILITIES,
|
|
497
|
+
...config.capabilities
|
|
498
|
+
};
|
|
499
|
+
if (catalogModel) {
|
|
500
|
+
return {
|
|
501
|
+
...catalogModel,
|
|
502
|
+
baseUrl: baseURL,
|
|
503
|
+
...config.capabilities?.reasoning === undefined ? {} : {
|
|
504
|
+
reasoning: config.capabilities.reasoning
|
|
505
|
+
},
|
|
506
|
+
...config.capabilities?.input === undefined ? {} : {
|
|
507
|
+
input: [
|
|
508
|
+
...config.capabilities.input
|
|
509
|
+
]
|
|
510
|
+
},
|
|
511
|
+
...config.capabilities?.contextWindow === undefined ? {} : {
|
|
512
|
+
contextWindow: config.capabilities.contextWindow
|
|
513
|
+
},
|
|
514
|
+
...config.capabilities?.maxOutputTokens === undefined ? {} : {
|
|
515
|
+
maxTokens: config.capabilities.maxOutputTokens
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
id: config.model,
|
|
521
|
+
name: config.model,
|
|
522
|
+
api: definition.api,
|
|
523
|
+
provider: definition.provider,
|
|
524
|
+
baseUrl: baseURL,
|
|
525
|
+
reasoning: capabilities.reasoning,
|
|
526
|
+
input: capabilities.input,
|
|
527
|
+
cost: {
|
|
528
|
+
input: 0,
|
|
529
|
+
output: 0,
|
|
530
|
+
cacheRead: 0,
|
|
531
|
+
cacheWrite: 0
|
|
532
|
+
},
|
|
533
|
+
contextWindow: capabilities.contextWindow,
|
|
534
|
+
maxTokens: capabilities.maxOutputTokens
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function createProtocolModelConfig(definition, config) {
|
|
538
|
+
if (!config || typeof config !== "object") invalid("model config must be an object.");
|
|
539
|
+
assertNonEmpty(config.model, "model");
|
|
540
|
+
if (config.apiKey !== undefined) assertNonEmpty(config.apiKey, "apiKey");
|
|
541
|
+
if (config.apiKey && hasAuthenticationHeader(config.headers)) {
|
|
542
|
+
invalid("apiKey and an authentication header cannot be configured together.");
|
|
543
|
+
}
|
|
544
|
+
if (definition.supportsCustomFetch === false && config.fetch) {
|
|
545
|
+
invalid(`Custom fetch is not supported for provider "${definition.provider}".`);
|
|
546
|
+
}
|
|
547
|
+
if (definition.resolveCatalogModel && config.capabilities !== undefined) {
|
|
548
|
+
invalid(`Capability overrides are not supported for provider "${definition.provider}".`);
|
|
549
|
+
}
|
|
550
|
+
assertFiniteNumber(config.temperature, "temperature", 0);
|
|
551
|
+
assertFiniteNumber(config.maxTokens, "maxTokens", 1);
|
|
552
|
+
assertFiniteNumber(config.timeoutMs, "timeoutMs", 1);
|
|
553
|
+
assertFiniteNumber(config.maxRetries, "maxRetries", 0);
|
|
554
|
+
if (config.maxRetries !== undefined && !Number.isInteger(config.maxRetries)) {
|
|
555
|
+
invalid("maxRetries must be an integer.");
|
|
556
|
+
}
|
|
557
|
+
assertFiniteNumber(config.capabilities?.contextWindow, "capabilities.contextWindow", 1);
|
|
558
|
+
assertFiniteNumber(config.capabilities?.maxOutputTokens, "capabilities.maxOutputTokens", 1);
|
|
559
|
+
const baseURL = normalizedBaseURL(config.baseURL, definition.defaultBaseURL);
|
|
560
|
+
const browserOrigin = globalThis.location?.origin;
|
|
561
|
+
if (browserOrigin && config.apiKey === undefined && !hasAuthenticationHeader(config.headers) && new URL(baseURL).origin !== browserOrigin) {
|
|
562
|
+
invalid("Keyless browser model requests must use a same-origin gateway.");
|
|
563
|
+
}
|
|
564
|
+
if ((definition.api === "openai-completions" || definition.api === "openai-responses") && /\/(?:chat\/completions|responses)$/i.test(new URL(baseURL).pathname)) {
|
|
565
|
+
invalid("baseURL must be an API root and must not include the final endpoint.");
|
|
566
|
+
}
|
|
567
|
+
const preparedConfig = {
|
|
568
|
+
...config,
|
|
569
|
+
baseURL,
|
|
570
|
+
...config.headers === undefined ? {} : {
|
|
571
|
+
headers: {
|
|
572
|
+
...config.headers
|
|
573
|
+
}
|
|
574
|
+
},
|
|
575
|
+
...config.capabilities === undefined ? {} : {
|
|
576
|
+
capabilities: {
|
|
577
|
+
...config.capabilities,
|
|
578
|
+
...config.capabilities.input === undefined ? {} : {
|
|
579
|
+
input: [
|
|
580
|
+
...config.capabilities.input
|
|
581
|
+
]
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
const info = {
|
|
587
|
+
provider: definition.provider,
|
|
588
|
+
model: preparedConfig.model
|
|
589
|
+
};
|
|
590
|
+
return defineAgentModelConfig({
|
|
591
|
+
info,
|
|
592
|
+
async resolve () {
|
|
593
|
+
const configuredFetch = preparedConfig.fetch ?? globalThis.fetch;
|
|
594
|
+
const options = {
|
|
595
|
+
...preparedConfig.temperature === undefined ? {} : {
|
|
596
|
+
temperature: preparedConfig.temperature
|
|
597
|
+
},
|
|
598
|
+
...preparedConfig.maxTokens === undefined ? {} : {
|
|
599
|
+
maxTokens: preparedConfig.maxTokens
|
|
600
|
+
},
|
|
601
|
+
...preparedConfig.reasoning === undefined ? {} : {
|
|
602
|
+
reasoning: preparedConfig.reasoning
|
|
603
|
+
},
|
|
604
|
+
...preparedConfig.timeoutMs === undefined ? {} : {
|
|
605
|
+
timeoutMs: preparedConfig.timeoutMs
|
|
606
|
+
},
|
|
607
|
+
...preparedConfig.maxRetries === undefined ? {} : {
|
|
608
|
+
maxRetries: preparedConfig.maxRetries
|
|
609
|
+
},
|
|
610
|
+
...preparedConfig.headers === undefined ? {} : {
|
|
611
|
+
headers: preparedConfig.headers
|
|
612
|
+
},
|
|
613
|
+
...definition.supportsCustomFetch === false || preparedConfig.apiKey === undefined ? {} : {
|
|
614
|
+
fetch: configuredFetch
|
|
615
|
+
},
|
|
616
|
+
...preparedConfig.apiKey === undefined ? {
|
|
617
|
+
apiKey: KEYLESS_GATEWAY_API_KEY,
|
|
618
|
+
...definition.supportsCustomFetch === false ? {} : {
|
|
619
|
+
fetch: createKeylessFetch(configuredFetch)
|
|
620
|
+
}
|
|
621
|
+
} : {
|
|
622
|
+
apiKey: preparedConfig.apiKey
|
|
623
|
+
}
|
|
624
|
+
};
|
|
625
|
+
return new PiModelBinding(info, resolveModel(definition, preparedConfig, baseURL), definition.stream, options);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
export { AgentModelError, AssistantMessageEventStream, createProtocolModelConfig, lazyApi };
|