@providerprotocol/ai 0.0.1
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/LICENSE +21 -0
- package/README.md +84 -0
- package/dist/anthropic/index.d.ts +41 -0
- package/dist/anthropic/index.js +500 -0
- package/dist/anthropic/index.js.map +1 -0
- package/dist/chunk-CUCRF5W6.js +136 -0
- package/dist/chunk-CUCRF5W6.js.map +1 -0
- package/dist/chunk-FTFX2VET.js +424 -0
- package/dist/chunk-FTFX2VET.js.map +1 -0
- package/dist/chunk-QUUX4G7U.js +117 -0
- package/dist/chunk-QUUX4G7U.js.map +1 -0
- package/dist/chunk-Y6Q7JCNP.js +39 -0
- package/dist/chunk-Y6Q7JCNP.js.map +1 -0
- package/dist/google/index.d.ts +69 -0
- package/dist/google/index.js +517 -0
- package/dist/google/index.js.map +1 -0
- package/dist/http/index.d.ts +61 -0
- package/dist/http/index.js +43 -0
- package/dist/http/index.js.map +1 -0
- package/dist/index.d.ts +792 -0
- package/dist/index.js +898 -0
- package/dist/index.js.map +1 -0
- package/dist/openai/index.d.ts +204 -0
- package/dist/openai/index.js +1340 -0
- package/dist/openai/index.js.map +1 -0
- package/dist/provider-CUJWjgNl.d.ts +192 -0
- package/dist/retry-I2661_rv.d.ts +118 -0
- package/package.json +88 -0
- package/src/anthropic/index.ts +3 -0
- package/src/core/image.ts +188 -0
- package/src/core/llm.ts +619 -0
- package/src/core/provider.ts +92 -0
- package/src/google/index.ts +3 -0
- package/src/http/errors.ts +112 -0
- package/src/http/fetch.ts +210 -0
- package/src/http/index.ts +31 -0
- package/src/http/keys.ts +136 -0
- package/src/http/retry.ts +205 -0
- package/src/http/sse.ts +136 -0
- package/src/index.ts +32 -0
- package/src/openai/index.ts +9 -0
- package/src/providers/anthropic/index.ts +17 -0
- package/src/providers/anthropic/llm.ts +196 -0
- package/src/providers/anthropic/transform.ts +452 -0
- package/src/providers/anthropic/types.ts +213 -0
- package/src/providers/google/index.ts +17 -0
- package/src/providers/google/llm.ts +203 -0
- package/src/providers/google/transform.ts +487 -0
- package/src/providers/google/types.ts +214 -0
- package/src/providers/openai/index.ts +151 -0
- package/src/providers/openai/llm.completions.ts +201 -0
- package/src/providers/openai/llm.responses.ts +211 -0
- package/src/providers/openai/transform.completions.ts +628 -0
- package/src/providers/openai/transform.responses.ts +718 -0
- package/src/providers/openai/types.ts +711 -0
- package/src/types/content.ts +133 -0
- package/src/types/errors.ts +85 -0
- package/src/types/index.ts +105 -0
- package/src/types/llm.ts +211 -0
- package/src/types/messages.ts +182 -0
- package/src/types/provider.ts +195 -0
- package/src/types/schema.ts +58 -0
- package/src/types/stream.ts +146 -0
- package/src/types/thread.ts +226 -0
- package/src/types/tool.ts +88 -0
- package/src/types/turn.ts +118 -0
- package/src/utils/id.ts +28 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createProvider
|
|
3
|
+
} from "./chunk-Y6Q7JCNP.js";
|
|
4
|
+
import {
|
|
5
|
+
AssistantMessage,
|
|
6
|
+
Message,
|
|
7
|
+
ToolResultMessage,
|
|
8
|
+
UserMessage,
|
|
9
|
+
generateId,
|
|
10
|
+
isAssistantMessage,
|
|
11
|
+
isToolResultMessage,
|
|
12
|
+
isUserMessage
|
|
13
|
+
} from "./chunk-QUUX4G7U.js";
|
|
14
|
+
import {
|
|
15
|
+
ExponentialBackoff,
|
|
16
|
+
LinearBackoff,
|
|
17
|
+
NoRetry,
|
|
18
|
+
RetryAfterStrategy,
|
|
19
|
+
TokenBucket
|
|
20
|
+
} from "./chunk-CUCRF5W6.js";
|
|
21
|
+
import {
|
|
22
|
+
DynamicKey,
|
|
23
|
+
RoundRobinKeys,
|
|
24
|
+
UPPError,
|
|
25
|
+
WeightedKeys
|
|
26
|
+
} from "./chunk-FTFX2VET.js";
|
|
27
|
+
|
|
28
|
+
// src/types/turn.ts
|
|
29
|
+
function createTurn(messages, toolExecutions, usage, cycles, data) {
|
|
30
|
+
const response = messages.filter((m) => m.type === "assistant").pop();
|
|
31
|
+
if (!response) {
|
|
32
|
+
throw new Error("Turn must contain at least one assistant message");
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
messages,
|
|
36
|
+
response,
|
|
37
|
+
toolExecutions,
|
|
38
|
+
usage,
|
|
39
|
+
cycles,
|
|
40
|
+
data
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function emptyUsage() {
|
|
44
|
+
return {
|
|
45
|
+
inputTokens: 0,
|
|
46
|
+
outputTokens: 0,
|
|
47
|
+
totalTokens: 0,
|
|
48
|
+
cycles: []
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function aggregateUsage(usages) {
|
|
52
|
+
const cycles = [];
|
|
53
|
+
let inputTokens = 0;
|
|
54
|
+
let outputTokens = 0;
|
|
55
|
+
for (const usage of usages) {
|
|
56
|
+
inputTokens += usage.inputTokens;
|
|
57
|
+
outputTokens += usage.outputTokens;
|
|
58
|
+
cycles.push({
|
|
59
|
+
inputTokens: usage.inputTokens,
|
|
60
|
+
outputTokens: usage.outputTokens
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
inputTokens,
|
|
65
|
+
outputTokens,
|
|
66
|
+
totalTokens: inputTokens + outputTokens,
|
|
67
|
+
cycles
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/types/stream.ts
|
|
72
|
+
function createStreamResult(generator, turnPromise, abortController) {
|
|
73
|
+
return {
|
|
74
|
+
[Symbol.asyncIterator]() {
|
|
75
|
+
return generator;
|
|
76
|
+
},
|
|
77
|
+
turn: turnPromise,
|
|
78
|
+
abort() {
|
|
79
|
+
abortController.abort();
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function textDelta(text2, index = 0) {
|
|
84
|
+
return {
|
|
85
|
+
type: "text_delta",
|
|
86
|
+
index,
|
|
87
|
+
delta: { text: text2 }
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function toolCallDelta(toolCallId, toolName, argumentsJson, index = 0) {
|
|
91
|
+
return {
|
|
92
|
+
type: "tool_call_delta",
|
|
93
|
+
index,
|
|
94
|
+
delta: { toolCallId, toolName, argumentsJson }
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function messageStart() {
|
|
98
|
+
return {
|
|
99
|
+
type: "message_start",
|
|
100
|
+
index: 0,
|
|
101
|
+
delta: {}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function messageStop() {
|
|
105
|
+
return {
|
|
106
|
+
type: "message_stop",
|
|
107
|
+
index: 0,
|
|
108
|
+
delta: {}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function contentBlockStart(index) {
|
|
112
|
+
return {
|
|
113
|
+
type: "content_block_start",
|
|
114
|
+
index,
|
|
115
|
+
delta: {}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function contentBlockStop(index) {
|
|
119
|
+
return {
|
|
120
|
+
type: "content_block_stop",
|
|
121
|
+
index,
|
|
122
|
+
delta: {}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/core/llm.ts
|
|
127
|
+
var DEFAULT_MAX_ITERATIONS = 10;
|
|
128
|
+
function llm(options) {
|
|
129
|
+
const { model: modelRef, config = {}, params, system, tools, toolStrategy, structure } = options;
|
|
130
|
+
const provider = modelRef.provider;
|
|
131
|
+
if (!provider.modalities.llm) {
|
|
132
|
+
throw new UPPError(
|
|
133
|
+
`Provider '${provider.name}' does not support LLM modality`,
|
|
134
|
+
"INVALID_REQUEST",
|
|
135
|
+
provider.name,
|
|
136
|
+
"llm"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const boundModel = provider.modalities.llm.bind(modelRef.modelId);
|
|
140
|
+
const capabilities = boundModel.capabilities;
|
|
141
|
+
if (structure && !capabilities.structuredOutput) {
|
|
142
|
+
throw new UPPError(
|
|
143
|
+
`Provider '${provider.name}' does not support structured output`,
|
|
144
|
+
"INVALID_REQUEST",
|
|
145
|
+
provider.name,
|
|
146
|
+
"llm"
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
if (tools && tools.length > 0 && !capabilities.tools) {
|
|
150
|
+
throw new UPPError(
|
|
151
|
+
`Provider '${provider.name}' does not support tools`,
|
|
152
|
+
"INVALID_REQUEST",
|
|
153
|
+
provider.name,
|
|
154
|
+
"llm"
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const instance = {
|
|
158
|
+
model: boundModel,
|
|
159
|
+
system,
|
|
160
|
+
params,
|
|
161
|
+
capabilities,
|
|
162
|
+
async generate(historyOrInput, ...inputs) {
|
|
163
|
+
const { history, messages } = parseInputs(historyOrInput, inputs);
|
|
164
|
+
return executeGenerate(
|
|
165
|
+
boundModel,
|
|
166
|
+
config,
|
|
167
|
+
system,
|
|
168
|
+
params,
|
|
169
|
+
tools,
|
|
170
|
+
toolStrategy,
|
|
171
|
+
structure,
|
|
172
|
+
history,
|
|
173
|
+
messages
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
stream(historyOrInput, ...inputs) {
|
|
177
|
+
if (!capabilities.streaming) {
|
|
178
|
+
throw new UPPError(
|
|
179
|
+
`Provider '${provider.name}' does not support streaming`,
|
|
180
|
+
"INVALID_REQUEST",
|
|
181
|
+
provider.name,
|
|
182
|
+
"llm"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const { history, messages } = parseInputs(historyOrInput, inputs);
|
|
186
|
+
return executeStream(
|
|
187
|
+
boundModel,
|
|
188
|
+
config,
|
|
189
|
+
system,
|
|
190
|
+
params,
|
|
191
|
+
tools,
|
|
192
|
+
toolStrategy,
|
|
193
|
+
structure,
|
|
194
|
+
history,
|
|
195
|
+
messages
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
return instance;
|
|
200
|
+
}
|
|
201
|
+
function isMessageInstance(value) {
|
|
202
|
+
if (value instanceof Message) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
if (typeof value === "object" && value !== null && "timestamp" in value && "type" in value && "id" in value) {
|
|
206
|
+
const obj = value;
|
|
207
|
+
const messageTypes = ["user", "assistant", "tool_result"];
|
|
208
|
+
return messageTypes.includes(obj.type);
|
|
209
|
+
}
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
function parseInputs(historyOrInput, inputs) {
|
|
213
|
+
if (typeof historyOrInput === "object" && historyOrInput !== null && "messages" in historyOrInput && Array.isArray(historyOrInput.messages)) {
|
|
214
|
+
const thread = historyOrInput;
|
|
215
|
+
const newMessages2 = inputs.map(inputToMessage);
|
|
216
|
+
return { history: [...thread.messages], messages: newMessages2 };
|
|
217
|
+
}
|
|
218
|
+
if (Array.isArray(historyOrInput) && historyOrInput.length > 0) {
|
|
219
|
+
const first = historyOrInput[0];
|
|
220
|
+
if (isMessageInstance(first)) {
|
|
221
|
+
const newMessages2 = inputs.map(inputToMessage);
|
|
222
|
+
return { history: historyOrInput, messages: newMessages2 };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const allInputs = [historyOrInput, ...inputs];
|
|
226
|
+
const newMessages = allInputs.map(inputToMessage);
|
|
227
|
+
return { history: [], messages: newMessages };
|
|
228
|
+
}
|
|
229
|
+
function inputToMessage(input) {
|
|
230
|
+
if (typeof input === "string") {
|
|
231
|
+
return new UserMessage(input);
|
|
232
|
+
}
|
|
233
|
+
if ("type" in input && "id" in input && "timestamp" in input) {
|
|
234
|
+
return input;
|
|
235
|
+
}
|
|
236
|
+
const block = input;
|
|
237
|
+
if (block.type === "text") {
|
|
238
|
+
return new UserMessage(block.text);
|
|
239
|
+
}
|
|
240
|
+
return new UserMessage([block]);
|
|
241
|
+
}
|
|
242
|
+
async function executeGenerate(model, config, system, params, tools, toolStrategy, structure, history, newMessages) {
|
|
243
|
+
validateMediaCapabilities(
|
|
244
|
+
[...history, ...newMessages],
|
|
245
|
+
model.capabilities,
|
|
246
|
+
model.provider.name
|
|
247
|
+
);
|
|
248
|
+
const maxIterations = toolStrategy?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
249
|
+
const allMessages = [...history, ...newMessages];
|
|
250
|
+
const toolExecutions = [];
|
|
251
|
+
const usages = [];
|
|
252
|
+
let cycles = 0;
|
|
253
|
+
let structuredData;
|
|
254
|
+
while (cycles < maxIterations + 1) {
|
|
255
|
+
cycles++;
|
|
256
|
+
const request = {
|
|
257
|
+
messages: allMessages,
|
|
258
|
+
system,
|
|
259
|
+
params,
|
|
260
|
+
tools,
|
|
261
|
+
structure,
|
|
262
|
+
config
|
|
263
|
+
};
|
|
264
|
+
const response = await model.complete(request);
|
|
265
|
+
usages.push(response.usage);
|
|
266
|
+
allMessages.push(response.message);
|
|
267
|
+
if (response.data !== void 0) {
|
|
268
|
+
structuredData = response.data;
|
|
269
|
+
}
|
|
270
|
+
if (response.message.hasToolCalls && tools && tools.length > 0) {
|
|
271
|
+
if (response.data !== void 0) {
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
if (cycles >= maxIterations) {
|
|
275
|
+
await toolStrategy?.onMaxIterations?.(maxIterations);
|
|
276
|
+
throw new UPPError(
|
|
277
|
+
`Tool execution exceeded maximum iterations (${maxIterations})`,
|
|
278
|
+
"INVALID_REQUEST",
|
|
279
|
+
model.provider.name,
|
|
280
|
+
"llm"
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const results = await executeTools(
|
|
284
|
+
response.message,
|
|
285
|
+
tools,
|
|
286
|
+
toolStrategy,
|
|
287
|
+
toolExecutions
|
|
288
|
+
);
|
|
289
|
+
allMessages.push(new ToolResultMessage(results));
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
break;
|
|
293
|
+
}
|
|
294
|
+
const data = structure ? structuredData : void 0;
|
|
295
|
+
return createTurn(
|
|
296
|
+
allMessages.slice(history.length),
|
|
297
|
+
// Only messages from this turn
|
|
298
|
+
toolExecutions,
|
|
299
|
+
aggregateUsage(usages),
|
|
300
|
+
cycles,
|
|
301
|
+
data
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
function executeStream(model, config, system, params, tools, toolStrategy, structure, history, newMessages) {
|
|
305
|
+
validateMediaCapabilities(
|
|
306
|
+
[...history, ...newMessages],
|
|
307
|
+
model.capabilities,
|
|
308
|
+
model.provider.name
|
|
309
|
+
);
|
|
310
|
+
const abortController = new AbortController();
|
|
311
|
+
const allMessages = [...history, ...newMessages];
|
|
312
|
+
const toolExecutions = [];
|
|
313
|
+
const usages = [];
|
|
314
|
+
let cycles = 0;
|
|
315
|
+
let generatorError = null;
|
|
316
|
+
let structuredData;
|
|
317
|
+
let resolveGenerator;
|
|
318
|
+
let rejectGenerator;
|
|
319
|
+
const generatorDone = new Promise((resolve, reject) => {
|
|
320
|
+
resolveGenerator = resolve;
|
|
321
|
+
rejectGenerator = reject;
|
|
322
|
+
});
|
|
323
|
+
const maxIterations = toolStrategy?.maxIterations ?? DEFAULT_MAX_ITERATIONS;
|
|
324
|
+
async function* generateStream() {
|
|
325
|
+
try {
|
|
326
|
+
while (cycles < maxIterations + 1) {
|
|
327
|
+
cycles++;
|
|
328
|
+
const request = {
|
|
329
|
+
messages: allMessages,
|
|
330
|
+
system,
|
|
331
|
+
params,
|
|
332
|
+
tools,
|
|
333
|
+
structure,
|
|
334
|
+
config,
|
|
335
|
+
signal: abortController.signal
|
|
336
|
+
};
|
|
337
|
+
const streamResult = model.stream(request);
|
|
338
|
+
for await (const event of streamResult) {
|
|
339
|
+
yield event;
|
|
340
|
+
}
|
|
341
|
+
const response = await streamResult.response;
|
|
342
|
+
usages.push(response.usage);
|
|
343
|
+
allMessages.push(response.message);
|
|
344
|
+
if (response.data !== void 0) {
|
|
345
|
+
structuredData = response.data;
|
|
346
|
+
}
|
|
347
|
+
if (response.message.hasToolCalls && tools && tools.length > 0) {
|
|
348
|
+
if (response.data !== void 0) {
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
if (cycles >= maxIterations) {
|
|
352
|
+
await toolStrategy?.onMaxIterations?.(maxIterations);
|
|
353
|
+
throw new UPPError(
|
|
354
|
+
`Tool execution exceeded maximum iterations (${maxIterations})`,
|
|
355
|
+
"INVALID_REQUEST",
|
|
356
|
+
model.provider.name,
|
|
357
|
+
"llm"
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
const results = await executeTools(
|
|
361
|
+
response.message,
|
|
362
|
+
tools,
|
|
363
|
+
toolStrategy,
|
|
364
|
+
toolExecutions
|
|
365
|
+
);
|
|
366
|
+
allMessages.push(new ToolResultMessage(results));
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
resolveGenerator();
|
|
372
|
+
} catch (error) {
|
|
373
|
+
generatorError = error;
|
|
374
|
+
rejectGenerator(error);
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const turnPromise = (async () => {
|
|
379
|
+
await generatorDone;
|
|
380
|
+
if (generatorError) {
|
|
381
|
+
throw generatorError;
|
|
382
|
+
}
|
|
383
|
+
const data = structure ? structuredData : void 0;
|
|
384
|
+
return createTurn(
|
|
385
|
+
allMessages.slice(history.length),
|
|
386
|
+
toolExecutions,
|
|
387
|
+
aggregateUsage(usages),
|
|
388
|
+
cycles,
|
|
389
|
+
data
|
|
390
|
+
);
|
|
391
|
+
})();
|
|
392
|
+
return createStreamResult(generateStream(), turnPromise, abortController);
|
|
393
|
+
}
|
|
394
|
+
async function executeTools(message, tools, toolStrategy, executions) {
|
|
395
|
+
const toolCalls = message.toolCalls ?? [];
|
|
396
|
+
const results = [];
|
|
397
|
+
const toolMap = new Map(tools.map((t) => [t.name, t]));
|
|
398
|
+
const promises = toolCalls.map(async (call) => {
|
|
399
|
+
const tool = toolMap.get(call.toolName);
|
|
400
|
+
if (!tool) {
|
|
401
|
+
return {
|
|
402
|
+
toolCallId: call.toolCallId,
|
|
403
|
+
result: `Tool '${call.toolName}' not found`,
|
|
404
|
+
isError: true
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const startTime = Date.now();
|
|
408
|
+
await toolStrategy?.onToolCall?.(tool, call.arguments);
|
|
409
|
+
if (toolStrategy?.onBeforeCall) {
|
|
410
|
+
const shouldRun = await toolStrategy.onBeforeCall(tool, call.arguments);
|
|
411
|
+
if (!shouldRun) {
|
|
412
|
+
return {
|
|
413
|
+
toolCallId: call.toolCallId,
|
|
414
|
+
result: "Tool execution skipped",
|
|
415
|
+
isError: true
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
let approved = true;
|
|
420
|
+
if (tool.approval) {
|
|
421
|
+
try {
|
|
422
|
+
approved = await tool.approval(call.arguments);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
throw error;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (!approved) {
|
|
428
|
+
const execution = {
|
|
429
|
+
toolName: tool.name,
|
|
430
|
+
toolCallId: call.toolCallId,
|
|
431
|
+
arguments: call.arguments,
|
|
432
|
+
result: "Tool execution denied",
|
|
433
|
+
isError: true,
|
|
434
|
+
duration: Date.now() - startTime,
|
|
435
|
+
approved: false
|
|
436
|
+
};
|
|
437
|
+
executions.push(execution);
|
|
438
|
+
return {
|
|
439
|
+
toolCallId: call.toolCallId,
|
|
440
|
+
result: "Tool execution denied by approval handler",
|
|
441
|
+
isError: true
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
const result = await tool.run(call.arguments);
|
|
446
|
+
await toolStrategy?.onAfterCall?.(tool, call.arguments, result);
|
|
447
|
+
const execution = {
|
|
448
|
+
toolName: tool.name,
|
|
449
|
+
toolCallId: call.toolCallId,
|
|
450
|
+
arguments: call.arguments,
|
|
451
|
+
result,
|
|
452
|
+
isError: false,
|
|
453
|
+
duration: Date.now() - startTime,
|
|
454
|
+
approved
|
|
455
|
+
};
|
|
456
|
+
executions.push(execution);
|
|
457
|
+
return {
|
|
458
|
+
toolCallId: call.toolCallId,
|
|
459
|
+
result,
|
|
460
|
+
isError: false
|
|
461
|
+
};
|
|
462
|
+
} catch (error) {
|
|
463
|
+
await toolStrategy?.onError?.(tool, call.arguments, error);
|
|
464
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
465
|
+
const execution = {
|
|
466
|
+
toolName: tool.name,
|
|
467
|
+
toolCallId: call.toolCallId,
|
|
468
|
+
arguments: call.arguments,
|
|
469
|
+
result: errorMessage,
|
|
470
|
+
isError: true,
|
|
471
|
+
duration: Date.now() - startTime,
|
|
472
|
+
approved
|
|
473
|
+
};
|
|
474
|
+
executions.push(execution);
|
|
475
|
+
return {
|
|
476
|
+
toolCallId: call.toolCallId,
|
|
477
|
+
result: errorMessage,
|
|
478
|
+
isError: true
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
results.push(...await Promise.all(promises));
|
|
483
|
+
return results;
|
|
484
|
+
}
|
|
485
|
+
function validateMediaCapabilities(messages, capabilities, providerName) {
|
|
486
|
+
for (const msg of messages) {
|
|
487
|
+
if (!isUserMessage(msg)) continue;
|
|
488
|
+
for (const block of msg.content) {
|
|
489
|
+
if (block.type === "image" && !capabilities.imageInput) {
|
|
490
|
+
throw new UPPError(
|
|
491
|
+
`Provider '${providerName}' does not support image input`,
|
|
492
|
+
"INVALID_REQUEST",
|
|
493
|
+
providerName,
|
|
494
|
+
"llm"
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
if (block.type === "video" && !capabilities.videoInput) {
|
|
498
|
+
throw new UPPError(
|
|
499
|
+
`Provider '${providerName}' does not support video input`,
|
|
500
|
+
"INVALID_REQUEST",
|
|
501
|
+
providerName,
|
|
502
|
+
"llm"
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
if (block.type === "audio" && !capabilities.audioInput) {
|
|
506
|
+
throw new UPPError(
|
|
507
|
+
`Provider '${providerName}' does not support audio input`,
|
|
508
|
+
"INVALID_REQUEST",
|
|
509
|
+
providerName,
|
|
510
|
+
"llm"
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// src/core/image.ts
|
|
518
|
+
var Image = class _Image {
|
|
519
|
+
source;
|
|
520
|
+
mimeType;
|
|
521
|
+
width;
|
|
522
|
+
height;
|
|
523
|
+
constructor(source, mimeType, width, height) {
|
|
524
|
+
this.source = source;
|
|
525
|
+
this.mimeType = mimeType;
|
|
526
|
+
this.width = width;
|
|
527
|
+
this.height = height;
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Check if this image has data loaded (false for URL sources)
|
|
531
|
+
*/
|
|
532
|
+
get hasData() {
|
|
533
|
+
return this.source.type !== "url";
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Convert to base64 string (throws if source is URL)
|
|
537
|
+
*/
|
|
538
|
+
toBase64() {
|
|
539
|
+
if (this.source.type === "base64") {
|
|
540
|
+
return this.source.data;
|
|
541
|
+
}
|
|
542
|
+
if (this.source.type === "bytes") {
|
|
543
|
+
return btoa(
|
|
544
|
+
Array.from(this.source.data).map((b) => String.fromCharCode(b)).join("")
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
throw new Error("Cannot convert URL image to base64. Fetch the image first.");
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Convert to data URL (throws if source is URL)
|
|
551
|
+
*/
|
|
552
|
+
toDataUrl() {
|
|
553
|
+
const base64 = this.toBase64();
|
|
554
|
+
return `data:${this.mimeType};base64,${base64}`;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Get raw bytes (throws if source is URL)
|
|
558
|
+
*/
|
|
559
|
+
toBytes() {
|
|
560
|
+
if (this.source.type === "bytes") {
|
|
561
|
+
return this.source.data;
|
|
562
|
+
}
|
|
563
|
+
if (this.source.type === "base64") {
|
|
564
|
+
const binaryString = atob(this.source.data);
|
|
565
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
566
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
567
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
568
|
+
}
|
|
569
|
+
return bytes;
|
|
570
|
+
}
|
|
571
|
+
throw new Error("Cannot get bytes from URL image. Fetch the image first.");
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Get the URL (only for URL sources)
|
|
575
|
+
*/
|
|
576
|
+
toUrl() {
|
|
577
|
+
if (this.source.type === "url") {
|
|
578
|
+
return this.source.url;
|
|
579
|
+
}
|
|
580
|
+
throw new Error("This image does not have a URL source.");
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Convert to ImageBlock for use in messages
|
|
584
|
+
*/
|
|
585
|
+
toBlock() {
|
|
586
|
+
return {
|
|
587
|
+
type: "image",
|
|
588
|
+
source: this.source,
|
|
589
|
+
mimeType: this.mimeType,
|
|
590
|
+
width: this.width,
|
|
591
|
+
height: this.height
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Create from file path (reads file into memory)
|
|
596
|
+
*/
|
|
597
|
+
static async fromPath(path) {
|
|
598
|
+
const file = Bun.file(path);
|
|
599
|
+
const data = await file.arrayBuffer();
|
|
600
|
+
const mimeType = file.type || detectMimeType(path);
|
|
601
|
+
return new _Image(
|
|
602
|
+
{ type: "bytes", data: new Uint8Array(data) },
|
|
603
|
+
mimeType
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* Create from URL reference (does not fetch - providers handle URL conversion)
|
|
608
|
+
*/
|
|
609
|
+
static fromUrl(url, mimeType) {
|
|
610
|
+
const detected = mimeType || detectMimeTypeFromUrl(url);
|
|
611
|
+
return new _Image({ type: "url", url }, detected);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Create from raw bytes
|
|
615
|
+
*/
|
|
616
|
+
static fromBytes(data, mimeType) {
|
|
617
|
+
return new _Image({ type: "bytes", data }, mimeType);
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Create from base64 string
|
|
621
|
+
*/
|
|
622
|
+
static fromBase64(base64, mimeType) {
|
|
623
|
+
return new _Image({ type: "base64", data: base64 }, mimeType);
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Create from an existing ImageBlock
|
|
627
|
+
*/
|
|
628
|
+
static fromBlock(block) {
|
|
629
|
+
return new _Image(
|
|
630
|
+
block.source,
|
|
631
|
+
block.mimeType,
|
|
632
|
+
block.width,
|
|
633
|
+
block.height
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
function detectMimeType(path) {
|
|
638
|
+
const ext = path.split(".").pop()?.toLowerCase();
|
|
639
|
+
switch (ext) {
|
|
640
|
+
case "jpg":
|
|
641
|
+
case "jpeg":
|
|
642
|
+
return "image/jpeg";
|
|
643
|
+
case "png":
|
|
644
|
+
return "image/png";
|
|
645
|
+
case "gif":
|
|
646
|
+
return "image/gif";
|
|
647
|
+
case "webp":
|
|
648
|
+
return "image/webp";
|
|
649
|
+
case "svg":
|
|
650
|
+
return "image/svg+xml";
|
|
651
|
+
case "bmp":
|
|
652
|
+
return "image/bmp";
|
|
653
|
+
case "ico":
|
|
654
|
+
return "image/x-icon";
|
|
655
|
+
default:
|
|
656
|
+
return "application/octet-stream";
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function detectMimeTypeFromUrl(url) {
|
|
660
|
+
try {
|
|
661
|
+
const pathname = new URL(url).pathname;
|
|
662
|
+
return detectMimeType(pathname);
|
|
663
|
+
} catch {
|
|
664
|
+
return "application/octet-stream";
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// src/types/content.ts
|
|
669
|
+
function text(content) {
|
|
670
|
+
return { type: "text", text: content };
|
|
671
|
+
}
|
|
672
|
+
function isTextBlock(block) {
|
|
673
|
+
return block.type === "text";
|
|
674
|
+
}
|
|
675
|
+
function isImageBlock(block) {
|
|
676
|
+
return block.type === "image";
|
|
677
|
+
}
|
|
678
|
+
function isAudioBlock(block) {
|
|
679
|
+
return block.type === "audio";
|
|
680
|
+
}
|
|
681
|
+
function isVideoBlock(block) {
|
|
682
|
+
return block.type === "video";
|
|
683
|
+
}
|
|
684
|
+
function isBinaryBlock(block) {
|
|
685
|
+
return block.type === "binary";
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// src/types/thread.ts
|
|
689
|
+
var Thread = class _Thread {
|
|
690
|
+
/** Unique thread identifier */
|
|
691
|
+
id;
|
|
692
|
+
/** Internal message storage */
|
|
693
|
+
_messages;
|
|
694
|
+
/** Creation timestamp */
|
|
695
|
+
_createdAt;
|
|
696
|
+
/** Last update timestamp */
|
|
697
|
+
_updatedAt;
|
|
698
|
+
/**
|
|
699
|
+
* Create a new thread, optionally with initial messages
|
|
700
|
+
*/
|
|
701
|
+
constructor(messages) {
|
|
702
|
+
this.id = generateId();
|
|
703
|
+
this._messages = messages ? [...messages] : [];
|
|
704
|
+
this._createdAt = /* @__PURE__ */ new Date();
|
|
705
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
706
|
+
}
|
|
707
|
+
/** All messages in the thread (readonly) */
|
|
708
|
+
get messages() {
|
|
709
|
+
return this._messages;
|
|
710
|
+
}
|
|
711
|
+
/** Number of messages */
|
|
712
|
+
get length() {
|
|
713
|
+
return this._messages.length;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Append messages from a turn
|
|
717
|
+
*/
|
|
718
|
+
append(turn) {
|
|
719
|
+
this._messages.push(...turn.messages);
|
|
720
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
721
|
+
return this;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Add raw messages
|
|
725
|
+
*/
|
|
726
|
+
push(...messages) {
|
|
727
|
+
this._messages.push(...messages);
|
|
728
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
729
|
+
return this;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Add a user message
|
|
733
|
+
*/
|
|
734
|
+
user(content) {
|
|
735
|
+
this._messages.push(new UserMessage(content));
|
|
736
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
737
|
+
return this;
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Add an assistant message
|
|
741
|
+
*/
|
|
742
|
+
assistant(content) {
|
|
743
|
+
this._messages.push(new AssistantMessage(content));
|
|
744
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Get messages by type
|
|
749
|
+
*/
|
|
750
|
+
filter(type) {
|
|
751
|
+
return this._messages.filter((m) => m.type === type);
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Get the last N messages
|
|
755
|
+
*/
|
|
756
|
+
tail(count) {
|
|
757
|
+
return this._messages.slice(-count);
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Create a new thread with a subset of messages
|
|
761
|
+
*/
|
|
762
|
+
slice(start, end) {
|
|
763
|
+
return new _Thread(this._messages.slice(start, end));
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Clear all messages
|
|
767
|
+
*/
|
|
768
|
+
clear() {
|
|
769
|
+
this._messages = [];
|
|
770
|
+
this._updatedAt = /* @__PURE__ */ new Date();
|
|
771
|
+
return this;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Convert to plain message array
|
|
775
|
+
*/
|
|
776
|
+
toMessages() {
|
|
777
|
+
return [...this._messages];
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* Serialize to JSON
|
|
781
|
+
*/
|
|
782
|
+
toJSON() {
|
|
783
|
+
return {
|
|
784
|
+
id: this.id,
|
|
785
|
+
messages: this._messages.map((m) => this.messageToJSON(m)),
|
|
786
|
+
createdAt: this._createdAt.toISOString(),
|
|
787
|
+
updatedAt: this._updatedAt.toISOString()
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Deserialize from JSON
|
|
792
|
+
*/
|
|
793
|
+
static fromJSON(json) {
|
|
794
|
+
const messages = json.messages.map((m) => _Thread.messageFromJSON(m));
|
|
795
|
+
const thread = new _Thread(messages);
|
|
796
|
+
thread.id = json.id;
|
|
797
|
+
thread._createdAt = new Date(json.createdAt);
|
|
798
|
+
thread._updatedAt = new Date(json.updatedAt);
|
|
799
|
+
return thread;
|
|
800
|
+
}
|
|
801
|
+
/**
|
|
802
|
+
* Iterate over messages
|
|
803
|
+
*/
|
|
804
|
+
[Symbol.iterator]() {
|
|
805
|
+
return this._messages[Symbol.iterator]();
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Convert a message to JSON
|
|
809
|
+
*/
|
|
810
|
+
messageToJSON(m) {
|
|
811
|
+
const base = {
|
|
812
|
+
id: m.id,
|
|
813
|
+
type: m.type,
|
|
814
|
+
content: [],
|
|
815
|
+
metadata: m.metadata,
|
|
816
|
+
timestamp: m.timestamp.toISOString()
|
|
817
|
+
};
|
|
818
|
+
if (m instanceof UserMessage) {
|
|
819
|
+
base.content = m.content;
|
|
820
|
+
} else if (m instanceof AssistantMessage) {
|
|
821
|
+
base.content = m.content;
|
|
822
|
+
base.toolCalls = m.toolCalls;
|
|
823
|
+
} else if (m instanceof ToolResultMessage) {
|
|
824
|
+
base.results = m.results;
|
|
825
|
+
}
|
|
826
|
+
return base;
|
|
827
|
+
}
|
|
828
|
+
/**
|
|
829
|
+
* Reconstruct a message from JSON
|
|
830
|
+
*/
|
|
831
|
+
static messageFromJSON(json) {
|
|
832
|
+
const options = {
|
|
833
|
+
id: json.id,
|
|
834
|
+
metadata: json.metadata
|
|
835
|
+
};
|
|
836
|
+
switch (json.type) {
|
|
837
|
+
case "user":
|
|
838
|
+
return new UserMessage(json.content, options);
|
|
839
|
+
case "assistant":
|
|
840
|
+
return new AssistantMessage(
|
|
841
|
+
json.content,
|
|
842
|
+
json.toolCalls,
|
|
843
|
+
options
|
|
844
|
+
);
|
|
845
|
+
case "tool_result":
|
|
846
|
+
return new ToolResultMessage(json.results ?? [], options);
|
|
847
|
+
default:
|
|
848
|
+
throw new Error(`Unknown message type: ${json.type}`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// src/index.ts
|
|
854
|
+
var ai = {
|
|
855
|
+
llm
|
|
856
|
+
// embedding, // Coming soon
|
|
857
|
+
// image, // Coming soon
|
|
858
|
+
};
|
|
859
|
+
export {
|
|
860
|
+
AssistantMessage,
|
|
861
|
+
DynamicKey,
|
|
862
|
+
ExponentialBackoff,
|
|
863
|
+
Image,
|
|
864
|
+
LinearBackoff,
|
|
865
|
+
Message,
|
|
866
|
+
NoRetry,
|
|
867
|
+
RetryAfterStrategy,
|
|
868
|
+
RoundRobinKeys,
|
|
869
|
+
Thread,
|
|
870
|
+
TokenBucket,
|
|
871
|
+
ToolResultMessage,
|
|
872
|
+
UPPError,
|
|
873
|
+
UserMessage,
|
|
874
|
+
WeightedKeys,
|
|
875
|
+
aggregateUsage,
|
|
876
|
+
ai,
|
|
877
|
+
contentBlockStart,
|
|
878
|
+
contentBlockStop,
|
|
879
|
+
createProvider,
|
|
880
|
+
createStreamResult,
|
|
881
|
+
createTurn,
|
|
882
|
+
emptyUsage,
|
|
883
|
+
isAssistantMessage,
|
|
884
|
+
isAudioBlock,
|
|
885
|
+
isBinaryBlock,
|
|
886
|
+
isImageBlock,
|
|
887
|
+
isTextBlock,
|
|
888
|
+
isToolResultMessage,
|
|
889
|
+
isUserMessage,
|
|
890
|
+
isVideoBlock,
|
|
891
|
+
llm,
|
|
892
|
+
messageStart,
|
|
893
|
+
messageStop,
|
|
894
|
+
text,
|
|
895
|
+
textDelta,
|
|
896
|
+
toolCallDelta
|
|
897
|
+
};
|
|
898
|
+
//# sourceMappingURL=index.js.map
|