@langchain/anthropic 0.3.19 → 0.3.20

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/types.cjs CHANGED
@@ -1,2 +1,49 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAnthropicImageBlockParam = void 0;
4
+ function isAnthropicImageBlockParam(block) {
5
+ if (block == null) {
6
+ return false;
7
+ }
8
+ if (typeof block !== "object") {
9
+ return false;
10
+ }
11
+ if (!("type" in block) || block.type !== "image") {
12
+ return false;
13
+ }
14
+ if (!("source" in block) || typeof block.source !== "object") {
15
+ return false;
16
+ }
17
+ if (block.source == null) {
18
+ return false;
19
+ }
20
+ if (!("type" in block.source)) {
21
+ return false;
22
+ }
23
+ if (block.source.type === "base64") {
24
+ if (!("media_type" in block.source)) {
25
+ return false;
26
+ }
27
+ if (typeof block.source.media_type !== "string") {
28
+ return false;
29
+ }
30
+ if (!("data" in block.source)) {
31
+ return false;
32
+ }
33
+ if (typeof block.source.data !== "string") {
34
+ return false;
35
+ }
36
+ return true;
37
+ }
38
+ if (block.source.type === "url") {
39
+ if (!("url" in block.source)) {
40
+ return false;
41
+ }
42
+ if (typeof block.source.url !== "string") {
43
+ return false;
44
+ }
45
+ return true;
46
+ }
47
+ return false;
48
+ }
49
+ exports.isAnthropicImageBlockParam = isAnthropicImageBlockParam;
package/dist/types.d.ts CHANGED
@@ -25,3 +25,4 @@ export type AnthropicToolResultBlockParam = Anthropic.Messages.ToolResultBlockPa
25
25
  export type AnthropicDocumentBlockParam = Anthropic.Messages.DocumentBlockParam;
26
26
  export type AnthropicThinkingBlockParam = Anthropic.Messages.ThinkingBlockParam;
27
27
  export type AnthropicRedactedThinkingBlockParam = Anthropic.Messages.RedactedThinkingBlockParam;
28
+ export declare function isAnthropicImageBlockParam(block: unknown): block is AnthropicImageBlockParam;
package/dist/types.js CHANGED
@@ -1 +1,45 @@
1
- export {};
1
+ export function isAnthropicImageBlockParam(block) {
2
+ if (block == null) {
3
+ return false;
4
+ }
5
+ if (typeof block !== "object") {
6
+ return false;
7
+ }
8
+ if (!("type" in block) || block.type !== "image") {
9
+ return false;
10
+ }
11
+ if (!("source" in block) || typeof block.source !== "object") {
12
+ return false;
13
+ }
14
+ if (block.source == null) {
15
+ return false;
16
+ }
17
+ if (!("type" in block.source)) {
18
+ return false;
19
+ }
20
+ if (block.source.type === "base64") {
21
+ if (!("media_type" in block.source)) {
22
+ return false;
23
+ }
24
+ if (typeof block.source.media_type !== "string") {
25
+ return false;
26
+ }
27
+ if (!("data" in block.source)) {
28
+ return false;
29
+ }
30
+ if (typeof block.source.data !== "string") {
31
+ return false;
32
+ }
33
+ return true;
34
+ }
35
+ if (block.source.type === "url") {
36
+ if (!("url" in block.source)) {
37
+ return false;
38
+ }
39
+ if (typeof block.source.url !== "string") {
40
+ return false;
41
+ }
42
+ return true;
43
+ }
44
+ return false;
45
+ }
@@ -5,21 +5,38 @@ exports._convertMessagesToAnthropicPayload = exports._convertLangChainToolCallTo
5
5
  * This util file contains functions for converting LangChain messages to Anthropic messages.
6
6
  */
7
7
  const messages_1 = require("@langchain/core/messages");
