@stacksjs/ai 0.70.87 → 0.70.90
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/agents/claude/index.js +197 -0
- package/dist/agents/index.js +1 -0
- package/dist/buddy.js +393 -0
- package/dist/drivers/anthropic/index.js +276 -0
- package/dist/drivers/claude-agent-sdk/index.js +198 -0
- package/dist/drivers/index.js +4 -0
- package/dist/drivers/ollama/index.js +332 -0
- package/dist/drivers/openai/index.js +351 -0
- package/dist/image.js +375 -0
- package/dist/index.js +16 -175
- package/dist/mcp.js +361 -0
- package/dist/personalization.js +244 -0
- package/dist/search.js +316 -0
- package/dist/text.js +51 -0
- package/dist/types.js +0 -0
- package/dist/utils/client-bedrock-runtime.js +17 -0
- package/dist/utils/client-bedrock.js +20 -0
- package/dist/utils/model-access.js +21 -0
- package/dist/utils/retry.js +39 -0
- package/dist/utils/tokens.js +59 -0
- package/dist/utils/usage.js +27 -0
- package/dist/utils/vision.js +54 -0
- package/package.json +1 -1
package/dist/image.js
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
export async function generateImage(prompt, options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
provider = "openai",
|
|
4
|
+
model = "dall-e-3",
|
|
5
|
+
size = "1024x1024",
|
|
6
|
+
quality = "standard",
|
|
7
|
+
n = 1,
|
|
8
|
+
responseFormat = "url",
|
|
9
|
+
style = "vivid"
|
|
10
|
+
} = options;
|
|
11
|
+
if (provider === "openai")
|
|
12
|
+
return generateImageOpenAI(prompt, { model, size, quality, n, responseFormat, style });
|
|
13
|
+
throw Error(`Image generation not supported for provider: ${provider}. Use 'openai'.`);
|
|
14
|
+
}
|
|
15
|
+
async function generateImageOpenAI(prompt, options) {
|
|
16
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
17
|
+
if (!apiKey)
|
|
18
|
+
throw Error("OPENAI_API_KEY environment variable is required for image generation.");
|
|
19
|
+
const response = await fetch("https://api.openai.com/v1/images/generations", {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: {
|
|
22
|
+
"Content-Type": "application/json",
|
|
23
|
+
Authorization: `Bearer ${apiKey}`
|
|
24
|
+
},
|
|
25
|
+
body: JSON.stringify({
|
|
26
|
+
model: options.model,
|
|
27
|
+
prompt,
|
|
28
|
+
size: options.size,
|
|
29
|
+
quality: options.quality,
|
|
30
|
+
n: options.n,
|
|
31
|
+
response_format: options.responseFormat,
|
|
32
|
+
style: options.style
|
|
33
|
+
})
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
const error = await response.text();
|
|
37
|
+
throw Error(`OpenAI Image API error: ${error}`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
images: (await response.json()).data.map((img) => ({
|
|
41
|
+
url: img.url,
|
|
42
|
+
b64_json: img.b64_json,
|
|
43
|
+
revisedPrompt: img.revised_prompt
|
|
44
|
+
})),
|
|
45
|
+
provider: "openai",
|
|
46
|
+
model: options.model
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export async function editImage(image, prompt, options = {}) {
|
|
50
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
51
|
+
if (!apiKey)
|
|
52
|
+
throw Error("OPENAI_API_KEY environment variable is required for image editing.");
|
|
53
|
+
const formData = new FormData;
|
|
54
|
+
formData.append("image", image);
|
|
55
|
+
formData.append("prompt", prompt);
|
|
56
|
+
formData.append("model", options.model || "dall-e-2");
|
|
57
|
+
if (options.mask)
|
|
58
|
+
formData.append("mask", options.mask);
|
|
59
|
+
if (options.n)
|
|
60
|
+
formData.append("n", String(options.n));
|
|
61
|
+
if (options.size)
|
|
62
|
+
formData.append("size", options.size);
|
|
63
|
+
if (options.responseFormat)
|
|
64
|
+
formData.append("response_format", options.responseFormat);
|
|
65
|
+
const response = await fetch("https://api.openai.com/v1/images/edits", {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: {
|
|
68
|
+
Authorization: `Bearer ${apiKey}`
|
|
69
|
+
},
|
|
70
|
+
body: formData
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const error = await response.text();
|
|
74
|
+
throw Error(`OpenAI Image Edit API error: ${error}`);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
images: (await response.json()).data.map((img) => ({
|
|
78
|
+
url: img.url,
|
|
79
|
+
b64_json: img.b64_json
|
|
80
|
+
})),
|
|
81
|
+
provider: "openai",
|
|
82
|
+
model: options.model || "dall-e-2"
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export async function createImageVariation(image, options = {}) {
|
|
86
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
87
|
+
if (!apiKey)
|
|
88
|
+
throw Error("OPENAI_API_KEY environment variable is required for image variations.");
|
|
89
|
+
const formData = new FormData;
|
|
90
|
+
formData.append("image", image);
|
|
91
|
+
formData.append("model", options.model || "dall-e-2");
|
|
92
|
+
if (options.n)
|
|
93
|
+
formData.append("n", String(options.n));
|
|
94
|
+
if (options.size)
|
|
95
|
+
formData.append("size", options.size);
|
|
96
|
+
if (options.responseFormat)
|
|
97
|
+
formData.append("response_format", options.responseFormat);
|
|
98
|
+
const response = await fetch("https://api.openai.com/v1/images/variations", {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
Authorization: `Bearer ${apiKey}`
|
|
102
|
+
},
|
|
103
|
+
body: formData
|
|
104
|
+
});
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
const error = await response.text();
|
|
107
|
+
throw Error(`OpenAI Image Variations API error: ${error}`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
images: (await response.json()).data.map((img) => ({
|
|
111
|
+
url: img.url,
|
|
112
|
+
b64_json: img.b64_json
|
|
113
|
+
})),
|
|
114
|
+
provider: "openai",
|
|
115
|
+
model: options.model || "dall-e-2"
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export async function analyzeImage(imageInput, prompt, options = {}) {
|
|
119
|
+
const { provider = "anthropic" } = options;
|
|
120
|
+
switch (provider) {
|
|
121
|
+
case "anthropic":
|
|
122
|
+
return analyzeImageAnthropic(imageInput, prompt, options);
|
|
123
|
+
case "openai":
|
|
124
|
+
return analyzeImageOpenAI(imageInput, prompt, options);
|
|
125
|
+
case "ollama":
|
|
126
|
+
return analyzeImageOllama(imageInput, prompt, options);
|
|
127
|
+
default:
|
|
128
|
+
throw Error(`Vision not supported for provider: ${provider}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function resolveImageToBase64(input) {
|
|
132
|
+
if (input.type === "base64")
|
|
133
|
+
return { data: input.data, mediaType: input.mediaType };
|
|
134
|
+
if (input.type === "url") {
|
|
135
|
+
const response = await fetch(input.url);
|
|
136
|
+
if (!response.ok)
|
|
137
|
+
throw Error(`Failed to fetch image from URL: ${input.url}`);
|
|
138
|
+
const buffer = await response.arrayBuffer(), base64 = Buffer.from(buffer).toString("base64"), contentType = response.headers.get("content-type") || "image/png";
|
|
139
|
+
return { data: base64, mediaType: contentType };
|
|
140
|
+
}
|
|
141
|
+
if (input.type === "file") {
|
|
142
|
+
const { readFile } = await import("node:fs/promises"), base64 = (await readFile(input.path)).toString("base64"), ext = input.path.split(".").pop()?.toLowerCase() || "png";
|
|
143
|
+
return { data: base64, mediaType: {
|
|
144
|
+
png: "image/png",
|
|
145
|
+
jpg: "image/jpeg",
|
|
146
|
+
jpeg: "image/jpeg",
|
|
147
|
+
gif: "image/gif",
|
|
148
|
+
webp: "image/webp"
|
|
149
|
+
}[ext] || "image/png" };
|
|
150
|
+
}
|
|
151
|
+
throw Error("Invalid image input type");
|
|
152
|
+
}
|
|
153
|
+
async function analyzeImageAnthropic(imageInput, prompt, options) {
|
|
154
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
155
|
+
if (!apiKey)
|
|
156
|
+
throw Error("ANTHROPIC_API_KEY environment variable is required for Claude vision.");
|
|
157
|
+
const { data, mediaType } = await resolveImageToBase64(imageInput), model = options.model || "claude-sonnet-4-20250514", maxTokens = options.maxTokens || 4096, messages = [{
|
|
158
|
+
role: "user",
|
|
159
|
+
content: [
|
|
160
|
+
{
|
|
161
|
+
type: "image",
|
|
162
|
+
source: { type: "base64", media_type: mediaType, data }
|
|
163
|
+
},
|
|
164
|
+
{ type: "text", text: prompt }
|
|
165
|
+
]
|
|
166
|
+
}], response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
167
|
+
method: "POST",
|
|
168
|
+
headers: {
|
|
169
|
+
"Content-Type": "application/json",
|
|
170
|
+
"x-api-key": apiKey,
|
|
171
|
+
"anthropic-version": "2023-06-01"
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
model,
|
|
175
|
+
max_tokens: maxTokens,
|
|
176
|
+
temperature: options.temperature,
|
|
177
|
+
messages
|
|
178
|
+
})
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok) {
|
|
181
|
+
const error = await response.text();
|
|
182
|
+
throw Error(`Claude Vision API error: ${error}`);
|
|
183
|
+
}
|
|
184
|
+
const responseData = await response.json();
|
|
185
|
+
return {
|
|
186
|
+
content: responseData.content[0].text,
|
|
187
|
+
model: responseData.model,
|
|
188
|
+
provider: "anthropic",
|
|
189
|
+
usage: {
|
|
190
|
+
promptTokens: responseData.usage?.input_tokens || 0,
|
|
191
|
+
completionTokens: responseData.usage?.output_tokens || 0,
|
|
192
|
+
totalTokens: (responseData.usage?.input_tokens || 0) + (responseData.usage?.output_tokens || 0)
|
|
193
|
+
},
|
|
194
|
+
finishReason: responseData.stop_reason
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function analyzeImageOpenAI(imageInput, prompt, options) {
|
|
198
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
199
|
+
if (!apiKey)
|
|
200
|
+
throw Error("OPENAI_API_KEY environment variable is required for GPT-4 vision.");
|
|
201
|
+
const model = options.model || "gpt-4o", maxTokens = options.maxTokens || 4096, detail = options.detail || "auto";
|
|
202
|
+
let imageContent;
|
|
203
|
+
if (imageInput.type === "url")
|
|
204
|
+
imageContent = { type: "image_url", image_url: { url: imageInput.url, detail } };
|
|
205
|
+
else {
|
|
206
|
+
const { data, mediaType } = await resolveImageToBase64(imageInput);
|
|
207
|
+
imageContent = {
|
|
208
|
+
type: "image_url",
|
|
209
|
+
image_url: { url: `data:${mediaType};base64,${data}`, detail }
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: {
|
|
215
|
+
"Content-Type": "application/json",
|
|
216
|
+
Authorization: `Bearer ${apiKey}`
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
model,
|
|
220
|
+
max_tokens: maxTokens,
|
|
221
|
+
temperature: options.temperature,
|
|
222
|
+
messages: [{
|
|
223
|
+
role: "user",
|
|
224
|
+
content: [
|
|
225
|
+
imageContent,
|
|
226
|
+
{ type: "text", text: prompt }
|
|
227
|
+
]
|
|
228
|
+
}]
|
|
229
|
+
})
|
|
230
|
+
});
|
|
231
|
+
if (!response.ok) {
|
|
232
|
+
const error = await response.text();
|
|
233
|
+
throw Error(`OpenAI Vision API error: ${error}`);
|
|
234
|
+
}
|
|
235
|
+
const responseData = await response.json();
|
|
236
|
+
return {
|
|
237
|
+
content: responseData.choices[0].message.content,
|
|
238
|
+
model: responseData.model,
|
|
239
|
+
provider: "openai",
|
|
240
|
+
usage: {
|
|
241
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
242
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
243
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
244
|
+
},
|
|
245
|
+
finishReason: responseData.choices[0].finish_reason
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
async function analyzeImageOllama(imageInput, prompt, options) {
|
|
249
|
+
const host = process.env.OLLAMA_HOST || "http://localhost:11434", model = options.model || "llava", { data } = await resolveImageToBase64(imageInput), response = await fetch(`${host}/api/generate`, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: { "Content-Type": "application/json" },
|
|
252
|
+
body: JSON.stringify({
|
|
253
|
+
model,
|
|
254
|
+
prompt,
|
|
255
|
+
images: [data],
|
|
256
|
+
stream: !1
|
|
257
|
+
})
|
|
258
|
+
});
|
|
259
|
+
if (!response.ok) {
|
|
260
|
+
const error = await response.text();
|
|
261
|
+
throw Error(`Ollama Vision API error: ${error}`);
|
|
262
|
+
}
|
|
263
|
+
const responseData = await response.json();
|
|
264
|
+
return {
|
|
265
|
+
content: responseData.response,
|
|
266
|
+
model: responseData.model,
|
|
267
|
+
provider: "ollama",
|
|
268
|
+
usage: {
|
|
269
|
+
promptTokens: responseData.prompt_eval_count || 0,
|
|
270
|
+
completionTokens: responseData.eval_count || 0,
|
|
271
|
+
totalTokens: (responseData.prompt_eval_count || 0) + (responseData.eval_count || 0)
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
export async function analyzeImages(images, prompt, options = {}) {
|
|
276
|
+
const { provider = "anthropic" } = options;
|
|
277
|
+
if (provider === "anthropic") {
|
|
278
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
279
|
+
if (!apiKey)
|
|
280
|
+
throw Error("ANTHROPIC_API_KEY required for multi-image analysis.");
|
|
281
|
+
const model = options.model || "claude-sonnet-4-20250514", contentBlocks = [];
|
|
282
|
+
for (const img of images) {
|
|
283
|
+
const { data, mediaType } = await resolveImageToBase64(img);
|
|
284
|
+
contentBlocks.push({
|
|
285
|
+
type: "image",
|
|
286
|
+
source: { type: "base64", media_type: mediaType, data }
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
contentBlocks.push({ type: "text", text: prompt });
|
|
290
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
291
|
+
method: "POST",
|
|
292
|
+
headers: {
|
|
293
|
+
"Content-Type": "application/json",
|
|
294
|
+
"x-api-key": apiKey,
|
|
295
|
+
"anthropic-version": "2023-06-01"
|
|
296
|
+
},
|
|
297
|
+
body: JSON.stringify({
|
|
298
|
+
model,
|
|
299
|
+
max_tokens: options.maxTokens || 4096,
|
|
300
|
+
temperature: options.temperature,
|
|
301
|
+
messages: [{ role: "user", content: contentBlocks }]
|
|
302
|
+
})
|
|
303
|
+
});
|
|
304
|
+
if (!response.ok) {
|
|
305
|
+
const error = await response.text();
|
|
306
|
+
throw Error(`Claude Vision API error: ${error}`);
|
|
307
|
+
}
|
|
308
|
+
const responseData = await response.json();
|
|
309
|
+
return {
|
|
310
|
+
content: responseData.content[0].text,
|
|
311
|
+
model: responseData.model,
|
|
312
|
+
provider: "anthropic",
|
|
313
|
+
usage: {
|
|
314
|
+
promptTokens: responseData.usage?.input_tokens || 0,
|
|
315
|
+
completionTokens: responseData.usage?.output_tokens || 0,
|
|
316
|
+
totalTokens: (responseData.usage?.input_tokens || 0) + (responseData.usage?.output_tokens || 0)
|
|
317
|
+
},
|
|
318
|
+
finishReason: responseData.stop_reason
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
if (provider === "openai") {
|
|
322
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
323
|
+
if (!apiKey)
|
|
324
|
+
throw Error("OPENAI_API_KEY required for multi-image analysis.");
|
|
325
|
+
const model = options.model || "gpt-4o", detail = options.detail || "auto", contentBlocks = [];
|
|
326
|
+
for (const img of images)
|
|
327
|
+
if (img.type === "url")
|
|
328
|
+
contentBlocks.push({ type: "image_url", image_url: { url: img.url, detail } });
|
|
329
|
+
else {
|
|
330
|
+
const { data, mediaType } = await resolveImageToBase64(img);
|
|
331
|
+
contentBlocks.push({
|
|
332
|
+
type: "image_url",
|
|
333
|
+
image_url: { url: `data:${mediaType};base64,${data}`, detail }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
contentBlocks.push({ type: "text", text: prompt });
|
|
337
|
+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
|
|
338
|
+
method: "POST",
|
|
339
|
+
headers: {
|
|
340
|
+
"Content-Type": "application/json",
|
|
341
|
+
Authorization: `Bearer ${apiKey}`
|
|
342
|
+
},
|
|
343
|
+
body: JSON.stringify({
|
|
344
|
+
model,
|
|
345
|
+
max_tokens: options.maxTokens || 4096,
|
|
346
|
+
temperature: options.temperature,
|
|
347
|
+
messages: [{ role: "user", content: contentBlocks }]
|
|
348
|
+
})
|
|
349
|
+
});
|
|
350
|
+
if (!response.ok) {
|
|
351
|
+
const error = await response.text();
|
|
352
|
+
throw Error(`OpenAI Vision API error: ${error}`);
|
|
353
|
+
}
|
|
354
|
+
const responseData = await response.json();
|
|
355
|
+
return {
|
|
356
|
+
content: responseData.choices[0].message.content,
|
|
357
|
+
model: responseData.model,
|
|
358
|
+
provider: "openai",
|
|
359
|
+
usage: {
|
|
360
|
+
promptTokens: responseData.usage?.prompt_tokens || 0,
|
|
361
|
+
completionTokens: responseData.usage?.completion_tokens || 0,
|
|
362
|
+
totalTokens: responseData.usage?.total_tokens || 0
|
|
363
|
+
},
|
|
364
|
+
finishReason: responseData.choices[0].finish_reason
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
throw Error(`Multi-image analysis not supported for provider: ${provider}`);
|
|
368
|
+
}
|
|
369
|
+
export const image = {
|
|
370
|
+
generate: generateImage,
|
|
371
|
+
edit: editImage,
|
|
372
|
+
variation: createImageVariation,
|
|
373
|
+
analyze: analyzeImage,
|
|
374
|
+
analyzeMultiple: analyzeImages
|
|
375
|
+
};
|