@effect-uai/anthropic 0.2.0 → 0.4.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.
package/dist/index.mjs CHANGED
@@ -1,649 +1,5 @@
1
- import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
2
- import { Array, Context, Effect, Layer, Match, Option, Order, Redacted, Result, Schema, Stream, pipe } from "effect";
3
- import { HttpClient, HttpClientRequest } from "effect/unstable/http";
4
- import * as AiError from "@effect-uai/core/AiError";
5
- import { LanguageModel } from "@effect-uai/core/LanguageModel";
6
- import { matchType } from "@effect-uai/core/Match";
7
- import * as SSE from "@effect-uai/core/SSE";
8
- import { JsonParseError } from "@effect-uai/core/JSONL";
9
- //#region src/codec.ts
10
- var codec_exports = /* @__PURE__ */ __exportAll({
11
- WireContentBlock: () => WireContentBlock,
12
- accumulatorToTurn: () => accumulatorToTurn,
13
- appendInputJsonDelta: () => appendInputJsonDelta,
14
- appendSignatureDelta: () => appendSignatureDelta,
15
- appendTextDelta: () => appendTextDelta,
16
- appendThinkingDelta: () => appendThinkingDelta,
17
- buildRequestBody: () => buildRequestBody,
18
- emptyAccumulator: () => emptyAccumulator,
19
- mergeUsage: () => mergeUsage,
20
- setStopReason: () => setStopReason,
21
- startBlock: () => startBlock
22
- });
23
- const WireTextBlock = Schema.Struct({
24
- type: Schema.Literal("text"),
25
- text: Schema.String
26
- });
27
- const WireToolUseBlock = Schema.Struct({
28
- type: Schema.Literal("tool_use"),
29
- id: Schema.String,
30
- name: Schema.String,
31
- input: Schema.Unknown
32
- });
33
- const WireThinkingBlock = Schema.Struct({
34
- type: Schema.Literal("thinking"),
35
- thinking: Schema.String,
36
- signature: Schema.optional(Schema.String)
37
- });
38
- const WireRedactedThinkingBlock = Schema.Struct({
39
- type: Schema.Literal("redacted_thinking"),
40
- data: Schema.String
41
- });
42
- const WireContentBlock = Schema.Union([
43
- WireTextBlock,
44
- WireToolUseBlock,
45
- WireThinkingBlock,
46
- WireRedactedThinkingBlock
47
- ]);
48
- Schema.Struct({
49
- input_tokens: Schema.optional(Schema.Number),
50
- output_tokens: Schema.optional(Schema.Number),
51
- cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),
52
- cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number))
53
- });
54
- const blockText = Match.type().pipe(matchType("input_text", (b) => b.text), matchType("input_image", () => ""), matchType("output_text", (b) => b.text), matchType("refusal", (b) => b.text), Match.exhaustive);
55
- const messageText = (message) => message.content.map(blockText).join("");
56
- const userContentBlock = (block) => Match.value(block).pipe(matchType("input_text", (b) => b.text.length === 0 ? Result.failVoid : Result.succeed({
57
- type: "text",
58
- text: b.text
59
- })), matchType("input_image", (b) => Result.succeed({
60
- type: "image",
61
- source: b.source._tag === "url" ? {
62
- type: "url",
63
- url: b.source.url
64
- } : {
65
- type: "base64",
66
- media_type: b.source.media_type,
67
- data: b.source.data
68
- }
69
- })), matchType("output_text", () => Result.failVoid), matchType("refusal", () => Result.failVoid), Match.exhaustive);
70
- const parseJson = (s) => Result.try({
71
- try: () => JSON.parse(s),
72
- catch: (cause) => new JsonParseError({
73
- line: s,
74
- cause
75
- })
76
- });
77
- const roleBucket = (item) => Match.value(item).pipe(matchType("message", (m) => m.role), matchType("function_call", () => "assistant"), matchType("function_call_output", () => "user"), matchType("reasoning", () => "assistant"), Match.exhaustive);
78
- const itemToUserBlocks = (item) => Match.value(item).pipe(matchType("message", (m) => m.role === "user" ? pipe(m.content, Array.filterMap(userContentBlock)) : []), matchType("function_call", () => []), matchType("function_call_output", (o) => [{
79
- type: "tool_result",
80
- tool_use_id: o.call_id,
81
- content: o.output
82
- }]), matchType("reasoning", () => []), Match.exhaustive);
83
- const itemToAssistantBlocks = (item) => Match.value(item).pipe(matchType("message", (m) => {
84
- const text = messageText(m);
85
- return Result.succeed(m.role === "assistant" && text.length > 0 ? [{
86
- type: "text",
87
- text
88
- }] : []);
89
- }), matchType("function_call", (f) => pipe(parseJson(f.arguments), Result.map((input) => [{
90
- type: "tool_use",
91
- id: f.call_id,
92
- name: f.name,
93
- input
94
- }]))), matchType("function_call_output", () => Result.succeed([])), matchType("reasoning", (r) => {
95
- const blocks = r.summary !== void 0 ? [{
96
- type: "thinking",
97
- thinking: r.summary,
98
- ...r.signature !== void 0 && { signature: r.signature }
99
- }] : r.signature !== void 0 ? [{
100
- type: "redacted_thinking",
101
- data: r.signature
102
- }] : [];
103
- return Result.succeed(blocks);
104
- }), Match.exhaustive);
105
- const flushAcc = (acc) => Option.match(acc.currentRole, {
106
- onNone: () => acc.messages,
107
- onSome: (role) => role === "user" && acc.userBuf.length > 0 ? [...acc.messages, {
108
- role: "user",
109
- content: acc.userBuf
110
- }] : role === "assistant" && acc.assistantBuf.length > 0 ? [...acc.messages, {
111
- role: "assistant",
112
- content: acc.assistantBuf
113
- }] : acc.messages
114
- });
115
- const appendUser = (acc, blocks) => blocks.length === 0 ? acc : Option.isSome(acc.currentRole) && acc.currentRole.value === "user" ? {
116
- ...acc,
117
- userBuf: [...acc.userBuf, ...blocks]
118
- } : {
119
- messages: flushAcc(acc),
120
- currentRole: Option.some("user"),
121
- userBuf: blocks,
122
- assistantBuf: []
123
- };
124
- const appendAssistant = (acc, blocks) => blocks.length === 0 ? acc : Option.isSome(acc.currentRole) && acc.currentRole.value === "assistant" ? {
125
- ...acc,
126
- assistantBuf: [...acc.assistantBuf, ...blocks]
127
- } : {
128
- messages: flushAcc(acc),
129
- currentRole: Option.some("assistant"),
130
- userBuf: [],
131
- assistantBuf: blocks
132
- };
133
- const groupStep = (acc, item) => {
134
- const bucket = roleBucket(item);
135
- if (bucket === "system") return Result.succeed(acc);
136
- if (bucket === "user") return Result.succeed(appendUser(acc, itemToUserBlocks(item)));
137
- return pipe(itemToAssistantBlocks(item), Result.map((blocks) => appendAssistant(acc, blocks)));
138
- };
139
- /**
140
- * Group consecutive same-role items into Anthropic-shaped messages.
141
- * Anthropic requires strict user/assistant alternation; consecutive items
142
- * from the same role are folded into one message's `content`. Fails if any
143
- * `function_call.arguments` is not valid JSON, since Anthropic's wire shape
144
- * requires an object input.
145
- */
146
- const groupedMessages = (history) => {
147
- const initial = Result.succeed({
148
- messages: [],
149
- currentRole: Option.none(),
150
- userBuf: [],
151
- assistantBuf: []
152
- });
153
- return pipe(Array.reduce(history, initial, (acc, item) => Result.flatMap(acc, (a) => groupStep(a, item))), Result.map(flushAcc));
154
- };
155
- const isSystemMessage = (item) => item.type === "message" && item.role === "system";
156
- const systemFromHistory = (history) => {
157
- const texts = pipe(history, Array.filterMap((item) => isSystemMessage(item) ? Result.succeed(messageText(item)) : Result.failVoid), Array.filter((s) => s.length > 0));
158
- return texts.length === 0 ? Option.none() : Option.some(texts.join("\n"));
159
- };
160
- const buildRequestBody = (params) => pipe(groupedMessages(params.history), Result.map((messages) => ({
161
- model: params.model,
162
- messages,
163
- max_tokens: params.maxTokens,
164
- ...Option.match(systemFromHistory(params.history), {
165
- onNone: () => ({}),
166
- onSome: (system) => ({ system })
167
- }),
168
- ...Option.match(params.temperature, {
169
- onNone: () => ({}),
170
- onSome: (temperature) => ({ temperature })
171
- }),
172
- ...Option.match(params.topP, {
173
- onNone: () => ({}),
174
- onSome: (top_p) => ({ top_p })
175
- }),
176
- ...Option.match(params.topK, {
177
- onNone: () => ({}),
178
- onSome: (top_k) => ({ top_k })
179
- }),
180
- ...Option.match(params.stopSequences, {
181
- onNone: () => ({}),
182
- onSome: (stop_sequences) => ({ stop_sequences })
183
- }),
184
- ...Option.match(params.thinking, {
185
- onNone: () => ({}),
186
- onSome: (thinking) => ({ thinking })
187
- }),
188
- ...Option.match(params.tools, {
189
- onNone: () => ({}),
190
- onSome: (tools) => ({ tools })
191
- }),
192
- ...Option.match(params.toolChoice, {
193
- onNone: () => ({}),
194
- onSome: (tool_choice) => ({ tool_choice })
195
- }),
196
- ...Option.match(params.userId, {
197
- onNone: () => ({}),
198
- onSome: (user_id) => ({ metadata: { user_id } })
199
- }),
200
- ...Option.match(params.outputConfig, {
201
- onNone: () => ({}),
202
- onSome: (output_config) => ({ output_config })
203
- }),
204
- stream: true
205
- })));
206
- const emptyBlock = (type) => ({
207
- type,
208
- text: "",
209
- inputJson: "",
210
- thinking: "",
211
- signature: "",
212
- id: Option.none(),
213
- name: Option.none(),
214
- redactedData: Option.none()
215
- });
216
- const emptyAccumulator = {
217
- blocks: {},
218
- stopReason: Option.none(),
219
- usage: {}
220
- };
221
- const replaceBlock = (acc, index, block) => ({
222
- ...acc,
223
- blocks: {
224
- ...acc.blocks,
225
- [index]: block
226
- }
227
- });
228
- const updateBlock = (acc, index, patch) => replaceBlock(acc, index, patch(acc.blocks[index] ?? emptyBlock("text")));
229
- const startBlock = (acc, index, block) => Match.value(block).pipe(matchType("text", () => replaceBlock(acc, index, emptyBlock("text"))), matchType("tool_use", (b) => replaceBlock(acc, index, {
230
- ...emptyBlock("tool_use"),
231
- id: Option.some(b.id),
232
- name: Option.some(b.name),
233
- inputJson: typeof b.input === "string" ? b.input : ""
234
- })), matchType("thinking", () => replaceBlock(acc, index, emptyBlock("thinking"))), matchType("redacted_thinking", (b) => replaceBlock(acc, index, {
235
- ...emptyBlock("redacted_thinking"),
236
- redactedData: Option.some(b.data)
237
- })), Match.exhaustive);
238
- const appendTextDelta = (acc, index, text) => updateBlock(acc, index, (b) => ({
239
- ...b,
240
- text: b.text + text
241
- }));
242
- const appendInputJsonDelta = (acc, index, partial) => updateBlock(acc, index, (b) => ({
243
- ...b,
244
- inputJson: b.inputJson + partial
245
- }));
246
- const appendThinkingDelta = (acc, index, thinking) => updateBlock(acc, index, (b) => ({
247
- ...b,
248
- thinking: b.thinking + thinking
249
- }));
250
- const appendSignatureDelta = (acc, index, signature) => updateBlock(acc, index, (b) => ({
251
- ...b,
252
- signature: b.signature + signature
253
- }));
254
- const setStopReason = (acc, reason) => ({
255
- ...acc,
256
- stopReason: Option.some(reason)
257
- });
258
- const cachedFromWire = (wire) => Option.fromNullishOr(wire.cache_read_input_tokens);
259
- const mergeUsage = (acc, wire) => {
260
- const cached = cachedFromWire(wire);
261
- const usage = {
262
- ...acc.usage,
263
- ...wire.input_tokens !== void 0 && { input_tokens: wire.input_tokens },
264
- ...wire.output_tokens !== void 0 && { output_tokens: wire.output_tokens },
265
- ...wire.input_tokens !== void 0 && wire.output_tokens !== void 0 && { total_tokens: wire.input_tokens + wire.output_tokens },
266
- ...Option.match(cached, {
267
- onNone: () => ({}),
268
- onSome: (cached_tokens) => ({ input_tokens_details: { cached_tokens } })
269
- })
270
- };
271
- return {
272
- ...acc,
273
- usage
274
- };
275
- };
276
- const stopReasonFromAnthropic = (reason) => Option.match(reason, {
277
- onNone: () => "stop",
278
- onSome: (r) => Match.value(r).pipe(Match.when("tool_use", () => "tool_calls"), Match.when("max_tokens", () => "max_tokens"), Match.orElse(() => "stop"))
279
- });
280
- const blocksByIndex = (acc) => pipe(Object.keys(acc.blocks), Array.map((k) => Number(k)), Array.sort(Order.Number), Array.map((i) => acc.blocks[i]));
281
- const blockToItems = (block) => Match.value(block.type).pipe(Match.when("text", () => block.text.length === 0 ? [] : [{
282
- type: "message",
283
- role: "assistant",
284
- content: [{
285
- type: "output_text",
286
- text: block.text
287
- }]
288
- }]), Match.when("tool_use", () => [{
289
- type: "function_call",
290
- call_id: Option.getOrElse(block.id, () => ""),
291
- name: Option.getOrElse(block.name, () => ""),
292
- arguments: block.inputJson
293
- }]), Match.when("thinking", () => [{
294
- type: "reasoning",
295
- ...block.thinking.length > 0 && { summary: block.thinking },
296
- ...block.signature.length > 0 && { signature: block.signature }
297
- }]), Match.when("redacted_thinking", () => [{
298
- type: "reasoning",
299
- ...Option.match(block.redactedData, {
300
- onNone: () => ({}),
301
- onSome: (signature) => ({ signature })
302
- })
303
- }]), Match.exhaustive);
304
- const mergeStep = (acc, item) => {
305
- const last = Array.last(acc.out);
306
- if (Option.isSome(last) && last.value.type === "message" && last.value.role === "assistant" && item.type === "message" && item.role === "assistant") {
307
- const merged = {
308
- ...last.value,
309
- content: [...last.value.content, ...item.content]
310
- };
311
- return { out: [...acc.out.slice(0, -1), merged] };
312
- }
313
- return { out: [...acc.out, item] };
314
- };
315
- const mergeAdjacentAssistantText = (items) => Array.reduce(items, { out: [] }, mergeStep).out;
316
- const accumulatorToTurn = (acc) => ({
317
- items: pipe(blocksByIndex(acc), Array.flatMap(blockToItems), mergeAdjacentAssistantText),
318
- usage: acc.usage,
319
- stop_reason: stopReasonFromAnthropic(acc.stopReason)
320
- });
321
- //#endregion
322
- //#region src/streamEvents.ts
323
- var streamEvents_exports = /* @__PURE__ */ __exportAll({
324
- KnownProviderEvent: () => KnownProviderEvent,
325
- ProviderEvent: () => ProviderEvent,
326
- applyEvent: () => applyEvent,
327
- isErrorEvent: () => isErrorEvent,
328
- isInputJsonDeltaEvent: () => isInputJsonDeltaEvent,
329
- isMessageStop: () => isMessageStop,
330
- isTextDeltaEvent: () => isTextDeltaEvent,
331
- isThinkingDeltaEvent: () => isThinkingDeltaEvent,
332
- isToolUseStartEvent: () => isToolUseStartEvent
333
- });
334
- const WireMessageStartUsage = Schema.Struct({
335
- input_tokens: Schema.optional(Schema.Number),
336
- output_tokens: Schema.optional(Schema.Number),
337
- cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),
338
- cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number))
339
- });
340
- const MessageStart = Schema.Struct({
341
- type: Schema.Literal("message_start"),
342
- message: Schema.Struct({
343
- id: Schema.optional(Schema.String),
344
- usage: Schema.optional(WireMessageStartUsage)
345
- })
346
- });
347
- const ContentBlockStart = Schema.Struct({
348
- type: Schema.Literal("content_block_start"),
349
- index: Schema.Number,
350
- content_block: WireContentBlock
351
- });
352
- const TextDelta = Schema.Struct({
353
- type: Schema.Literal("text_delta"),
354
- text: Schema.String
355
- });
356
- const InputJsonDelta = Schema.Struct({
357
- type: Schema.Literal("input_json_delta"),
358
- partial_json: Schema.String
359
- });
360
- const ThinkingDelta = Schema.Struct({
361
- type: Schema.Literal("thinking_delta"),
362
- thinking: Schema.String
363
- });
364
- const SignatureDelta = Schema.Struct({
365
- type: Schema.Literal("signature_delta"),
366
- signature: Schema.String
367
- });
368
- const Delta = Schema.Union([
369
- TextDelta,
370
- InputJsonDelta,
371
- ThinkingDelta,
372
- SignatureDelta
373
- ]);
374
- const ContentBlockDelta = Schema.Struct({
375
- type: Schema.Literal("content_block_delta"),
376
- index: Schema.Number,
377
- delta: Delta
378
- });
379
- const ContentBlockStop = Schema.Struct({
380
- type: Schema.Literal("content_block_stop"),
381
- index: Schema.Number
382
- });
383
- const MessageDelta = Schema.Struct({
384
- type: Schema.Literal("message_delta"),
385
- delta: Schema.Struct({
386
- stop_reason: Schema.optional(Schema.NullOr(Schema.String)),
387
- stop_sequence: Schema.optional(Schema.NullOr(Schema.String))
388
- }),
389
- usage: Schema.optional(WireMessageStartUsage)
390
- });
391
- const MessageStop = Schema.Struct({ type: Schema.Literal("message_stop") });
392
- const Ping = Schema.Struct({ type: Schema.Literal("ping") });
393
- const ErrorEvent = Schema.Struct({
394
- type: Schema.Literal("error"),
395
- error: Schema.optional(Schema.Struct({
396
- type: Schema.optional(Schema.String),
397
- message: Schema.optional(Schema.String)
398
- }))
399
- });
400
- /**
401
- * Catch-all variant for wire events that fail to decode against any known
402
- * schema, plus events that fail to JSON-parse. The decoder never produces
403
- * this directly - it's synthesized by `sseEventToProviderEvent` when
404
- * `decodeKnown` fails.
405
- */
406
- const Unknown = Schema.Struct({
407
- type: Schema.Literal("_unknown"),
408
- raw: Schema.Unknown
409
- });
410
- /**
411
- * Internal: union of variants we actually know how to decode from the wire.
412
- * Used as the decode target; failures are caught and re-emitted as `Unknown`.
413
- */
414
- const KnownProviderEvent = Schema.Union([
415
- MessageStart,
416
- ContentBlockStart,
417
- ContentBlockDelta,
418
- ContentBlockStop,
419
- MessageDelta,
420
- MessageStop,
421
- Ping,
422
- ErrorEvent
423
- ]);
424
- /**
425
- * Public: every event the native stream can emit. Discriminated on `type`.
426
- * The `_unknown` branch closes the cardinality so downstream `Match.exhaustive`
427
- * cannot silently miss a wire event we didn't model.
428
- */
429
- const ProviderEvent = Schema.Union([
430
- MessageStart,
431
- ContentBlockStart,
432
- ContentBlockDelta,
433
- ContentBlockStop,
434
- MessageDelta,
435
- MessageStop,
436
- Ping,
437
- ErrorEvent,
438
- Unknown
439
- ]);
440
- const mergeOptionalUsage = (acc, wire) => wire === void 0 ? acc : mergeUsage(acc, wire);
441
- const applyEvent = (acc, event) => Match.value(event).pipe(matchType("message_start", (e) => mergeOptionalUsage(acc, e.message.usage)), matchType("content_block_start", (e) => startBlock(acc, e.index, e.content_block)), matchType("content_block_delta", (e) => Match.value(e.delta).pipe(matchType("text_delta", (d) => appendTextDelta(acc, e.index, d.text)), matchType("input_json_delta", (d) => appendInputJsonDelta(acc, e.index, d.partial_json)), matchType("thinking_delta", (d) => appendThinkingDelta(acc, e.index, d.thinking)), matchType("signature_delta", (d) => appendSignatureDelta(acc, e.index, d.signature)), Match.exhaustive)), matchType("content_block_stop", () => acc), matchType("message_delta", (e) => {
442
- const withUsage = mergeOptionalUsage(acc, e.usage);
443
- const reason = e.delta.stop_reason;
444
- return reason === void 0 || reason === null ? withUsage : setStopReason(withUsage, reason);
445
- }), matchType("message_stop", () => acc), matchType("ping", () => acc), matchType("error", () => acc), matchType("_unknown", () => acc), Match.exhaustive);
446
- const isTextDeltaEvent = (event) => event.type === "content_block_delta" && event.delta.type === "text_delta";
447
- const isThinkingDeltaEvent = (event) => event.type === "content_block_delta" && event.delta.type === "thinking_delta";
448
- const isInputJsonDeltaEvent = (event) => event.type === "content_block_delta" && event.delta.type === "input_json_delta";
449
- const isToolUseStartEvent = (event) => event.type === "content_block_start" && event.content_block.type === "tool_use";
450
- const isMessageStop = (event) => event.type === "message_stop";
451
- const isErrorEvent = (event) => event.type === "error";
452
- //#endregion
453
- //#region src/Anthropic.ts
454
- /**
455
- * Provider-typed service tag. Yield this when you want Anthropic-specific
456
- * options (`topK`, `stopSequences`, `thinking`); yield the generic
457
- * `LanguageModel` tag for provider-portable code. Both are registered by
458
- * `layer`.
459
- */
460
- var Anthropic = class extends Context.Service()("@betalyra/effect-uai/providers/anthropic/Anthropic") {};
461
- const ANTHROPIC_VERSION = "2023-06-01";
462
- const STRUCTURED_OUTPUTS_BETA = "structured-outputs-2025-11-13";
463
- const FALLBACK_MAX_TOKENS = 4096;
464
- const outputConfig = (request) => pipe(Option.fromUndefinedOr(request.structured), Option.map((format) => ({ format: {
465
- type: "json_schema",
466
- schema: format.schema["~standard"].jsonSchema.input({ target: "draft-2020-12" })
467
- } })));
468
- const resolvedMaxTokens = (cfg, request) => request.maxOutputTokens ?? cfg.defaultMaxTokens ?? FALLBACK_MAX_TOKENS;
469
- const toolDescriptors = (request) => request.tools !== void 0 && request.tools.length > 0 ? Option.some(request.tools.map((t) => ({
470
- name: t.name,
471
- description: t.description,
472
- input_schema: t.inputSchema
473
- }))) : Option.none();
474
- const toolChoiceWire = (request) => pipe(Option.fromUndefinedOr(request.toolChoice), Option.map((choice) => choice === "auto" ? { type: "auto" } : choice === "required" ? { type: "any" } : choice === "none" ? { type: "none" } : {
475
- type: "tool",
476
- name: choice.name
477
- }));
478
- const decodeKnown = Schema.decodeUnknownEffect(KnownProviderEvent);
479
- const makeUnknown = (raw) => ({
480
- type: "_unknown",
481
- raw
482
- });
483
- /**
484
- * Parse one SSE event's `data` payload into a typed `ProviderEvent`. Never
485
- * fails: JSON-parse and schema-decode failures both produce a synthesized
486
- * `_unknown` event so consumers of `streamNative` never silently miss a
487
- * wire event we didn't model.
488
- */
489
- const sseEventToProviderEvent = (ev) => Effect.try({
490
- try: () => JSON.parse(ev.data),
491
- catch: () => ev.data
492
- }).pipe(Effect.flatMap((parsed) => decodeKnown(parsed).pipe(Effect.orElseSucceed(() => makeUnknown(parsed)))), Effect.orElseSucceed(() => makeUnknown(ev.data)));
493
- const deltasFromEvent = (next, event) => Match.value(event).pipe(matchType("content_block_start", (e) => e.content_block.type === "tool_use" ? [{
494
- type: "tool_call_start",
495
- call_id: e.content_block.id,
496
- name: e.content_block.name
497
- }] : []), matchType("content_block_delta", (e) => Match.value(e.delta).pipe(matchType("text_delta", (d) => [{
498
- type: "text_delta",
499
- text: d.text
500
- }]), matchType("thinking_delta", (d) => [{
501
- type: "reasoning_delta",
502
- text: d.thinking,
503
- kind: "trace"
504
- }]), matchType("input_json_delta", (d) => {
505
- const block = next.blocks[e.index];
506
- if (block === void 0) return [];
507
- const callId = Option.getOrElse(block.id, () => "");
508
- return callId.length === 0 ? [] : [{
509
- type: "tool_call_args_delta",
510
- call_id: callId,
511
- delta: d.partial_json
512
- }];
513
- }), matchType("signature_delta", () => []), Match.exhaustive)), matchType("message_start", (e) => e.message.usage === void 0 ? [] : [{
514
- type: "usage_update",
515
- usage: next.usage
516
- }]), matchType("message_delta", (e) => e.usage === void 0 ? [] : [{
517
- type: "usage_update",
518
- usage: next.usage
519
- }]), matchType("message_stop", () => [{
520
- type: "turn_complete",
521
- turn: accumulatorToTurn(next)
522
- }]), matchType("content_block_stop", () => []), matchType("ping", () => []), matchType("error", () => []), matchType("_unknown", () => []), Match.exhaustive);
523
- const httpStatusError = (status, body) => {
524
- const provider = "anthropic";
525
- const raw = body;
526
- if (status === 429) return new AiError.RateLimited({
527
- provider,
528
- raw
529
- });
530
- if (status === 408 || status === 504) return new AiError.Timeout({
531
- provider,
532
- raw
533
- });
534
- if (status === 401) return new AiError.AuthFailed({
535
- provider,
536
- subtype: "auth",
537
- raw
538
- });
539
- if (status === 403) return new AiError.AuthFailed({
540
- provider,
541
- subtype: "permission",
542
- raw
543
- });
544
- if (status === 402) return new AiError.AuthFailed({
545
- provider,
546
- subtype: "billing",
547
- raw
548
- });
549
- if (status === 413) return new AiError.ContextLengthExceeded({
550
- provider,
551
- raw
552
- });
553
- if (status === 529) return new AiError.Unavailable({
554
- provider,
555
- status,
556
- raw
557
- });
558
- if (status >= 500) return new AiError.Unavailable({
559
- provider,
560
- status,
561
- raw
562
- });
563
- return new AiError.InvalidRequest({
564
- provider,
565
- raw
566
- });
567
- };
568
- const buildNativeStream = (cfg) => {
569
- const url = `${cfg.baseUrl ?? "https://api.anthropic.com"}/v1/messages`;
570
- return (request) => Stream.unwrap(Effect.gen(function* () {
571
- const structured = outputConfig(request);
572
- const bodyResult = buildRequestBody({
573
- model: request.model,
574
- history: request.history,
575
- maxTokens: resolvedMaxTokens(cfg, request),
576
- temperature: Option.fromUndefinedOr(request.temperature),
577
- topP: Option.fromUndefinedOr(request.topP),
578
- topK: Option.fromUndefinedOr(request.topK),
579
- stopSequences: Option.fromUndefinedOr(request.stopSequences),
580
- thinking: Option.fromUndefinedOr(request.thinking),
581
- tools: toolDescriptors(request),
582
- toolChoice: toolChoiceWire(request),
583
- userId: Option.fromUndefinedOr(request.user),
584
- outputConfig: structured
585
- });
586
- const body = yield* Result.match(bodyResult, {
587
- onFailure: (cause) => Effect.fail(new AiError.InvalidRequest({
588
- provider: "anthropic",
589
- param: "input.function_call.arguments",
590
- raw: cause
591
- })),
592
- onSuccess: (b) => Effect.succeed(b)
593
- });
594
- const client = yield* HttpClient.HttpClient;
595
- const baseRequest = HttpClientRequest.post(url).pipe(HttpClientRequest.setHeader("x-api-key", Redacted.value(cfg.apiKey)), HttpClientRequest.setHeader("anthropic-version", ANTHROPIC_VERSION), HttpClientRequest.bodyJsonUnsafe(body), HttpClientRequest.accept("text/event-stream"));
596
- const httpRequest = Option.isSome(structured) ? baseRequest.pipe(HttpClientRequest.setHeader("anthropic-beta", STRUCTURED_OUTPUTS_BETA)) : baseRequest;
597
- const response = yield* client.execute(httpRequest).pipe(Effect.mapError((cause) => new AiError.Unavailable({
598
- provider: "anthropic",
599
- raw: cause
600
- })));
601
- if (response.status >= 400) {
602
- const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
603
- return Stream.fail(httpStatusError(response.status, text));
604
- }
605
- return response.stream.pipe(Stream.mapError((cause) => new AiError.Unavailable({
606
- provider: "anthropic",
607
- raw: cause
608
- })), SSE.fromBytes, Stream.mapEffect(sseEventToProviderEvent), Stream.flatMap((event) => event.type === "error" ? Stream.fail(new AiError.Unavailable({
609
- provider: "anthropic",
610
- raw: event
611
- })) : Stream.succeed(event)));
612
- }));
613
- };
614
- /**
615
- * Project a stream of native `ProviderEvent`s into canonical `TurnEvent`s.
616
- * Threads a fresh `Accumulator` per stream so tool-call lookup and
617
- * `accumulatorToTurn` assembly work correctly across the run.
618
- */
619
- const toCanonical = (s) => s.pipe(Stream.mapAccum(() => emptyAccumulator, (acc, event) => {
620
- const next = applyEvent(acc, event);
621
- return [next, deltasFromEvent(next, event)];
622
- }));
623
- /**
624
- * Build an `AnthropicService` value. For Layer-based setup, prefer `layer`.
625
- */
626
- const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => {
627
- const streamNative = (request) => buildNativeStream(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client));
628
- return {
629
- streamNative,
630
- streamTurn: (request) => toCanonical(streamNative(request)),
631
- toCanonical
632
- };
633
- });
634
- /**
635
- * Layer that registers both the provider-specific `Anthropic` tag and the
636
- * generic `LanguageModel` tag, sharing one underlying implementation.
637
- *
638
- * The generic tag accepts `CommonRequest`; the typed tag accepts the full
639
- * `AnthropicRequest` surface.
640
- */
641
- const layer = (cfg) => {
642
- const typed = Layer.effect(Anthropic, make(cfg));
643
- const generic = Layer.effect(LanguageModel, Effect.map(make(cfg), (s) => ({ streamTurn: (request) => s.streamTurn(request) })));
644
- return Layer.merge(typed, generic);
645
- };
646
- //#endregion
647
- export { Anthropic, codec_exports as codec, layer, make, streamEvents_exports as streamEvents, toCanonical };
648
-
649
- //# sourceMappingURL=index.mjs.map
1
+ import { t as codec_exports } from "./codec.mjs";
2
+ import { t as streamEvents_exports } from "./streamEvents.mjs";
3
+ import { t as Anthropic_exports } from "./Anthropic.mjs";
4
+ import "./models.mjs";
5
+ export { Anthropic_exports as Anthropic, codec_exports as codec, streamEvents_exports as streamEvents };
@@ -0,0 +1,25 @@
1
+ //#region src/models.d.ts
2
+ /**
3
+ * Known Anthropic model identifiers usable via the Messages API (as of
4
+ * April 2026). The `(string & {})` tail keeps autocomplete on the literals
5
+ * while still accepting any string, so newly-released models work without
6
+ * an SDK update.
7
+ *
8
+ * Reference: https://platform.claude.com/docs/en/docs/about-claude/models
9
+ *
10
+ * Latest tier:
11
+ * - `claude-opus-4-7` - most capable; agentic coding focus. Adaptive
12
+ * thinking only (no extended thinking).
13
+ * - `claude-sonnet-4-6` - speed + intelligence balance. Extended +
14
+ * adaptive thinking.
15
+ * - `claude-haiku-4-5-20251001` (alias `claude-haiku-4-5`) - fastest;
16
+ * extended thinking.
17
+ *
18
+ * Deprecated and retiring 2026-06-15:
19
+ * `claude-sonnet-4-20250514` (`claude-sonnet-4-0`),
20
+ * `claude-opus-4-20250514` (`claude-opus-4-0`).
21
+ */
22
+ type AnthropicModel = "claude-opus-4-7" | "claude-sonnet-4-6" | "claude-haiku-4-5" | "claude-haiku-4-5-20251001" | "claude-opus-4-6" | "claude-sonnet-4-5" | "claude-sonnet-4-5-20250929" | "claude-opus-4-5" | "claude-opus-4-5-20251101" | "claude-opus-4-1" | "claude-opus-4-1-20250805" | (string & {});
23
+ //#endregion
24
+ export { AnthropicModel };
25
+ //# sourceMappingURL=models.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAoBA;;;;;;;;;;;;;;;;;;;KAAY,cAAA"}
@@ -0,0 +1 @@
1
+ export {};