8
+ const types_js_1 = require("../types.cjs");
8
9
  function _formatImage(imageUrl) {
9
- const regex = /^data:(image\/.+);base64,(.+)$/;
10
- const match = imageUrl.match(regex);
11
- if (match === null) {
10
+ const parsed = (0, messages_1.parseBase64DataUrl)({ dataUrl: imageUrl });
11
+ if (parsed) {
12
+ return {
13
+ type: "base64",
14
+ media_type: parsed.mime_type,
15
+ data: parsed.data,
16
+ };
17
+ }
18
+ let parsedUrl;
19
+ try {
20
+ parsedUrl = new URL(imageUrl);
21
+ }
22
+ catch {
12
23
  throw new Error([
13
- "Anthropic only supports base64-encoded images currently.",
24
+ `Malformed image URL: ${JSON.stringify(imageUrl)}. Content blocks of type 'image_url' must be a valid http, https, or base64-encoded data URL.`,
14
25
  "Example: data:image/png;base64,/9j/4AAQSk...",
26
+ "Example: https://example.com/image.jpg",
15
27
  ].join("\n\n"));
16
28
  }
17
- return {
18
- type: "base64",
19
- media_type: match[1] ?? "",
20
- data: match[2] ?? "",
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- };
29
+ if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
30
+ return {
31
+ type: "url",
32
+ url: imageUrl,
33
+ };
34
+ }
35
+ throw new Error([
36
+ `Invalid image URL protocol: ${JSON.stringify(parsedUrl.protocol)}. Anthropic only supports images as http, https, or base64-encoded data URLs on 'image_url' content blocks.`,
37
+ "Example: data:image/png;base64,/9j/4AAQSk...",
38
+ "Example: https://example.com/image.jpg",
39
+ ].join("\n\n"));
23
40
  }
24
41
  function _ensureMessageContents(messages) {
25
42
  // Merge runs of human/tool messages into single human messages with content blocks.
@@ -82,6 +99,188 @@ function _convertLangChainToolCallToAnthropic(toolCall) {
82
99
  };
83
100
  }
84
101
  exports._convertLangChainToolCallToAnthropic = _convertLangChainToolCallToAnthropic;
