@effect/ai-anthropic 0.16.1 → 0.17.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.
Files changed (48) hide show
  1. package/AnthropicTool/package.json +6 -0
  2. package/dist/cjs/AnthropicClient.js +286 -190
  3. package/dist/cjs/AnthropicClient.js.map +1 -1
  4. package/dist/cjs/AnthropicLanguageModel.js +1026 -311
  5. package/dist/cjs/AnthropicLanguageModel.js.map +1 -1
  6. package/dist/cjs/AnthropicTokenizer.js +8 -6
  7. package/dist/cjs/AnthropicTokenizer.js.map +1 -1
  8. package/dist/cjs/AnthropicTool.js +461 -0
  9. package/dist/cjs/AnthropicTool.js.map +1 -0
  10. package/dist/cjs/Generated.js +3507 -1230
  11. package/dist/cjs/Generated.js.map +1 -1
  12. package/dist/cjs/index.js +3 -1
  13. package/dist/cjs/internal/utilities.js +13 -3
  14. package/dist/cjs/internal/utilities.js.map +1 -1
  15. package/dist/dts/AnthropicClient.d.ts +673 -17
  16. package/dist/dts/AnthropicClient.d.ts.map +1 -1
  17. package/dist/dts/AnthropicLanguageModel.d.ts +217 -26
  18. package/dist/dts/AnthropicLanguageModel.d.ts.map +1 -1
  19. package/dist/dts/AnthropicTokenizer.d.ts +1 -1
  20. package/dist/dts/AnthropicTokenizer.d.ts.map +1 -1
  21. package/dist/dts/AnthropicTool.d.ts +523 -0
  22. package/dist/dts/AnthropicTool.d.ts.map +1 -0
  23. package/dist/dts/Generated.d.ts +7863 -3496
  24. package/dist/dts/Generated.d.ts.map +1 -1
  25. package/dist/dts/index.d.ts +4 -0
  26. package/dist/dts/index.d.ts.map +1 -1
  27. package/dist/esm/AnthropicClient.js +269 -188
  28. package/dist/esm/AnthropicClient.js.map +1 -1
  29. package/dist/esm/AnthropicLanguageModel.js +1022 -306
  30. package/dist/esm/AnthropicLanguageModel.js.map +1 -1
  31. package/dist/esm/AnthropicTokenizer.js +8 -6
  32. package/dist/esm/AnthropicTokenizer.js.map +1 -1
  33. package/dist/esm/AnthropicTool.js +452 -0
  34. package/dist/esm/AnthropicTool.js.map +1 -0
  35. package/dist/esm/Generated.js +3492 -1063
  36. package/dist/esm/Generated.js.map +1 -1
  37. package/dist/esm/index.js +4 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/internal/utilities.js +12 -2
  40. package/dist/esm/internal/utilities.js.map +1 -1
  41. package/package.json +11 -3
  42. package/src/AnthropicClient.ts +713 -369
  43. package/src/AnthropicLanguageModel.ts +1404 -345
  44. package/src/AnthropicTokenizer.ts +14 -23
  45. package/src/AnthropicTool.ts +553 -0
  46. package/src/Generated.ts +4165 -1681
  47. package/src/index.ts +5 -0
  48. package/src/internal/utilities.ts +18 -4
@@ -1,26 +1,25 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import { AiError } from "@effect/ai/AiError";
5
- import * as AiLanguageModel from "@effect/ai/AiLanguageModel";
6
- import * as AiModel from "@effect/ai/AiModel";
7
- import * as AiResponse from "@effect/ai/AiResponse";
8
- import { addGenAIAnnotations } from "@effect/ai/AiTelemetry";
4
+ import * as AiError from "@effect/ai/AiError";
5
+ import * as IdGenerator from "@effect/ai/IdGenerator";
6
+ import * as LanguageModel from "@effect/ai/LanguageModel";
7
+ import * as AiModel from "@effect/ai/Model";
8
+ import { addGenAIAnnotations } from "@effect/ai/Telemetry";
9
+ import * as Tool from "@effect/ai/Tool";
9
10
  import * as Arr from "effect/Array";
10
11
  import * as Context from "effect/Context";
12
+ import * as DateTime from "effect/DateTime";
11
13
  import * as Effect from "effect/Effect";
12
14
  import * as Encoding from "effect/Encoding";
13
15
  import { dual } from "effect/Function";
14
16
  import * as Layer from "effect/Layer";
15
- import * as Option from "effect/Option";
16
17
  import * as Predicate from "effect/Predicate";
17
18
  import * as Stream from "effect/Stream";
18
19
  import { AnthropicClient } from "./AnthropicClient.js";
19
20
  import * as AnthropicTokenizer from "./AnthropicTokenizer.js";
21
+ import * as AnthropicTool from "./AnthropicTool.js";
20
22
  import * as InternalUtilities from "./internal/utilities.js";
21
- const constDisableValidation = {
22
- disableValidation: true
23
- };
24
23
  // =============================================================================
25
24
  // Configuration
26
25
  // =============================================================================
@@ -35,29 +34,21 @@ export class Config extends /*#__PURE__*/Context.Tag("@effect/ai-anthropic/Anthr
35
34
  static getOrUndefined = /*#__PURE__*/Effect.map(/*#__PURE__*/Effect.context(), context => context.unsafeMap.get(Config.key));
36
35
  }
37
36
  // =============================================================================
38
- // Anthropic Provider Metadata
39
- // =============================================================================
40
- /**
41
- * @since 1.0.0
42
- * @category Context
43
- */
44
- export class ProviderMetadata extends /*#__PURE__*/Context.Tag(InternalUtilities.ProviderMetadataKey)() {}
45
- // =============================================================================
46
37
  // Anthropic Language Model
47
38
  // =============================================================================
48
39
  /**
49
40
  * @since 1.0.0
50
- * @category AiModels
41
+ * @category Ai Models
51
42
  */
