@ai-sdk/langchain 2.0.241 → 2.0.243
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/CHANGELOG.md +15 -0
- package/dist/index.js +39 -59
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +39 -59
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/utils.ts +50 -124
package/src/utils.ts
CHANGED
|
@@ -122,25 +122,46 @@ function getDefaultFilename(
|
|
|
122
122
|
return `${prefix}.${ext}`;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
detail?: 'auto' | 'low' | 'high';
|
|
134
|
-
};
|
|
135
|
-
};
|
|
125
|
+
function convertImageToContentBlock(
|
|
126
|
+
data: string | Uint8Array | URL | ArrayBuffer,
|
|
127
|
+
mediaType: string = 'image/png',
|
|
128
|
+
): ContentBlock.Multimodal.Image {
|
|
129
|
+
if (data instanceof URL) {
|
|
130
|
+
if (data.protocol !== 'data:') {
|
|
131
|
+
return { type: 'image', url: data.toString() };
|
|
132
|
+
}
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
134
|
+
data = data.toString();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (typeof data === 'string') {
|
|
138
|
+
if (data.startsWith('http://') || data.startsWith('https://')) {
|
|
139
|
+
return { type: 'image', url: data };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (data.startsWith('data:')) {
|
|
143
|
+
const matches = data.match(/^data:([^;]+);base64,(.+)$/);
|
|
144
|
+
if (matches) {
|
|
145
|
+
return {
|
|
146
|
+
type: 'image',
|
|
147
|
+
data: matches[2],
|
|
148
|
+
mimeType: matches[1],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { type: 'image', url: data };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return { type: 'image', data, mimeType: mediaType };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const bytes = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
|
|
159
|
+
return {
|
|
160
|
+
type: 'image',
|
|
161
|
+
data: btoa(String.fromCharCode(...bytes)),
|
|
162
|
+
mimeType: mediaType,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
144
165
|
|
|
145
166
|
/**
|
|
146
167
|
* Converts UserContent to LangChain HumanMessage
|
|
@@ -152,7 +173,7 @@ export function convertUserContent(content: UserContent): HumanMessage {
|
|
|
152
173
|
return new HumanMessage({ content });
|
|
153
174
|
}
|
|
154
175
|
|
|
155
|
-
const contentBlocks:
|
|
176
|
+
const contentBlocks: ContentBlock.Standard[] = [];
|
|
156
177
|
|
|
157
178
|
for (const part of content) {
|
|
158
179
|
if (part.type === 'text') {
|
|
@@ -164,64 +185,15 @@ export function convertUserContent(content: UserContent): HumanMessage {
|
|
|
164
185
|
mediaType?: string;
|
|
165
186
|
};
|
|
166
187
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
*/
|
|
171
|
-
if (imagePart.image instanceof URL) {
|
|
172
|
-
contentBlocks.push({
|
|
173
|
-
type: 'image_url',
|
|
174
|
-
image_url: { url: imagePart.image.toString() },
|
|
175
|
-
});
|
|
176
|
-
} else if (typeof imagePart.image === 'string') {
|
|
177
|
-
/**
|
|
178
|
-
* Handle string (could be URL or base64)
|
|
179
|
-
*/
|
|
180
|
-
/**
|
|
181
|
-
* Check if it's a URL (including data: URLs)
|
|
182
|
-
*/
|
|
183
|
-
if (
|
|
184
|
-
imagePart.image.startsWith('http://') ||
|
|
185
|
-
imagePart.image.startsWith('https://') ||
|
|
186
|
-
imagePart.image.startsWith('data:')
|
|
187
|
-
) {
|
|
188
|
-
/**
|
|
189
|
-
* OpenAI accepts both http URLs and data URLs directly
|
|
190
|
-
*/
|
|
191
|
-
contentBlocks.push({
|
|
192
|
-
type: 'image_url',
|
|
193
|
-
image_url: { url: imagePart.image },
|
|
194
|
-
});
|
|
195
|
-
} else {
|
|
196
|
-
/**
|
|
197
|
-
* Assume base64 encoded data - wrap in data URL
|
|
198
|
-
*/
|
|
199
|
-
const mimeType = imagePart.mediaType || 'image/png';
|
|
200
|
-
contentBlocks.push({
|
|
201
|
-
type: 'image_url',
|
|
202
|
-
image_url: { url: `data:${mimeType};base64,${imagePart.image}` },
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
} else if (
|
|
206
|
-
/**
|
|
207
|
-
* Handle Uint8Array or ArrayBuffer (binary data)
|
|
208
|
-
*/
|
|
188
|
+
if (
|
|
189
|
+
imagePart.image instanceof URL ||
|
|
190
|
+
typeof imagePart.image === 'string' ||
|
|
209
191
|
imagePart.image instanceof Uint8Array ||
|
|
210
192
|
imagePart.image instanceof ArrayBuffer
|
|
211
193
|
) {
|
|
212
|
-
|
|
213
|
-
imagePart.image
|
|
214
|
-
|
|
215
|
-
: imagePart.image;
|
|
216
|
-
/**
|
|
217
|
-
* Convert to base64 data URL
|
|
218
|
-
*/
|
|
219
|
-
const base64 = btoa(String.fromCharCode(...bytes));
|
|
220
|
-
const mimeType = imagePart.mediaType || 'image/png';
|
|
221
|
-
contentBlocks.push({
|
|
222
|
-
type: 'image_url',
|
|
223
|
-
image_url: { url: `data:${mimeType};base64,${base64}` },
|
|
224
|
-
});
|
|
194
|
+
contentBlocks.push(
|
|
195
|
+
convertImageToContentBlock(imagePart.image, imagePart.mediaType),
|
|
196
|
+
);
|
|
225
197
|
}
|
|
226
198
|
} else if (part.type === 'file') {
|
|
227
199
|
const filePart = part as {
|
|
@@ -231,58 +203,12 @@ export function convertUserContent(content: UserContent): HumanMessage {
|
|
|
231
203
|
filename?: string;
|
|
232
204
|
};
|
|
233
205
|
|
|
234
|
-
/**
|
|
235
|
-
* Check if this is an image file - if so, use OpenAI's image_url format
|
|
236
|
-
*/
|
|
237
206
|
const isImage = filePart.mediaType?.startsWith('image/');
|
|
238
207
|
|
|
239
208
|
if (isImage) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if (filePart.data instanceof URL) {
|
|
244
|
-
contentBlocks.push({
|
|
245
|
-
type: 'image_url',
|
|
246
|
-
image_url: { url: filePart.data.toString() },
|
|
247
|
-
});
|
|
248
|
-
} else if (typeof filePart.data === 'string') {
|
|
249
|
-
/**
|
|
250
|
-
* URLs (including data URLs) can be passed directly
|
|
251
|
-
*/
|
|
252
|
-
if (
|
|
253
|
-
filePart.data.startsWith('http://') ||
|
|
254
|
-
filePart.data.startsWith('https://') ||
|
|
255
|
-
filePart.data.startsWith('data:')
|
|
256
|
-
) {
|
|
257
|
-
contentBlocks.push({
|
|
258
|
-
type: 'image_url',
|
|
259
|
-
image_url: { url: filePart.data },
|
|
260
|
-
});
|
|
261
|
-
} else {
|
|
262
|
-
/**
|
|
263
|
-
* Assume base64 - wrap in data URL
|
|
264
|
-
*/
|
|
265
|
-
contentBlocks.push({
|
|
266
|
-
type: 'image_url',
|
|
267
|
-
image_url: {
|
|
268
|
-
url: `data:${filePart.mediaType};base64,${filePart.data}`,
|
|
269
|
-
},
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
} else if (
|
|
273
|
-
filePart.data instanceof Uint8Array ||
|
|
274
|
-
filePart.data instanceof ArrayBuffer
|
|
275
|
-
) {
|
|
276
|
-
const bytes =
|
|
277
|
-
filePart.data instanceof ArrayBuffer
|
|
278
|
-
? new Uint8Array(filePart.data)
|
|
279
|
-
: filePart.data;
|
|
280
|
-
const base64 = btoa(String.fromCharCode(...bytes));
|
|
281
|
-
contentBlocks.push({
|
|
282
|
-
type: 'image_url',
|
|
283
|
-
image_url: { url: `data:${filePart.mediaType};base64,${base64}` },
|
|
284
|
-
});
|
|
285
|
-
}
|
|
209
|
+
contentBlocks.push(
|
|
210
|
+
convertImageToContentBlock(filePart.data, filePart.mediaType),
|
|
211
|
+
);
|
|
286
212
|
} else {
|
|
287
213
|
// Handle non-image files using LangChain's ContentBlock format
|
|
288
214
|
const filename =
|
|
@@ -362,7 +288,7 @@ export function convertUserContent(content: UserContent): HumanMessage {
|
|
|
362
288
|
});
|
|
363
289
|
}
|
|
364
290
|
|
|
365
|
-
return new HumanMessage({
|
|
291
|
+
return new HumanMessage({ contentBlocks });
|
|
366
292
|
}
|
|
367
293
|
|
|
368
294
|
/**
|