102
+ const standardContentBlockConverter = {
103
+ providerName: "anthropic",
104
+ fromStandardTextBlock(block) {
105
+ return {
106
+ type: "text",
107
+ text: block.text,
108
+ ...("citations" in (block.metadata ?? {})
109
+ ? { citations: block.metadata.citations }
110
+ : {}),
111
+ ...("cache_control" in (block.metadata ?? {})
112
+ ? { cache_control: block.metadata.cache_control }
113
+ : {}),
114
+ };
115
+ },
116
+ fromStandardImageBlock(block) {
117
+ if (block.source_type === "url") {
118
+ const data = (0, messages_1.parseBase64DataUrl)({
119
+ dataUrl: block.url,
120
+ asTypedArray: false,
121
+ });
122
+ if (data) {
123
+ return {
124
+ type: "image",
125
+ source: {
126
+ type: "base64",
127
+ data: data.data,
128
+ media_type: data.mime_type,
129
+ },
130
+ ...("cache_control" in (block.metadata ?? {})
131
+ ? { cache_control: block.metadata.cache_control }
132
+ : {}),
133
+ };
134
+ }
135
+ else {
136
+ return {
137
+ type: "image",
138
+ source: {
139
+ type: "url",
140
+ url: block.url,
141
+ media_type: block.mime_type ?? "",
142
+ },
143
+ ...("cache_control" in (block.metadata ?? {})
144
+ ? { cache_control: block.metadata.cache_control }
145
+ : {}),
146
+ };
147
+ }
148
+ }
149
+ else {
150
+ if (block.source_type === "base64") {
151
+ return {
152
+ type: "image",
153
+ source: {
154
+ type: "base64",
155
+ data: block.data,
156
+ media_type: block.mime_type ?? "",
157
+ },
158
+ ...("cache_control" in (block.metadata ?? {})
159
+ ? { cache_control: block.metadata.cache_control }
160
+ : {}),
161
+ };
162
+ }
163
+ else {
164
+ throw new Error(`Unsupported image source type: ${block.source_type}`);
165
+ }
166
+ }
167
+ },
168
+ fromStandardFileBlock(block) {
169
+ const mime_type = (block.mime_type ?? "").split(";")[0];
170
+ if (block.source_type === "url") {
171
+ if (mime_type === "application/pdf" || mime_type === "") {
172
+ return {
173
+ type: "document",
174
+ source: {
175
+ type: "url",
176
+ url: block.url,
177
+ media_type: block.mime_type ?? "",
178
+ },
179
+ ...("cache_control" in (block.metadata ?? {})
180
+ ? { cache_control: block.metadata.cache_control }
181
+ : {}),
182
+ ...("citations" in (block.metadata ?? {})
183
+ ? { citations: block.metadata.citations }
184
+ : {}),
185
+ ...("context" in (block.metadata ?? {})
186
+ ? { context: block.metadata.context }
187
+ : {}),
188
+ ...("title" in (block.metadata ?? {})
189
+ ? { title: block.metadata.title }
190
+ : {}),
191
+ };
192
+ }
193
+ throw new Error(`Unsupported file mime type for file url source: ${block.mime_type}`);
194
+ }
195
+ else if (block.source_type === "text") {
196
+ if (mime_type === "text/plain" || mime_type === "") {
197
+ return {
198
+ type: "document",
199
+ source: {
200
+ type: "text",
201
+ data: block.text,
202
+ media_type: block.mime_type ?? "",
203
+ },
204
+ ...("cache_control" in (block.metadata ?? {})
205
+ ? { cache_control: block.metadata.cache_control }
206
+ : {}),
207
+ ...("citations" in (block.metadata ?? {})
208
+ ? { citations: block.metadata.citations }
209
+ : {}),
210
+ ...("context" in (block.metadata ?? {})
211
+ ? { context: block.metadata.context }
212
+ : {}),
213
+ ...("title" in (block.metadata ?? {})
214
+ ? { title: block.metadata.title }
215
+ : {}),
216
+ };
217
+ }
218
+ else {
219
+ throw new Error(`Unsupported file mime type for file text source: ${block.mime_type}`);
220
+ }
221
+ }
222
+ else if (block.source_type === "base64") {
223
+ if (mime_type === "application/pdf" || mime_type === "") {
224
+ return {
225
+ type: "document",
226
+ source: {
227
+ type: "base64",
228
+ data: block.data,
229
+ media_type: "application/pdf",
230
+ },
231
+ ...("cache_control" in (block.metadata ?? {})
232
+ ? { cache_control: block.metadata.cache_control }
233
+ : {}),
234
+ ...("citations" in (block.metadata ?? {})
235
+ ? { citations: block.metadata.citations }
236
+ : {}),
237
+ ...("context" in (block.metadata ?? {})
238
+ ? { context: block.metadata.context }
239
+ : {}),
240
+ ...("title" in (block.metadata ?? {})
241
+ ? { title: block.metadata.title }
242
+ : {}),
243
+ };
244
+ }
245
+ else if (["image/jpeg", "image/png", "image/gif", "image/webp"].includes(mime_type)) {
246
+ return {
247
+ type: "document",
248
+ source: {
249
+ type: "content",
250
+ content: [
251
+ {
252
+ type: "image",
253
+ source: {
254
+ type: "base64",
255
+ data: block.data,
256
+ media_type: mime_type,
257
+ },
258
+ },
259
+ ],
260
+ },
261
+ ...("cache_control" in (block.metadata ?? {})
262
+ ? { cache_control: block.metadata.cache_control }
263
+ : {}),
264
+ ...("citations" in (block.metadata ?? {})
265
+ ? { citations: block.metadata.citations }
266
+ : {}),
267
+ ...("context" in (block.metadata ?? {})
268
+ ? { context: block.metadata.context }
269
+ : {}),
270
+ ...("title" in (block.metadata ?? {})
271
+ ? { title: block.metadata.title }
272
+ : {}),
273
+ };
274
+ }
275
+ else {
276
+ throw new Error(`Unsupported file mime type for file base64 source: ${block.mime_type}`);
277
+ }
278
+ }
279
+ else {
280
+ throw new Error(`Unsupported file source type: ${block.source_type}`);
281
+ }
282
+ },
283
+ };
85
284
  function _formatContent(content) {
86
285
  const toolTypes = ["tool_use", "tool_result", "input_json_delta"];
87
286
  const textTypes = ["text", "text_delta"];
@@ -90,6 +289,9 @@ function _formatContent(content) {
90
289
  }
91
290
  else {
92
291
  const contentBlocks = content.map((contentPart) => {
292
+ if ((0, messages_1.isDataContentBlock)(contentPart)) {
293
+ return (0, messages_1.convertToProviderContentBlock)(contentPart, standardContentBlockConverter);
294
+ }
93
295
  const cacheControl = "cache_control" in contentPart ? contentPart.cache_control : undefined;
94
296
  if (contentPart.type === "image_url") {
95
297
  let source;
@@ -105,6 +307,9 @@ function _formatContent(content) {
105
307
  ...(cacheControl ? { cache_control: cacheControl } : {}),
106
308
  };
107
309
  }
310
+ else if ((0, types_js_1.isAnthropicImageBlockParam)(contentPart)) {
311
+ return contentPart;
312
+ }
108
313
  else if (contentPart.type === "document") {
109
314
  // PDF
110
315
  return {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * This util file contains functions for converting LangChain messages to Anthropic messages.
3
3
  */
4
- import { BaseMessage } from "@langchain/core/messages";
4
+ import { type BaseMessage } from "@langchain/core/messages";
5
5
  import { ToolCall } from "@langchain/core/messages/tool";
6
6
  import { AnthropicMessageCreateParams, AnthropicToolResponse } from "../types.js";
7
7
  export declare function _convertLangChainToolCallToAnthropic(toolCall: ToolCall): AnthropicToolResponse;
@@ -1,22 +1,39 @@
1
1
  /**
2
2
  * This util file contains functions for converting LangChain messages to Anthropic messages.
3
3
  */
4
- import { HumanMessage, isAIMessage, } from "@langchain/core/messages";
4
+ import { HumanMessage, isAIMessage, isDataContentBlock, convertToProviderContentBlock, parseBase64DataUrl, } from "@langchain/core/messages";
5
+ import { isAnthropicImageBlockParam, } from "../types.js";
5
6
  function _formatImage(imageUrl) {
6
- const regex = /^data:(image\/.+);base64,(.+)$/;
7
- const match = imageUrl.match(regex);
8
- if (match === null) {
7
+ const parsed = parseBase64DataUrl({ dataUrl: imageUrl });
8
+ if (parsed) {
9
+ return {
10
+ type: "base64",
11
+ media_type: parsed.mime_type,
12
+ data: parsed.data,
13
+ };
14
+ }
15
+ let parsedUrl;
16
+ try {
17
+ parsedUrl = new URL(imageUrl);
18
+ }
19
+ catch {
9
20
  throw new Error([
10
- "Anthropic only supports base64-encoded images currently.",
21
+ `Malformed image URL: ${JSON.stringify(imageUrl)}. Content blocks of type 'image_url' must be a valid http, https, or base64-encoded data URL.`,
11
22
  "Example: data:image/png;base64,/9j/4AAQSk...",
23
+ "Example: https://example.com/image.jpg",
12
24
  ].join("\n\n"));
13
25
  }
14
- return {
15
- type: "base64",
16
- media_type: match[1] ?? "",
17
- data: match[2] ?? "",
18
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
- };
26
+ if (parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:") {
27
+ return {
28
+ type: "url",
29
+ url: imageUrl,
30
+ };
31
+ }
32
+ throw new Error([
33
+ `Invalid image URL protocol: ${JSON.stringify(parsedUrl.protocol)}. Anthropic only supports images as http, https, or base64-encoded data URLs on 'image_url' content blocks.`,
34
+ "Example: data:image/png;base64,/9j/4AAQSk...",
35
+ "Example: https://example.com/image.jpg",
36
+ ].join("\n\n"));
20
37
  }
21
38
  function _ensureMessageContents(messages) {
22
39
  // Merge runs of human/tool messages into single human messages with content blocks.
@@ -78,6 +95,188 @@ export function _convertLangChainToolCallToAnthropic(toolCall) {
78
95
  input: toolCall.args,
79
96
  };
80
97
  }
98
+ const standardContentBlockConverter = {
99
+ providerName: "anthropic",
100
+ fromStandardTextBlock(block) {
101
+ return {
102
+ type: "text",
103
+ text: block.text,
104
+ ...("citations" in (block.metadata ?? {})
105
+ ? { citations: block.metadata.citations }
106
+ : {}),
107
+ ...("cache_control" in (block.metadata ?? {})
108
+ ? { cache_control: block.metadata.cache_control }
109
+ : {}),
110
+ };
111
+ },
112
+ fromStandardImageBlock(block) {
113
+ if (block.source_type === "url") {
114
+ const data = parseBase64DataUrl({
115
+ dataUrl: block.url,
116
+ asTypedArray: false,
117
+ });
118
+ if (data) {
119
+ return {
120
+ type: "image",
121
+ source: {
122
+ type: "base64",
123
+ data: data.data,
124
+ media_type: data.mime_type,
125
+ },
126
+ ...("cache_control" in (block.metadata ?? {})
127
+ ? { cache_control: block.metadata.cache_control }
128
+ : {}),
129
+ };
130
+ }
131
+ else {
132
+ return {
133
+ type: "image",
134
+ source: {
135
+ type: "url",
136
+ url: block.url,
137
+ media_type: block.mime_type ?? "",
138
+ },
139
+ ...("cache_control" in (block.metadata ?? {})
140
+ ? { cache_control: block.metadata.cache_control }
141
+ : {}),
142
+ };
143
+ }
144
+ }
145
+ else {
146
+ if (block.source_type === "base64") {
147
+ return {
148
+ type: "image",
149
+ source: {
150
+ type: "base64",
151
+ data: block.data,
152
+ media_type: block.mime_type ?? "",
153
+ },
154
+ ...("cache_control" in (block.metadata ?? {})
155
+ ? { cache_control: block.metadata.cache_control }
156
+ : {}),
157
+ };
158
+ }
159
+ else {
160
+ throw new Error(`Unsupported image source type: ${block.source_type}`);
161
+ }
162
+ }
163
+ },
164
+ fromStandardFileBlock(block) {
165
+ const mime_type = (block.mime_type ?? "").split(";")[0];
166
+ if (block.source_type === "url") {
167
+ if (mime_type === "application/pdf" || mime_type === "") {
168
+ return {
169
+ type: "document",
170
+ source: {
171
+ type: "url",
172
+ url: block.url,
173
+ media_type: block.mime_type ?? "",
174
+ },
175
+ ...("cache_control" in (block.metadata ?? {})
176
+ ? { cache_control: block.metadata.cache_control }
177
+ : {}),
178
+ ...("citations" in (block.metadata ?? {})
179
+ ? { citations: block.metadata.citations }
180
+ : {}),
181
+ ...("context" in (block.metadata ?? {})
182
+ ? { context: block.metadata.context }
183
+ : {}),
184
+ ...("title" in (block.metadata ?? {})
185
+ ? { title: block.metadata.title }
186
+ : {}),
187
+ };
188
+ }
189
+ throw new Error(`Unsupported file mime type for file url source: ${block.mime_type}`);
190
+ }
191
+ else if (block.source_type === "text") {
192
+ if (mime_type === "text/plain" || mime_type === "") {
193
+ return {
194
+ type: "document",
195
+ source: {
196
+ type: "text",
197
+ data: block.text,
198
+ media_type: block.mime_type ?? "",
199
+ },
200
+ ...("cache_control" in (block.metadata ?? {})
201
+ ? { cache_control: block.metadata.cache_control }
202
+ : {}),
203
+ ...("citations" in (block.metadata ?? {})
204
+ ? { citations: block.metadata.citations }
205
+ : {}),
206
+ ...("context" in (block.metadata ?? {})
207
+ ? { context: block.metadata.context }
208
+ : {}),
209
+ ...("title" in (block.metadata ?? {})
210
+ ? { title: block.metadata.title }
211
+ : {}),
212
+ };
213
+ }
214
+ else {
215
+ throw new Error(`Unsupported file mime type for file text source: ${block.mime_type}`);
216
+ }
217
+ }
218
+ else if (block.source_type === "base64") {
219
+ if (mime_type === "application/pdf" || mime_type === "") {
220
+ return {
221
+ type: "document",
222
+ source: {
223
+ type: "base64",
224
+ data: block.data,
225
+ media_type: "application/pdf",
226
+ },
227
+ ...("cache_control" in (block.metadata ?? {})
228
+ ? { cache_control: block.metadata.cache_control }
229
+ : {}),
230
+ ...("citations" in (block.metadata ?? {})
231
+ ? { citations: block.metadata.citations }
232
+ : {}),
233
+ ...("context" in (block.metadata ?? {})
234
+ ? { context: block.metadata.context }
235
+ : {}),
236
+ ...("title" in (block.metadata ?? {})
237
+ ? { title: block.metadata.title }
238
+ : {}),
239
+ };
240
+ }
241
+ else if (["image/jpeg", "image/png", "image/gif", "image/webp"].includes(mime_type)) {
242
+ return {
243
+ type: "document",
244
+ source: {
245
+ type: "content",
246
+ content: [
247
+ {
248
+ type: "image",
249
+ source: {
250
+ type: "base64",
251
+ data: block.data,
252
+ media_type: mime_type,
253
+ },
254
+ },
255
+ ],
256
+ },
257
+ ...("cache_control" in (block.metadata ?? {})
258
+ ? { cache_control: block.metadata.cache_control }
259
+ : {}),
260
+ ...("citations" in (block.metadata ?? {})
261
+ ? { citations: block.metadata.citations }
262
+ : {}),
263
+ ...("context" in (block.metadata ?? {})
264
+ ? { context: block.metadata.context }
265
+ : {}),
266
+ ...("title" in (block.metadata ?? {})
267
+ ? { title: block.metadata.title }
268
+ : {}),
269
+ };
270
+ }
271
+ else {
272
+ throw new Error(`Unsupported file mime type for file base64 source: ${block.mime_type}`);
273
+ }
274
+ }
275
+ else {
276
+ throw new Error(`Unsupported file source type: ${block.source_type}`);
277
+ }
278
+ },
279
+ };
81
280
  function _formatContent(content) {
82
281
  const toolTypes = ["tool_use", "tool_result", "input_json_delta"];
83
282
  const textTypes = ["text", "text_delta"];
@@ -86,6 +285,9 @@ function _formatContent(content) {
86
285
  }
87
286
  else {
88
287
  const contentBlocks = content.map((contentPart) => {
288
+ if (isDataContentBlock(contentPart)) {
289
+ return convertToProviderContentBlock(contentPart, standardContentBlockConverter);
290
+ }
89
291
  const cacheControl = "cache_control" in contentPart ? contentPart.cache_control : undefined;
90
292
  if (contentPart.type === "image_url") {
91
293
  let source;
@@ -101,6 +303,9 @@ function _formatContent(content) {
101
303
  ...(cacheControl ? { cache_control: cacheControl } : {}),
102
304
  };
103
305
  }
306
+ else if (isAnthropicImageBlockParam(contentPart)) {
307
+ return contentPart;
308
+ }
104
309
  else if (contentPart.type === "document") {
105
310
  // PDF
106
311
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/anthropic",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "description": "Anthropic integrations for LangChain.js",
5
5
  "type": "module",
6
6
  "engines": {
@@ -35,7 +35,7 @@
35
35
  "author": "LangChain",
36
36
  "license": "MIT",
37
37
  "dependencies": {
38
- "@anthropic-ai/sdk": "^0.37.0",
38
+ "@anthropic-ai/sdk": "^0.39.0",
39
39
  "fast-xml-parser": "^4.4.1",
40
40
  "zod": "^3.22.4",
41
41
  "zod-to-json-schema": "^3.22.4"