@effect-uai/anthropic 0.1.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/LICENSE +21 -0
- package/README.md +43 -0
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/index.d.mts +438 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +649 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +54 -0
- package/src/Anthropic.ts +390 -0
- package/src/codec.ts +628 -0
- package/src/index.ts +4 -0
- package/src/models.ts +34 -0
- package/src/streamEvents.ts +221 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { Match, Schema } from "effect"
|
|
2
|
+
import { matchType } from "@effect-uai/core/Match"
|
|
3
|
+
import {
|
|
4
|
+
type Accumulator,
|
|
5
|
+
WireContentBlock,
|
|
6
|
+
type WireUsage,
|
|
7
|
+
appendInputJsonDelta,
|
|
8
|
+
appendSignatureDelta,
|
|
9
|
+
appendTextDelta,
|
|
10
|
+
appendThinkingDelta,
|
|
11
|
+
mergeUsage,
|
|
12
|
+
setStopReason,
|
|
13
|
+
startBlock,
|
|
14
|
+
} from "./codec.js"
|
|
15
|
+
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Wire schemas for the SSE events we map onto our `Accumulator`. Anthropic's
|
|
18
|
+
// streaming protocol uses named SSE events; we match on the inner JSON
|
|
19
|
+
// `type` field which mirrors the event name.
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
const WireMessageStartUsage = Schema.Struct({
|
|
23
|
+
input_tokens: Schema.optional(Schema.Number),
|
|
24
|
+
output_tokens: Schema.optional(Schema.Number),
|
|
25
|
+
cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
26
|
+
cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const MessageStart = Schema.Struct({
|
|
30
|
+
type: Schema.Literal("message_start"),
|
|
31
|
+
message: Schema.Struct({
|
|
32
|
+
id: Schema.optional(Schema.String),
|
|
33
|
+
usage: Schema.optional(WireMessageStartUsage),
|
|
34
|
+
}),
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const ContentBlockStart = Schema.Struct({
|
|
38
|
+
type: Schema.Literal("content_block_start"),
|
|
39
|
+
index: Schema.Number,
|
|
40
|
+
content_block: WireContentBlock,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const TextDelta = Schema.Struct({
|
|
44
|
+
type: Schema.Literal("text_delta"),
|
|
45
|
+
text: Schema.String,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const InputJsonDelta = Schema.Struct({
|
|
49
|
+
type: Schema.Literal("input_json_delta"),
|
|
50
|
+
partial_json: Schema.String,
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
const ThinkingDelta = Schema.Struct({
|
|
54
|
+
type: Schema.Literal("thinking_delta"),
|
|
55
|
+
thinking: Schema.String,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const SignatureDelta = Schema.Struct({
|
|
59
|
+
type: Schema.Literal("signature_delta"),
|
|
60
|
+
signature: Schema.String,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const Delta = Schema.Union([TextDelta, InputJsonDelta, ThinkingDelta, SignatureDelta])
|
|
64
|
+
|
|
65
|
+
const ContentBlockDelta = Schema.Struct({
|
|
66
|
+
type: Schema.Literal("content_block_delta"),
|
|
67
|
+
index: Schema.Number,
|
|
68
|
+
delta: Delta,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
const ContentBlockStop = Schema.Struct({
|
|
72
|
+
type: Schema.Literal("content_block_stop"),
|
|
73
|
+
index: Schema.Number,
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
const MessageDelta = Schema.Struct({
|
|
77
|
+
type: Schema.Literal("message_delta"),
|
|
78
|
+
delta: Schema.Struct({
|
|
79
|
+
stop_reason: Schema.optional(Schema.NullOr(Schema.String)),
|
|
80
|
+
stop_sequence: Schema.optional(Schema.NullOr(Schema.String)),
|
|
81
|
+
}),
|
|
82
|
+
usage: Schema.optional(WireMessageStartUsage),
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
const MessageStop = Schema.Struct({
|
|
86
|
+
type: Schema.Literal("message_stop"),
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const Ping = Schema.Struct({
|
|
90
|
+
type: Schema.Literal("ping"),
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const ErrorEvent = Schema.Struct({
|
|
94
|
+
type: Schema.Literal("error"),
|
|
95
|
+
error: Schema.optional(
|
|
96
|
+
Schema.Struct({
|
|
97
|
+
type: Schema.optional(Schema.String),
|
|
98
|
+
message: Schema.optional(Schema.String),
|
|
99
|
+
}),
|
|
100
|
+
),
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Catch-all variant for wire events that fail to decode against any known
|
|
105
|
+
* schema, plus events that fail to JSON-parse. The decoder never produces
|
|
106
|
+
* this directly - it's synthesized by `sseEventToProviderEvent` when
|
|
107
|
+
* `decodeKnown` fails.
|
|
108
|
+
*/
|
|
109
|
+
const Unknown = Schema.Struct({
|
|
110
|
+
type: Schema.Literal("_unknown"),
|
|
111
|
+
raw: Schema.Unknown,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Internal: union of variants we actually know how to decode from the wire.
|
|
116
|
+
* Used as the decode target; failures are caught and re-emitted as `Unknown`.
|
|
117
|
+
*/
|
|
118
|
+
export const KnownProviderEvent = Schema.Union([
|
|
119
|
+
MessageStart,
|
|
120
|
+
ContentBlockStart,
|
|
121
|
+
ContentBlockDelta,
|
|
122
|
+
ContentBlockStop,
|
|
123
|
+
MessageDelta,
|
|
124
|
+
MessageStop,
|
|
125
|
+
Ping,
|
|
126
|
+
ErrorEvent,
|
|
127
|
+
])
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Public: every event the native stream can emit. Discriminated on `type`.
|
|
131
|
+
* The `_unknown` branch closes the cardinality so downstream `Match.exhaustive`
|
|
132
|
+
* cannot silently miss a wire event we didn't model.
|
|
133
|
+
*/
|
|
134
|
+
export const ProviderEvent = Schema.Union([
|
|
135
|
+
MessageStart,
|
|
136
|
+
ContentBlockStart,
|
|
137
|
+
ContentBlockDelta,
|
|
138
|
+
ContentBlockStop,
|
|
139
|
+
MessageDelta,
|
|
140
|
+
MessageStop,
|
|
141
|
+
Ping,
|
|
142
|
+
ErrorEvent,
|
|
143
|
+
Unknown,
|
|
144
|
+
])
|
|
145
|
+
export type ProviderEvent = typeof ProviderEvent.Type
|
|
146
|
+
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
// Apply event → Accumulator. Pure: the mapping never fails. Caller wires
|
|
149
|
+
// this into `Stream.mapAccum` and emits text/turn_complete deltas based on
|
|
150
|
+
// the diff.
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
const mergeOptionalUsage = (acc: Accumulator, wire: WireUsage | undefined): Accumulator =>
|
|
154
|
+
wire === undefined ? acc : mergeUsage(acc, wire)
|
|
155
|
+
|
|
156
|
+
export const applyEvent = (acc: Accumulator, event: ProviderEvent): Accumulator =>
|
|
157
|
+
Match.value(event).pipe(
|
|
158
|
+
matchType("message_start", (e) => mergeOptionalUsage(acc, e.message.usage)),
|
|
159
|
+
matchType("content_block_start", (e) => startBlock(acc, e.index, e.content_block)),
|
|
160
|
+
matchType("content_block_delta", (e) =>
|
|
161
|
+
Match.value(e.delta).pipe(
|
|
162
|
+
matchType("text_delta", (d) => appendTextDelta(acc, e.index, d.text)),
|
|
163
|
+
matchType("input_json_delta", (d) => appendInputJsonDelta(acc, e.index, d.partial_json)),
|
|
164
|
+
matchType("thinking_delta", (d) => appendThinkingDelta(acc, e.index, d.thinking)),
|
|
165
|
+
matchType("signature_delta", (d) => appendSignatureDelta(acc, e.index, d.signature)),
|
|
166
|
+
Match.exhaustive,
|
|
167
|
+
),
|
|
168
|
+
),
|
|
169
|
+
matchType("content_block_stop", () => acc),
|
|
170
|
+
matchType("message_delta", (e) => {
|
|
171
|
+
const withUsage = mergeOptionalUsage(acc, e.usage)
|
|
172
|
+
const reason = e.delta.stop_reason
|
|
173
|
+
return reason === undefined || reason === null ? withUsage : setStopReason(withUsage, reason)
|
|
174
|
+
}),
|
|
175
|
+
matchType("message_stop", () => acc),
|
|
176
|
+
matchType("ping", () => acc),
|
|
177
|
+
matchType("error", () => acc),
|
|
178
|
+
// No silent drops: unknown wire events flow through `streamNative` but
|
|
179
|
+
// produce no accumulator change. Step 3 (canonical `other` event) will
|
|
180
|
+
// also forward them to `TurnEvent`.
|
|
181
|
+
matchType("_unknown", () => acc),
|
|
182
|
+
Match.exhaustive,
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
// Helpers for producing TurnEvent from a step.
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
export const isTextDeltaEvent = (
|
|
190
|
+
event: ProviderEvent,
|
|
191
|
+
): event is Extract<
|
|
192
|
+
ProviderEvent,
|
|
193
|
+
{ type: "content_block_delta"; delta: { type: "text_delta" } }
|
|
194
|
+
> => event.type === "content_block_delta" && event.delta.type === "text_delta"
|
|
195
|
+
|
|
196
|
+
export const isThinkingDeltaEvent = (
|
|
197
|
+
event: ProviderEvent,
|
|
198
|
+
): event is Extract<
|
|
199
|
+
ProviderEvent,
|
|
200
|
+
{ type: "content_block_delta"; delta: { type: "thinking_delta" } }
|
|
201
|
+
> => event.type === "content_block_delta" && event.delta.type === "thinking_delta"
|
|
202
|
+
|
|
203
|
+
export const isInputJsonDeltaEvent = (
|
|
204
|
+
event: ProviderEvent,
|
|
205
|
+
): event is Extract<
|
|
206
|
+
ProviderEvent,
|
|
207
|
+
{ type: "content_block_delta"; delta: { type: "input_json_delta" } }
|
|
208
|
+
> => event.type === "content_block_delta" && event.delta.type === "input_json_delta"
|
|
209
|
+
|
|
210
|
+
export const isToolUseStartEvent = (
|
|
211
|
+
event: ProviderEvent,
|
|
212
|
+
): event is Extract<
|
|
213
|
+
ProviderEvent,
|
|
214
|
+
{ type: "content_block_start"; content_block: { type: "tool_use" } }
|
|
215
|
+
> => event.type === "content_block_start" && event.content_block.type === "tool_use"
|
|
216
|
+
|
|
217
|
+
export const isMessageStop = (event: ProviderEvent): boolean => event.type === "message_stop"
|
|
218
|
+
|
|
219
|
+
export const isErrorEvent = (
|
|
220
|
+
event: ProviderEvent,
|
|
221
|
+
): event is Extract<ProviderEvent, { type: "error" }> => event.type === "error"
|