@effect-uai/anthropic 0.1.0 → 0.3.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/Anthropic.d.mts +94 -0
- package/dist/Anthropic.d.mts.map +1 -0
- package/dist/Anthropic.mjs +226 -0
- package/dist/Anthropic.mjs.map +1 -0
- package/dist/codec.d.mts +142 -0
- package/dist/codec.d.mts.map +1 -0
- package/dist/codec.mjs +354 -0
- package/dist/codec.mjs.map +1 -0
- package/dist/index.d.mts +5 -438
- package/dist/index.mjs +5 -649
- package/dist/models.d.mts +25 -0
- package/dist/models.d.mts.map +1 -0
- package/dist/models.mjs +1 -0
- package/dist/streamEvents.d.mts +196 -0
- package/dist/streamEvents.d.mts.map +1 -0
- package/dist/streamEvents.mjs +152 -0
- package/dist/streamEvents.mjs.map +1 -0
- package/package.json +16 -12
- package/src/Anthropic.ts +49 -54
- package/src/codec.ts +104 -109
- package/src/index.ts +2 -2
- package/src/streamEvents.ts +27 -25
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,649 +1,5 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
|
|
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"}
|
package/dist/models.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|