52
- export const model = (model, config) => AiModel.make(layer({
43
+ export const model = (model, config) => AiModel.make("anthropic", layer({
53
44
  model,
54
45
  config
55
46
  }));
56
47
  /**
57
48
  * @since 1.0.0
58
- * @category AiModels
49
+ * @category Ai Models
59
50
  */
60
- export const modelWithTokenizer = (model, config) => AiModel.make(layerWithTokenizer({
51
+ export const modelWithTokenizer = (model, config) => AiModel.make("anthropic", layerWithTokenizer({
61
52
  model,
62
53
  config
63
54
  }));
@@ -67,88 +58,86 @@ export const modelWithTokenizer = (model, config) => AiModel.make(layerWithToken
67
58
  */
68
59
  export const make = /*#__PURE__*/Effect.fnUntraced(function* (options) {
69
60
  const client = yield* AnthropicClient;
70
- const makeRequest = Effect.fnUntraced(function* (method, {
71
- prompt,
72
- system,
73
- toolChoice,
74
- tools
75
- }) {
61
+ const makeRequest = Effect.fnUntraced(function* (providerOptions) {
76
62
  const context = yield* Effect.context();
77
- const useStructured = tools.length === 1 && tools[0].structured;
78
- let tool_choice = undefined;
79
- if (useStructured) {
80
- tool_choice = {
81
- type: "tool",
82
- name: tools[0].name
83
- };
84
- } else if (tools.length > 0) {
85
- if (toolChoice === "required") {
86
- tool_choice = {
87
- type: "any"
88
- };
89
- } else if (typeof toolChoice === "object") {
90
- tool_choice = {
91
- type: "tool",
92
- name: toolChoice.tool
93
- };
94
- } else {
95
- tool_choice = {
96
- type: toolChoice
97
- };
98
- }
99
- }
100
- const messages = yield* makeMessages(method, prompt);
101
- return {
63
+ const config = {
102
64
  model: options.model,
103
- max_tokens: 4096,
104
65
  ...options.config,
105
- ...context.unsafeMap.get(Config.key),
106
- // TODO: re-evaluate a better way to do this
107
- system: Option.getOrUndefined(system),
66
+ ...context.unsafeMap.get(Config.key)
67
+ };
68
+ const {
69
+ betas: messageBetas,
108
70
  messages,
109
- tools: tools.length === 0 ? undefined : tools.map(tool => ({
110
- name: tool.name,
111
- description: tool.description,
112
- input_schema: tool.parameters
113
- })),
114
- tool_choice
71
+ system
72
+ } = yield* prepareMessages(providerOptions);
73
+ const {
74
+ betas: toolBetas,
75
+ toolChoice,
76
+ tools
77
+ } = yield* prepareTools(providerOptions, config);
78
+ const responseFormat = providerOptions.responseFormat;
79
+ const request = {
80
+ max_tokens: 4096,
81
+ ...config,
82
+ system,
83
+ messages,
84
+ tools: responseFormat.type === "text" ? tools : [{
85
+ name: responseFormat.objectName,
86
+ description: Tool.getDescriptionFromSchemaAst(responseFormat.schema.ast) ?? "Respond with a JSON object",
87
+ input_schema: Tool.getJsonSchemaFromSchemaAst(responseFormat.schema.ast)
88
+ }],
89
+ tool_choice: responseFormat.type === "text" ? toolChoice : {
90
+ type: "tool",
91
+ name: responseFormat.objectName,
92
+ disable_parallel_tool_use: true
93
+ }
94
+ };
95
+ return {
96
+ betas: new Set([...messageBetas, ...toolBetas]),
97
+ request
115
98
  };
116
99
  });
117
- return yield* AiLanguageModel.make({
100
+ return yield* LanguageModel.make({
118
101
  generateText: Effect.fnUntraced(function* (options) {
119
- const request = yield* makeRequest("generateText", options);
102
+ const {
103
+ betas,
104
+ request
105
+ } = yield* makeRequest(options);
120
106
  annotateRequest(options.span, request);
121
- const rawResponse = yield* client.client.messagesPost({
122
- params: {},
107
+ const anthropicBeta = betas.size > 0 ? Array.from(betas).join(",") : undefined;
108
+ const rawResponse = yield* client.createMessage({
109
+ params: {
110
+ "anthropic-beta": anthropicBeta
111
+ },
123
112
  payload: request
124
113
  });
125
- annotateChatResponse(options.span, rawResponse);
126
- const response = yield* makeResponse(rawResponse);
114
+ annotateResponse(options.span, rawResponse);
115
+ return yield* makeResponse(rawResponse, options);
116
+ }),
117
+ streamText: Effect.fnUntraced(function* (options) {
118
+ const {
119
+ betas,
120
+ request
121
+ } = yield* makeRequest(options);
122
+ annotateRequest(options.span, request);
123
+ const anthropicBeta = betas.size > 0 ? Array.from(betas).join(",") : undefined;
124
+ return client.createMessageStream({
125
+ params: {
126
+ "anthropic-beta": anthropicBeta
127
+ },
128
+ payload: request
129
+ });
130
+ }, (effect, options) => effect.pipe(Effect.flatMap(stream => makeStreamResponse(stream, options)), Stream.unwrap, Stream.map(response => {
131
+ annotateStreamResponse(options.span, response);
127
132
  return response;
128
- }, Effect.catchAll(cause => AiError.is(cause) ? cause : new AiError({
129
- module: "AnthropicLanguageModel",
130
- method: "generateText",
131
- description: "An error occurred",
132
- cause
133
- }))),
134
- streamText(options) {
135
- return makeRequest("streamText", options).pipe(Effect.tap(request => annotateRequest(options.span, request)), Effect.map(client.stream), Stream.unwrap, Stream.map(response => {
136
- annotateStreamResponse(options.span, response);
137
- return response;
138
- }), Stream.catchAll(cause => AiError.is(cause) ? Effect.fail(cause) : Effect.fail(new AiError({
139
- module: "AnthropicLanguageModel",
140
- method: "streamText",
141
- description: "An error occurred",
142
- cause
143
- }))));
144
- }
133
+ })))
145
134
  });
146
135
  });
147
136
  /**
148
137
  * @since 1.0.0
149
138
  * @category Layers
150
139
  */
151
- export const layer = options => Layer.effect(AiLanguageModel.AiLanguageModel, make({
140
+ export const layer = options => Layer.effect(LanguageModel.LanguageModel, make({
152
141
  model: options.model,
153
142
  config: options.config
154
143
  }));
@@ -165,193 +154,125 @@ export const withConfigOverride = /*#__PURE__*/dual(2, (self, overrides) => Effe
165
154
  ...config,
166
155
  ...overrides
167
156
  })));
168
- const groupMessages = prompt => {
169
- const messages = [];
170
- let current = undefined;
171
- for (const message of prompt.messages) {
172
- switch (message._tag) {
173
- case "AssistantMessage":
174
- {
175
- if (current?.type !== "assistant") {
176
- current = {
177
- type: "assistant",
178
- messages: []
179
- };
180
- messages.push(current);
181
- }
182
- current.messages.push(message);
183
- break;
184
- }
185
- case "ToolMessage":
186
- case "UserMessage":
187
- {
188
- if (current?.type !== "user") {
189
- current = {
190
- type: "user",
191
- messages: []
192
- };
193
- messages.push(current);
194
- }
195
- current.messages.push(message);
196
- break;
197
- }
198
- }
199
- }
200
- return messages;
201
- };
202
- const makeMessages = /*#__PURE__*/Effect.fnUntraced(function* (method, prompt) {
157
+ // =============================================================================
158
+ // Prompt Conversion
159
+ // =============================================================================
160
+ const prepareMessages = /*#__PURE__*/Effect.fnUntraced(function* (options) {
161
+ const betas = new Set();
162
+ const groups = groupMessages(options.prompt);
163
+ let system = undefined;
203
164
  const messages = [];
204
- const groups = groupMessages(prompt);
205
165
  for (let i = 0; i < groups.length; i++) {
206
166
  const group = groups[i];
207
167
  const isLastGroup = i === groups.length - 1;
208
168
  switch (group.type) {
209
- case "assistant":
169
+ case "system":
210
170
  {
211
- const content = [];
212
- for (let j = 0; j < group.messages.length; j++) {
213
- const message = group.messages[j];
214
- const isLastMessage = j === group.messages.length - 1;
215
- for (let k = 0; k < message.parts.length; k++) {
216
- const part = message.parts[k];
217
- const isLastPart = k === message.parts.length - 1;
218
- switch (part._tag) {
219
- case "ReasoningPart":
220
- {
221
- content.push({
222
- type: "thinking",
223
- thinking: part.reasoningText,
224
- signature: part.signature
225
- });
226
- break;
227
- }
228
- case "RedactedReasoningPart":
229
- {
230
- content.push({
231
- type: "redacted_thinking",
232
- data: part.redactedText
233
- });
234
- break;
235
- }
236
- case "TextPart":
237
- {
238
- content.push({
239
- type: "text",
240
- text:
241
- // Anthropic does not allow trailing whitespace in assistant
242
- // content blocks
243
- isLastGroup && isLastMessage && isLastPart ? part.text.trim() : part.text
244
- });
245
- break;
246
- }
247
- case "ToolCallPart":
248
- {
249
- content.push({
250
- type: "tool_use",
251
- id: part.id,
252
- name: part.name,
253
- input: part.params
254
- });
255
- break;
256
- }
257
- }
258
- }
259
- }
260
- messages.push({
261
- role: "assistant",
262
- content
263
- });
171
+ system = group.messages.map(message => ({
172
+ type: "text",
173
+ text: message.content,
174
+ cache_control: getCacheControl(message)
175
+ }));
264
176
  break;
265
177
  }
266
178
  case "user":
267
179
  {
268
180
  const content = [];
269
- for (let j = 0; j < group.messages.length; j++) {
270
- const message = group.messages[j];
271
- switch (message._tag) {
272
- case "ToolMessage":
181
+ for (const message of group.messages) {
182
+ switch (message.role) {
183
+ case "user":
273
184
  {
274
- for (let k = 0; k < message.parts.length; k++) {
275
- const part = message.parts[k];
276
- // TODO: support advanced tool result content parts
277
- content.push({
278
- type: "tool_result",
279
- tool_use_id: part.id,
280
- content: JSON.stringify(part.result)
281
- });
282
- }
283
- break;
284
- }
285
- case "UserMessage":
286
- {
287
- for (let k = 0; k < message.parts.length; k++) {
288
- const part = message.parts[k];
289
- switch (part._tag) {
290
- case "FilePart":
291
- {
292
- if (Predicate.isUndefined(part.mediaType) || part.mediaType !== "application/pdf") {
293
- return yield* new AiError({
294
- module: "AnthropicLanguageModel",
295
- method,
296
- description: "AnthropicLanguageModel only supports PDF file inputs"
297
- });
298
- }
299
- content.push({
300
- type: "document",
301
- source: {
302
- type: "base64",
303
- media_type: "application/pdf",
304
- data: Encoding.encodeBase64(part.data)
305
- }
306
- });
307
- break;
308
- }
309
- case "FileUrlPart":
310
- {
311
- content.push({
312
- type: "document",
313
- source: {
314
- type: "url",
315
- url: part.url.toString()
316
- }
317
- });
318
- break;
319
- }
320
- case "TextPart":
185
+ for (let j = 0; j < message.content.length; j++) {
186
+ const part = message.content[j];
187
+ const isLastPart = j === message.content.length - 1;
188
+ // Attempt to get the cache control from the part first. If
189
+ // the part does not have cache control defined and we are
190
+ // evaluating the last part for this message, also check the
191
+ // message for cache control.
192
+ const cacheControl = getCacheControl(part) ?? (isLastPart ? getCacheControl(message) : undefined);
193
+ switch (part.type) {
194
+ case "text":
321
195
  {
322
196
  content.push({
323
197
  type: "text",
324
- text: part.text
198
+ text: part.text,
199
+ cache_control: cacheControl
325
200
  });
326
201
  break;
327
202
  }
328
- case "ImagePart":
203
+ case "file":
329
204
  {
330
- content.push({
331
- type: "image",
332
- source: {
205
+ if (part.mediaType.startsWith("image/")) {
206
+ const source = part.data instanceof URL ? {
207
+ type: "url",
208
+ url: part.data.toString()
209
+ } : {
333
210
  type: "base64",
334
- media_type: part.mediaType ?? "image/jpeg",
335
- data: Encoding.encodeBase64(part.data)
211
+ media_type: part.mediaType === "image/*" ? "image/jpeg" : part.mediaType,
212
+ data: typeof part.data === "string" ? part.data : Encoding.encodeBase64(part.data)
213
+ };
214
+ content.push({
215
+ type: "image",
216
+ source,
217
+ cache_control: cacheControl
218
+ });
219
+ } else if (part.mediaType === "application/pdf" || part.mediaType === "text/plain") {
220
+ if (part.mediaType === "application/pdf") {
221
+ betas.add("pdfs-2024-09-25");
336
222
  }
337
- });
338
- break;
339
- }
340
- case "ImageUrlPart":
341
- {
342
- content.push({
343
- type: "image",
344
- source: {
223
+ const enableCitations = shouldEnableCitations(part);
224
+ const documentOptions = getDocumentMetadata(part);
225
+ const source = part.data instanceof URL ? {
345
226
  type: "url",
346
- url: part.url.toString()
347
- }
348
- });
227
+ url: part.data.toString()
228
+ } : part.mediaType === "application/pdf" ? {
229
+ type: "base64",
230
+ media_type: "application/pdf",
231
+ data: typeof part.data === "string" ? part.data : Encoding.encodeBase64(part.data)
232
+ } : {
233
+ type: "text",
234
+ media_type: "text/plain",
235
+ data: typeof part.data === "string" ? part.data : Encoding.encodeBase64(part.data)
236
+ };
237
+ content.push({
238
+ type: "document",
239
+ source,
240
+ title: documentOptions?.title ?? part.fileName,
241
+ ...(documentOptions?.context ? {
242
+ context: documentOptions.context
243
+ } : undefined),
244
+ ...(enableCitations ? {
245
+ citations: {
246
+ enabled: true
247
+ }
248
+ } : undefined),
249
+ cache_control: cacheControl
250
+ });
251
+ } else {
252
+ return yield* new AiError.MalformedInput({
253
+ module: "AnthropicLanguageModel",
254
+ method: "prepareMessages",
255
+ description: `Detected unsupported media type for file: '${part.mediaType}'`
256
+ });
257
+ }
349
258
  break;
350
259
  }
351
260
  }
352
261
  }
353
262
  break;
354
263
  }
264
+ // TODO: advanced tool result content parts
265
+ case "tool":
266
+ {
267
+ for (const part of message.content) {
268
+ content.push({
269
+ type: "tool_result",
270
+ tool_use_id: part.id,
271
+ content: JSON.stringify(part.result)
272
+ });
273
+ }
274
+ break;
275
+ }
355
276
  }
356
277
  }
357
278
  messages.push({
@@ -360,81 +281,559 @@ const makeMessages = /*#__PURE__*/Effect.fnUntraced(function* (method, prompt) {
360
281
  });
361
282
  break;
362
283
  }
284
+ case "assistant":
285
+ {
286
+ const content = [];
287
+ for (let j = 0; j < group.messages.length; j++) {
288
+ const message = group.messages[j];
289
+ const isLastMessage = j === group.messages.length - 1;
290
+ for (let k = 0; k < message.content.length; k++) {
291
+ const part = message.content[k];
292
+ const isLastPart = k === message.content.length - 1;
293
+ // Attempt to get the cache control from the part first. If
294
+ // the part does not have cache control defined and we are
295
+ // evaluating the last part for this message, also check the
296
+ // message for cache control.
297
+ const cacheControl = getCacheControl(part) ?? (isLastPart ? getCacheControl(message) : undefined);
298
+ switch (part.type) {
299
+ case "text":
300
+ {
301
+ content.push({
302
+ type: "text",
303
+ // Anthropic does not allow trailing whitespace in assistant
304
+ // content blocks
305
+ text: isLastGroup && isLastMessage && isLastPart ? part.text.trim() : part.text
306
+ });
307
+ break;
308
+ }
309
+ case "reasoning":
310
+ {
311
+ const options = part.options.anthropic;
312
+ if (Predicate.isNotUndefined(options)) {
313
+ if (options.type === "thinking") {
314
+ content.push({
315
+ type: "thinking",
316
+ thinking: part.text,
317
+ signature: options.signature
318
+ });
319
+ } else {
320
+ content.push({
321
+ type: "redacted_thinking",
322
+ data: options.redactedData
323
+ });
324
+ }
325
+ }
326
+ break;
327
+ }
328
+ case "tool-call":
329
+ {
330
+ if (part.providerExecuted) {
331
+ if (part.name === "AnthropicWebSearch") {
332
+ content.push({
333
+ type: "server_tool_use",
334
+ id: part.id,
335
+ name: "web_search",
336
+ input: part.params,
337
+ cache_control: cacheControl
338
+ });
339
+ }
340
+ if (part.name === "AnthropicCodeExecution") {
341
+ content.push({
342
+ type: "server_tool_use",
343
+ id: part.id,
344
+ name: "code_execution",
345
+ input: part.params,
346
+ cache_control: cacheControl
347
+ });
348
+ }
349
+ } else {
350
+ content.push({
351
+ type: "tool_use",
352
+ id: part.id,
353
+ name: part.name,
354
+ input: part.params,
355
+ cache_control: cacheControl
356
+ });
357
+ }
358
+ break;
359
+ }
360
+ }
361
+ }
362
+ }
363
+ messages.push({
364
+ role: "assistant",
365
+ content
366
+ });
367
+ break;
368
+ }
363
369
  }
364
370
  }
365
- if (Arr.isNonEmptyReadonlyArray(messages)) {
366
- return messages;
367
- }
368
- return yield* new AiError({
369
- module: "AnthropicLanguageModel",
370
- method,
371
- description: "Prompt contained no messages"
372
- });
371
+ return {
372
+ system,
373
+ messages,
374
+ betas
375
+ };
373
376
  });
374
- const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* (response) {
377
+ // =============================================================================
378
+ // Response Conversion
379
+ // =============================================================================
380
+ const makeResponse = /*#__PURE__*/Effect.fnUntraced(function* (response, options) {
381
+ const idGenerator = yield* IdGenerator.IdGenerator;
375
382
  const parts = [];
376
- parts.push(new AiResponse.MetadataPart({
383
+ const citableDocuments = extractCitableDocuments(options.prompt);
384
+ parts.push({
385
+ type: "response-metadata",
377
386
  id: response.id,
378
- model: response.model
379
- }, constDisableValidation));
387
+ modelId: response.model,
388
+ timestamp: DateTime.formatIso(yield* DateTime.now)
389
+ });
380
390
  for (const part of response.content) {
381
391
  switch (part.type) {
382
392
  case "text":
383
393
  {
384
- parts.push(new AiResponse.TextPart({
385
- text: part.text
386
- }, constDisableValidation));
394
+ // The text parts should only be added to the response here if the
395
+ // response format is `"text"`. If the response format is `"json"`,
396
+ // then the text parts must instead be added to the response when a
397
+ // tool call is received.
398
+ if (options.responseFormat.type === "text") {
399
+ parts.push({
400
+ type: "text",
401
+ text: part.text
402
+ });
403
+ if (Predicate.isNotNullable(part.citations)) {
404
+ for (const citation of part.citations) {
405
+ const source = yield* processCitation(citation, citableDocuments, idGenerator);
406
+ if (Predicate.isNotUndefined(source)) {
407
+ parts.push(source);
408
+ }
409
+ }
410
+ }
411
+ }
412
+ break;
413
+ }
414
+ case "thinking":
415
+ {
416
+ parts.push({
417
+ type: "reasoning",
418
+ text: part.thinking,
419
+ metadata: {
420
+ anthropic: {
421
+ type: "thinking",
422
+ signature: part.signature
423
+ }
424
+ }
425
+ });
426
+ break;
427
+ }
428
+ case "redacted_thinking":
429
+ {
430
+ parts.push({
431
+ type: "reasoning",
432
+ text: "",
433
+ metadata: {
434
+ anthropic: {
435
+ type: "redacted_thinking",
436
+ redactedData: part.data
437
+ }
438
+ }
439
+ });
387
440
  break;
388
441
  }
389
442
  case "tool_use":
390
443
  {
391
- parts.push(AiResponse.ToolCallPart.fromUnknown({
392
- id: part.id,
393
- name: part.name,
394
- params: part.input
395
- }));
444
+ // When a `"json"` response format is requested, the JSON that we need
445
+ // will be returned by the tool call injected into the request
446
+ if (options.responseFormat.type === "json") {
447
+ parts.push({
448
+ type: "text",
449
+ text: JSON.stringify(part.input)
450
+ });
451
+ } else {
452
+ const providerTool = AnthropicTool.getProviderDefinedToolName(part.name);
453
+ const name = Predicate.isNotUndefined(providerTool) ? providerTool : part.name;
454
+ const providerName = Predicate.isNotUndefined(providerTool) ? part.name : undefined;
455
+ parts.push({
456
+ type: "tool-call",
457
+ id: part.id,
458
+ name,
459
+ params: part.input,
460
+ providerName,
461
+ providerExecuted: false
462
+ });
463
+ }
396
464
  break;
397
465
  }
398
- case "thinking":
466
+ case "server_tool_use":
399
467
  {
400
- parts.push(new AiResponse.ReasoningPart({
401
- reasoningText: part.thinking,
402
- signature: part.signature
403
- }, constDisableValidation));
468
+ const providerTool = AnthropicTool.getProviderDefinedToolName(part.name);
469
+ if (Predicate.isNotUndefined(providerTool)) {
470
+ parts.push({
471
+ type: "tool-call",
472
+ id: part.id,
473
+ name: providerTool,
474
+ params: part.input,
475
+ providerName: part.name,
476
+ providerExecuted: true
477
+ });
478
+ }
404
479
  break;
405
480
  }
406
- case "redacted_thinking":
481
+ case "code_execution_tool_result":
482
+ case "bash_code_execution_tool_result":
483
+ case "text_editor_code_execution_tool_result":
484
+ {
485
+ parts.push({
486
+ type: "tool-result",
487
+ id: part.tool_use_id,
488
+ name: "AnthropicCodeExecution",
489
+ result: part.content,
490
+ providerName: "code_execution",
491
+ providerExecuted: true
492
+ });
493
+ break;
494
+ }
495
+ case "web_search_tool_result":
407
496
  {
408
- parts.push(new AiResponse.RedactedReasoningPart({
409
- redactedText: part.data
410
- }, constDisableValidation));
497
+ parts.push({
498
+ type: "tool-result",
499
+ id: part.tool_use_id,
500
+ name: "AnthropicWebSearch",
501
+ result: part.content,
502
+ providerName: "web_search",
503
+ providerExecuted: true
504
+ });
411
505
  break;
412
506
  }
413
507
  }
414
508
  }
415
- const metadata = {};
416
- if (response.stop_sequence !== null) {
417
- metadata.stopSequence = response.stop_sequence;
418
- }
419
- parts.push(new AiResponse.FinishPart({
420
- // Anthropic always returns a non-null `stop_reason` for non-streaming responses
421
- reason: InternalUtilities.resolveFinishReason(response.stop_reason),
422
- usage: new AiResponse.Usage({
509
+ // Anthropic always returns a non-null `stop_reason` for non-streaming responses
510
+ const finishReason = InternalUtilities.resolveFinishReason(response.stop_reason, options.responseFormat.type === "json");
511
+ parts.push({
512
+ type: "finish",
513
+ reason: finishReason,
514
+ usage: {
423
515
  inputTokens: response.usage.input_tokens,
424
516
  outputTokens: response.usage.output_tokens,
425
517
  totalTokens: response.usage.input_tokens + response.usage.output_tokens,
426
- reasoningTokens: 0,
427
- cacheReadInputTokens: response.usage.cache_read_input_tokens ?? 0,
428
- cacheWriteInputTokens: response.usage.cache_creation_input_tokens ?? 0
429
- }),
430
- providerMetadata: {
431
- [InternalUtilities.ProviderMetadataKey]: metadata
518
+ cachedInputTokens: response.usage.cache_read_input_tokens ?? undefined
519
+ },
520
+ metadata: {
521
+ anthropic: {
522
+ usage: response.usage,
523
+ stopSequence: response.stop_sequence ?? undefined
524
+ }
525
+ }
526
+ });
527
+ return parts;
528
+ });
529
+ const makeStreamResponse = /*#__PURE__*/Effect.fnUntraced(function* (stream, options) {
530
+ const idGenerator = yield* IdGenerator.IdGenerator;
531
+ const citableDocuments = extractCitableDocuments(options.prompt);
532
+ // Setup all requisite state for the streaming response
533
+ let finishReason = "unknown";
534
+ const contentBlocks = {};
535
+ let blockType = undefined;
536
+ const usage = {
537
+ inputTokens: undefined,
538
+ outputTokens: undefined,
539
+ totalTokens: undefined
540
+ };
541
+ let metaUsage = undefined;
542
+ let stopSequence = undefined;
543
+ return stream.pipe(Stream.mapEffect(Effect.fnUntraced(function* (event) {
544
+ const parts = [];
545
+ switch (event.type) {
546
+ case "ping":
547
+ {
548
+ break;
549
+ }
550
+ case "message_start":
551
+ {
552
+ // Track usage metadata
553
+ usage.inputTokens = event.message.usage.input_tokens;
554
+ metaUsage = event.message.usage;
555
+ // Track response metadata
556
+ parts.push({
557
+ type: "response-metadata",
558
+ id: event.message.id,
559
+ modelId: event.message.model,
560
+ timestamp: DateTime.formatIso(yield* DateTime.now)
561
+ });
562
+ break;
563
+ }
564
+ case "message_delta":
565
+ {
566
+ // Track usage metadata
567
+ if (Predicate.isNotNullable(event.usage.output_tokens)) {
568
+ usage.outputTokens = event.usage.output_tokens;
569
+ }
570
+ usage.totalTokens = (usage.inputTokens ?? 0) + (event.usage.output_tokens ?? 0);
571
+ // Track stop sequence metadata
572
+ if (Predicate.isNotNullable(event.delta.stop_sequence)) {
573
+ stopSequence = event.delta.stop_sequence;
574
+ }
575
+ // Track the response finish reason
576
+ if (Predicate.isNotNullable(event.delta.stop_reason)) {
577
+ finishReason = InternalUtilities.resolveFinishReason(event.delta.stop_reason);
578
+ }
579
+ break;
580
+ }
581
+ case "message_stop":
582
+ {
583
+ parts.push({
584
+ type: "finish",
585
+ reason: finishReason,
586
+ usage,
587
+ metadata: {
588
+ anthropic: {
589
+ usage: metaUsage,
590
+ stopSequence
591
+ }
592
+ }
593
+ });
594
+ break;
595
+ }
596
+ case "content_block_start":
597
+ {
598
+ blockType = event.content_block.type;
599
+ switch (event.content_block.type) {
600
+ case "text":
601
+ {
602
+ contentBlocks[event.index] = {
603
+ type: "text"
604
+ };
605
+ parts.push({
606
+ type: "text-start",
607
+ id: event.index.toString()
608
+ });
609
+ break;
610
+ }
611
+ case "thinking":
612
+ {
613
+ contentBlocks[event.index] = {
614
+ type: "reasoning"
615
+ };
616
+ parts.push({
617
+ type: "reasoning-start",
618
+ id: event.index.toString()
619
+ });
620
+ break;
621
+ }
622
+ case "redacted_thinking":
623
+ {
624
+ contentBlocks[event.index] = {
625
+ type: "reasoning"
626
+ };
627
+ parts.push({
628
+ type: "reasoning-start",
629
+ id: event.index.toString(),
630
+ metadata: {
631
+ anthropic: {
632
+ type: "redacted_thinking",
633
+ redactedData: event.content_block.data
634
+ }
635
+ }
636
+ });
637
+ break;
638
+ }
639
+ case "tool_use":
640
+ {
641
+ const toolName = event.content_block.name;
642
+ const providerTool = AnthropicTool.getProviderDefinedToolName(toolName);
643
+ const name = Predicate.isNotUndefined(providerTool) ? providerTool : toolName;
644
+ const providerName = Predicate.isNotUndefined(providerTool) ? toolName : undefined;
645
+ contentBlocks[event.index] = {
646
+ type: "tool-call",
647
+ id: event.content_block.id,
648
+ name,
649
+ params: "",
650
+ providerName,
651
+ providerExecuted: false
652
+ };
653
+ parts.push({
654
+ type: "tool-params-start",
655
+ id: event.content_block.id,
656
+ name: toolName,
657
+ providerName,
658
+ providerExecuted: false
659
+ });
660
+ break;
661
+ }
662
+ case "server_tool_use":
663
+ {
664
+ const toolName = event.content_block.name;
665
+ const providerTool = AnthropicTool.getProviderDefinedToolName(toolName);
666
+ if (Predicate.isNotUndefined(providerTool)) {
667
+ contentBlocks[event.index] = {
668
+ type: "tool-call",
669
+ id: event.content_block.id,
670
+ name: providerTool,
671
+ params: "",
672
+ providerName: toolName,
673
+ providerExecuted: true
674
+ };
675
+ parts.push({
676
+ type: "tool-params-start",
677
+ id: event.content_block.id,
678
+ name: providerTool,
679
+ providerName: toolName,
680
+ providerExecuted: true
681
+ });
682
+ }
683
+ break;
684
+ }
685
+ case "code_execution_tool_result":
686
+ case "bash_code_execution_tool_result":
687
+ case "text_editor_code_execution_tool_result":
688
+ {
689
+ parts.push({
690
+ type: "tool-result",
691
+ id: event.content_block.tool_use_id,
692
+ name: "AnthropicCodeExecution",
693
+ result: event.content_block.content,
694
+ providerName: "code_execution",
695
+ providerExecuted: true
696
+ });
697
+ break;
698
+ }
699
+ case "web_search_tool_result":
700
+ {
701
+ parts.push({
702
+ type: "tool-result",
703
+ id: event.content_block.tool_use_id,
704
+ name: "AnthropicWebSearch",
705
+ result: event.content_block.content,
706
+ providerName: "web_search",
707
+ providerExecuted: true
708
+ });
709
+ break;
710
+ }
711
+ }
712
+ break;
713
+ }
714
+ case "content_block_delta":
715
+ {
716
+ switch (event.delta.type) {
717
+ case "text_delta":
718
+ {
719
+ parts.push({
720
+ type: "text-delta",
721
+ id: event.index.toString(),
722
+ delta: event.delta.text
723
+ });
724
+ break;
725
+ }
726
+ case "thinking_delta":
727
+ {
728
+ parts.push({
729
+ type: "reasoning-delta",
730
+ id: event.index.toString(),
731
+ delta: event.delta.thinking
732
+ });
733
+ break;
734
+ }
735
+ case "signature_delta":
736
+ {
737
+ if (blockType === "thinking") {
738
+ parts.push({
739
+ type: "reasoning-delta",
740
+ id: event.index.toString(),
741
+ delta: "",
742
+ metadata: {
743
+ anthropic: {
744
+ type: "thinking",
745
+ signature: event.delta.signature
746
+ }
747
+ }
748
+ });
749
+ }
750
+ break;
751
+ }
752
+ case "input_json_delta":
753
+ {
754
+ const contentBlock = contentBlocks[event.index];
755
+ const delta = event.delta.partial_json;
756
+ if (contentBlock.type === "tool-call") {
757
+ parts.push({
758
+ type: "tool-params-delta",
759
+ id: contentBlock.id,
760
+ delta
761
+ });
762
+ contentBlock.params += delta;
763
+ }
764
+ break;
765
+ }
766
+ case "citations_delta":
767
+ {
768
+ const citation = event.delta.citation;
769
+ const source = yield* processCitation(citation, citableDocuments, idGenerator);
770
+ if (Predicate.isNotUndefined(source)) {
771
+ parts.push(source);
772
+ }
773
+ }
774
+ }
775
+ break;
776
+ }
777
+ case "content_block_stop":
778
+ {
779
+ if (Predicate.isNotNullable(contentBlocks[event.index])) {
780
+ const contentBlock = contentBlocks[event.index];
781
+ switch (contentBlock.type) {
782
+ case "text":
783
+ {
784
+ parts.push({
785
+ type: "text-end",
786
+ id: event.index.toString()
787
+ });
788
+ break;
789
+ }
790
+ case "reasoning":
791
+ {
792
+ parts.push({
793
+ type: "reasoning-end",
794
+ id: event.index.toString()
795
+ });
796
+ break;
797
+ }
798
+ case "tool-call":
799
+ {
800
+ parts.push({
801
+ type: "tool-params-end",
802
+ id: contentBlock.id
803
+ });
804
+ // If the tool call has no parameters, an empty string is returned
805
+ const params = contentBlock.params.length === 0 ? "{}" : contentBlock.params;
806
+ parts.push({
807
+ type: "tool-call",
808
+ id: contentBlock.id,
809
+ name: contentBlock.name,
810
+ params: JSON.parse(params),
811
+ providerName: contentBlock.providerName,
812
+ providerExecuted: contentBlock.providerExecuted
813
+ });
814
+ break;
815
+ }
816
+ }
817
+ delete contentBlocks[event.index];
818
+ }
819
+ blockType = undefined;
820
+ break;
821
+ }
822
+ case "error":
823
+ {
824
+ parts.push({
825
+ type: "error",
826
+ error: event.error
827
+ });
828
+ break;
829
+ }
432
830
  }
433
- }, constDisableValidation));
434
- return new AiResponse.AiResponse({
435
- parts
436
- }, constDisableValidation);
831
+ return parts;
832
+ })), Stream.flattenIterables);
437
833
  });
834
+ // =============================================================================
835
+ // Telemetry
836
+ // =============================================================================
438
837
  const annotateRequest = (span, request) => {
439
838
  addGenAIAnnotations(span, {
440
839
  system: "anthropic",
@@ -451,7 +850,7 @@ const annotateRequest = (span, request) => {
451
850
  }
452
851
  });
453
852
  };
454
- const annotateChatResponse = (span, response) => {
853
+ const annotateResponse = (span, response) => {
455
854
  addGenAIAnnotations(span, {
456
855
  response: {
457
856
  id: response.id,
@@ -464,19 +863,336 @@ const annotateChatResponse = (span, response) => {
464
863
  }
465
864
  });
466
865
  };
467
- const annotateStreamResponse = (span, response) => {
468
- const metadataPart = response.parts.find(part => part._tag === "MetadataPart");
469
- const finishPart = response.parts.find(part => part._tag === "FinishPart");
470
- addGenAIAnnotations(span, {
471
- response: {
472
- id: metadataPart?.id,
473
- model: metadataPart?.model,
474
- finishReasons: finishPart?.reason ? [finishPart.reason] : undefined
475
- },
476
- usage: {
477
- inputTokens: finishPart?.usage.inputTokens,
478
- outputTokens: finishPart?.usage.outputTokens
866
+ const annotateStreamResponse = (span, part) => {
867
+ if (part.type === "response-metadata") {
868
+ addGenAIAnnotations(span, {
869
+ response: {
870
+ id: part.id,
871
+ model: part.modelId
872
+ }
873
+ });
874
+ }
875
+ if (part.type === "finish") {
876
+ addGenAIAnnotations(span, {
877
+ response: {
878
+ finishReasons: [part.reason]
879
+ },
880
+ usage: {
881
+ inputTokens: part.usage.inputTokens,
882
+ outputTokens: part.usage.outputTokens
883
+ }
884
+ });
885
+ }
886
+ };
887
+ /**
888
+ * A helper method which takes in large language model provider options from
889
+ * the base Effect AI SDK as well as Anthropic request configuration options
890
+ * and returns the prepared tools, tool choice, and Anthropic betas to include
891
+ * in a request.
892
+ *
893
+ * This method is primarily exposed for use by other Effect provider
894
+ * integrations which can utilize Anthropic models (i.e. Amazon Bedrock).
895
+ *
896
+ * @since 1.0.0
897
+ * @category Tool Calling
898
+ */
899
+ export const prepareTools = /*#__PURE__*/Effect.fnUntraced(function* (options, config) {
900
+ // Return immediately if no tools are in the toolkit or a tool choice of
901
+ // "none" was specified
902
+ if (options.tools.length === 0 || options.toolChoice === "none") {
903
+ return {
904
+ betas: new Set(),
905
+ tools: undefined,
906
+ toolChoice: undefined
907
+ };
908
+ }
909
+ const betas = new Set();
910
+ let tools = [];
911
+ let toolChoice = undefined;
912
+ // Convert the tools in the toolkit to the provider-defined format
913
+ for (const tool of options.tools) {
914
+ if (Tool.isUserDefined(tool)) {
915
+ tools.push({
916
+ name: tool.name,
917
+ description: Tool.getDescription(tool),
918
+ input_schema: Tool.getJsonSchema(tool)
919
+ });
479
920
  }
480
- });
921
+ if (Tool.isProviderDefined(tool)) {
922
+ switch (tool.id) {
923
+ case "anthropic.bash_20241022":
924
+ {
925
+ betas.add("computer-use-2024-10-22");
926
+ tools.push({
927
+ name: "bash",
928
+ type: "bash_20241022"
929
+ });
930
+ break;
931
+ }
932
+ case "anthropic.bash_20250124":
933
+ {
934
+ betas.add("computer-use-2025-01-24");
935
+ tools.push({
936
+ name: "bash",
937
+ type: "bash_20250124"
938
+ });
939
+ break;
940
+ }
941
+ case "anthropic.code_execution_20250522":
942
+ {
943
+ betas.add("code-execution-2025-05-22");
944
+ tools.push({
945
+ ...tool.args,
946
+ name: "code_execution",
947
+ type: "code_execution_2025522"
948
+ });
949
+ break;
950
+ }
951
+ case "anthropic.code_execution_20250825":
952
+ {
953
+ betas.add("code-execution-2025-08-25");
954
+ tools.push({
955
+ ...tool.args,
956
+ name: "code_execution",
957
+ type: "code_execution_20250825"
958
+ });
959
+ break;
960
+ }
961
+ case "anthropic.computer_use_20241022":
962
+ {
963
+ betas.add("computer-use-2025-10-22");
964
+ tools.push({
965
+ ...tool.args,
966
+ name: "computer",
967
+ type: "computer_20241022"
968
+ });
969
+ break;
970
+ }
971
+ case "anthropic.computer_use_20250124":
972
+ {
973
+ betas.add("computer-use-2025-01-24");
974
+ tools.push({
975
+ ...tool.args,
976
+ name: "computer",
977
+ type: "computer_20250124"
978
+ });
979
+ break;
980
+ }
981
+ case "anthropic.text_editor_20241022":
982
+ {
983
+ betas.add("computer-use-2024-10-22");
984
+ tools.push({
985
+ name: "str_replace_editor",
986
+ type: "text_editor_20241022"
987
+ });
988
+ break;
989
+ }
990
+ case "anthropic.text_editor_20250124":
991
+ {
992
+ betas.add("computer-use-2025-01-24");
993
+ tools.push({
994
+ name: "str_replace_editor",
995
+ type: "text_editor_20250124"
996
+ });
997
+ break;
998
+ }
999
+ case "anthropic.text_editor_20250429":
1000
+ {
1001
+ betas.add("computer-use-2025-01-24");
1002
+ tools.push({
1003
+ name: "str_replace_based_edit_tool",
1004
+ type: "text_editor_20250429"
1005
+ });
1006
+ break;
1007
+ }
1008
+ case "anthropic.text_editor_20250728":
1009
+ {
1010
+ tools.push({
1011
+ name: "str_replace_based_edit_tool",
1012
+ type: "text_editor_20250728"
1013
+ });
1014
+ break;
1015
+ }
1016
+ case "anthropic.web_search_20250305":
1017
+ {
1018
+ tools.push({
1019
+ ...tool.args,
1020
+ name: "web_search",
1021
+ type: "web_search_20250305"
1022
+ });
1023
+ break;
1024
+ }
1025
+ default:
1026
+ {
1027
+ return yield* new AiError.MalformedInput({
1028
+ module: "AnthropicLanguageModel",
1029
+ method: "prepareTools",
1030
+ description: `Received request to call unknown provider-defined tool '${tool.name}'`
1031
+ });
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+ // Convert the tool choice to the provider-defined format
1037
+ if (options.toolChoice === "auto") {
1038
+ toolChoice = {
1039
+ type: "auto",
1040
+ disable_parallel_tool_use: config.disableParallelToolCalls
1041
+ };
1042
+ } else if (options.toolChoice === "required") {
1043
+ toolChoice = {
1044
+ type: "any",
1045
+ disable_parallel_tool_use: config.disableParallelToolCalls
1046
+ };
1047
+ } else if ("tool" in options.toolChoice) {
1048
+ toolChoice = {
1049
+ type: "tool",
1050
+ name: options.toolChoice.tool,
1051
+ disable_parallel_tool_use: config.disableParallelToolCalls
1052
+ };
1053
+ } else {
1054
+ const allowedTools = new Set(options.toolChoice.oneOf);
1055
+ tools = tools.filter(tool => allowedTools.has(tool.name));
1056
+ toolChoice = {
1057
+ type: options.toolChoice.mode === "required" ? "any" : "auto",
1058
+ disable_parallel_tool_use: config.disableParallelToolCalls
1059
+ };
1060
+ }
1061
+ return {
1062
+ betas,
1063
+ tools,
1064
+ toolChoice
1065
+ };
1066
+ });
1067
+ const groupMessages = prompt => {
1068
+ const messages = [];
1069
+ let current = undefined;
1070
+ for (const message of prompt.content) {
1071
+ switch (message.role) {
1072
+ case "system":
1073
+ {
1074
+ if (current?.type !== "system") {
1075
+ current = {
1076
+ type: "system",
1077
+ messages: []
1078
+ };
1079
+ messages.push(current);
1080
+ }
1081
+ current.messages.push(message);
1082
+ break;
1083
+ }
1084
+ case "assistant":
1085
+ {
1086
+ if (current?.type !== "assistant") {
1087
+ current = {
1088
+ type: "assistant",
1089
+ messages: []
1090
+ };
1091
+ messages.push(current);
1092
+ }
1093
+ current.messages.push(message);
1094
+ break;
1095
+ }
1096
+ case "tool":
1097
+ case "user":
1098
+ {
1099
+ if (current?.type !== "user") {
1100
+ current = {
1101
+ type: "user",
1102
+ messages: []
1103
+ };
1104
+ messages.push(current);
1105
+ }
1106
+ current.messages.push(message);
1107
+ break;
1108
+ }
1109
+ }
1110
+ }
1111
+ return messages;
1112
+ };
1113
+ const isCitationPart = part => {
1114
+ if (part.type === "file" && (part.mediaType === "application/pdf" || part.mediaType === "text/plain")) {
1115
+ return part.options.anthropic?.citations?.enabled ?? false;
1116
+ }
1117
+ return false;
1118
+ };
1119
+ const extractCitableDocuments = prompt => {
1120
+ const citableDocuments = [];
1121
+ for (const message of prompt.content) {
1122
+ if (message.role === "user") {
1123
+ for (const part of message.content) {
1124
+ if (isCitationPart(part)) {
1125
+ citableDocuments.push({
1126
+ title: part.fileName ?? "Untitled Document",
1127
+ fileName: part.fileName,
1128
+ mediaType: part.mediaType
1129
+ });
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ return citableDocuments;
1135
+ };
1136
+ const getCacheControl = part => part.options.anthropic?.cacheControl;
1137
+ const getDocumentMetadata = part => {
1138
+ const options = part.options.anthropic;
1139
+ if (Predicate.isNotUndefined(options)) {
1140
+ return {
1141
+ title: options.documentTitle,
1142
+ context: options.documentContext
1143
+ };
1144
+ }
1145
+ return undefined;
481
1146
  };
1147
+ const shouldEnableCitations = part => part.options.anthropic?.citations?.enabled ?? false;
1148
+ const processCitation = /*#__PURE__*/Effect.fnUntraced(function* (citation, citableDocuments, idGenerator) {
1149
+ if (citation.type === "page_location" || citation.type === "char_location") {
1150
+ const citedDocument = citableDocuments[citation.document_index];
1151
+ if (Predicate.isNotUndefined(citedDocument)) {
1152
+ const id = yield* idGenerator.generateId();
1153
+ const metadata = citation.type === "char_location" ? {
1154
+ source: "document",
1155
+ type: citation.type,
1156
+ citedText: citation.cited_text,
1157
+ startCharIndex: citation.start_char_index,
1158
+ endCharIndex: citation.end_char_index
1159
+ } : {
1160
+ source: "document",
1161
+ type: citation.type,
1162
+ citedText: citation.cited_text,
1163
+ startPageNumber: citation.start_page_number,
1164
+ endPageNumber: citation.end_page_number
1165
+ };
1166
+ return {
1167
+ type: "source",
1168
+ sourceType: "document",
1169
+ id,
1170
+ mediaType: citedDocument.mediaType,
1171
+ title: citation.document_title ?? citedDocument.title,
1172
+ fileName: citedDocument.fileName,
1173
+ metadata: {
1174
+ anthropic: metadata
1175
+ }
1176
+ };
1177
+ }
1178
+ }
1179
+ if (citation.type === "web_search_result_location") {
1180
+ const id = yield* idGenerator.generateId();
1181
+ const metadata = {
1182
+ source: "url",
1183
+ citedText: citation.cited_text,
1184
+ encryptedIndex: citation.encrypted_index
1185
+ };
1186
+ return {
1187
+ type: "source",
1188
+ sourceType: "url",
1189
+ id,
1190
+ url: citation.url,
1191
+ title: citation.title ?? "Untitled",
1192
+ metadata: {
1193
+ anthropic: metadata
1194
+ }
1195
+ };
1196
+ }
1197
+ });
482
1198
  //# sourceMappingURL=AnthropicLanguageModel.js.map