@effect/ai-anthropic 0.0.1
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/AnthropicClient/package.json +6 -0
- package/AnthropicCompletions/package.json +6 -0
- package/AnthropicConfig/package.json +6 -0
- package/AnthropicTokenizer/package.json +6 -0
- package/Generated/package.json +6 -0
- package/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/cjs/AnthropicClient.js +213 -0
- package/dist/cjs/AnthropicClient.js.map +1 -0
- package/dist/cjs/AnthropicCompletions.js +290 -0
- package/dist/cjs/AnthropicCompletions.js.map +1 -0
- package/dist/cjs/AnthropicConfig.js +31 -0
- package/dist/cjs/AnthropicConfig.js.map +1 -0
- package/dist/cjs/AnthropicTokenizer.js +50 -0
- package/dist/cjs/AnthropicTokenizer.js.map +1 -0
- package/dist/cjs/Generated.js +1510 -0
- package/dist/cjs/Generated.js.map +1 -0
- package/dist/cjs/index.js +19 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/dts/AnthropicClient.d.ts +126 -0
- package/dist/dts/AnthropicClient.d.ts.map +1 -0
- package/dist/dts/AnthropicCompletions.d.ts +25 -0
- package/dist/dts/AnthropicCompletions.d.ts.map +1 -0
- package/dist/dts/AnthropicConfig.d.ts +39 -0
- package/dist/dts/AnthropicConfig.d.ts.map +1 -0
- package/dist/dts/AnthropicTokenizer.d.ts +8 -0
- package/dist/dts/AnthropicTokenizer.d.ts.map +1 -0
- package/dist/dts/Generated.d.ts +3937 -0
- package/dist/dts/Generated.d.ts.map +1 -0
- package/dist/dts/index.d.ts +21 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/esm/AnthropicClient.js +199 -0
- package/dist/esm/AnthropicClient.js.map +1 -0
- package/dist/esm/AnthropicCompletions.js +279 -0
- package/dist/esm/AnthropicCompletions.js.map +1 -0
- package/dist/esm/AnthropicConfig.js +22 -0
- package/dist/esm/AnthropicConfig.js.map +1 -0
- package/dist/esm/AnthropicTokenizer.js +41 -0
- package/dist/esm/AnthropicTokenizer.js.map +1 -0
- package/dist/esm/Generated.js +1273 -0
- package/dist/esm/Generated.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/package.json +79 -0
- package/src/AnthropicClient.ts +415 -0
- package/src/AnthropicCompletions.ts +352 -0
- package/src/AnthropicConfig.ts +76 -0
- package/src/AnthropicTokenizer.ts +52 -0
- package/src/Generated.ts +1811 -0
- package/src/index.ts +24 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as AiError from "@effect/ai/AiError"
|
|
5
|
+
import * as AiResponse from "@effect/ai/AiResponse"
|
|
6
|
+
import * as AiRole from "@effect/ai/AiRole"
|
|
7
|
+
import * as Sse from "@effect/experimental/Sse"
|
|
8
|
+
import * as HttpBody from "@effect/platform/HttpBody"
|
|
9
|
+
import * as HttpClient from "@effect/platform/HttpClient"
|
|
10
|
+
import type * as HttpClientError from "@effect/platform/HttpClientError"
|
|
11
|
+
import * as HttpClientRequest from "@effect/platform/HttpClientRequest"
|
|
12
|
+
import * as Chunk from "effect/Chunk"
|
|
13
|
+
import * as Config from "effect/Config"
|
|
14
|
+
import type { ConfigError } from "effect/ConfigError"
|
|
15
|
+
import * as Context from "effect/Context"
|
|
16
|
+
import * as Data from "effect/Data"
|
|
17
|
+
import * as Effect from "effect/Effect"
|
|
18
|
+
import { identity } from "effect/Function"
|
|
19
|
+
import * as Layer from "effect/Layer"
|
|
20
|
+
import * as Option from "effect/Option"
|
|
21
|
+
import * as Redacted from "effect/Redacted"
|
|
22
|
+
import * as Stream from "effect/Stream"
|
|
23
|
+
import type { Mutable } from "effect/Types"
|
|
24
|
+
import { AnthropicConfig } from "./AnthropicConfig.js"
|
|
25
|
+
import * as Generated from "./Generated.js"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 1.0.0
|
|
29
|
+
* @category tags
|
|
30
|
+
*/
|
|
31
|
+
export class AnthropicClient extends Context.Tag(
|
|
32
|
+
"@effect/ai-openai/AnthropicClient"
|
|
33
|
+
)<AnthropicClient, AnthropicClient.Service>() {}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* @since 1.0.0
|
|
37
|
+
* @category models
|
|
38
|
+
*/
|
|
39
|
+
export declare namespace AnthropicClient {
|
|
40
|
+
/**
|
|
41
|
+
* @since 1.0.0
|
|
42
|
+
* @category models
|
|
43
|
+
*/
|
|
44
|
+
export interface Service {
|
|
45
|
+
readonly client: Generated.Client
|
|
46
|
+
readonly streamRequest: <A>(
|
|
47
|
+
request: HttpClientRequest.HttpClientRequest
|
|
48
|
+
) => Stream.Stream<A, HttpClientError.HttpClientError>
|
|
49
|
+
readonly stream: (
|
|
50
|
+
request: StreamCompletionRequest
|
|
51
|
+
) => Stream.Stream<StreamChunk, HttpClientError.HttpClientError>
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @since 1.0.0
|
|
57
|
+
* @category constructors
|
|
58
|
+
*/
|
|
59
|
+
export const make = (options: {
|
|
60
|
+
readonly apiKey?: Redacted.Redacted | undefined
|
|
61
|
+
readonly apiUrl?: string | undefined
|
|
62
|
+
readonly anthropicVersion?: string
|
|
63
|
+
readonly organizationId?: Redacted.Redacted | undefined
|
|
64
|
+
readonly projectId?: Redacted.Redacted | undefined
|
|
65
|
+
readonly transformClient?: (
|
|
66
|
+
client: HttpClient.HttpClient
|
|
67
|
+
) => HttpClient.HttpClient
|
|
68
|
+
}): Effect.Effect<AnthropicClient.Service, never, HttpClient.HttpClient> =>
|
|
69
|
+
Effect.gen(function*() {
|
|
70
|
+
const httpClient = (yield* HttpClient.HttpClient).pipe(
|
|
71
|
+
HttpClient.mapRequest((request) =>
|
|
72
|
+
request.pipe(
|
|
73
|
+
HttpClientRequest.prependUrl(options.apiUrl ?? "https://api.anthropic.com"),
|
|
74
|
+
options.apiKey ? HttpClientRequest.setHeader("x-api-key", Redacted.value(options.apiKey)) : identity,
|
|
75
|
+
HttpClientRequest.setHeader("anthropic-version", options.anthropicVersion ?? "2023-06-01"),
|
|
76
|
+
HttpClientRequest.acceptJson
|
|
77
|
+
)
|
|
78
|
+
),
|
|
79
|
+
options.transformClient ? options.transformClient : identity
|
|
80
|
+
)
|
|
81
|
+
const httpClientOk = HttpClient.filterStatusOk(httpClient)
|
|
82
|
+
const client = Generated.make(httpClient, {
|
|
83
|
+
transformClient: (client) =>
|
|
84
|
+
AnthropicConfig.getOrUndefined.pipe(
|
|
85
|
+
Effect.map((config) => config?.transformClient ? config.transformClient(client) : client)
|
|
86
|
+
)
|
|
87
|
+
})
|
|
88
|
+
const streamRequest = <A = unknown>(
|
|
89
|
+
request: HttpClientRequest.HttpClientRequest
|
|
90
|
+
) =>
|
|
91
|
+
httpClientOk.execute(request).pipe(
|
|
92
|
+
Effect.map((r) => r.stream),
|
|
93
|
+
Stream.unwrapScoped,
|
|
94
|
+
Stream.decodeText(),
|
|
95
|
+
Stream.pipeThroughChannel(Sse.makeChannel()),
|
|
96
|
+
Stream.takeWhile((event) => event.event !== "message_stop"),
|
|
97
|
+
Stream.map((event) => JSON.parse(event.data) as A)
|
|
98
|
+
)
|
|
99
|
+
const stream = (request: StreamCompletionRequest) =>
|
|
100
|
+
Stream.suspend(() => {
|
|
101
|
+
const usage: Mutable<Partial<UsagePart>> = { _tag: "Usage" }
|
|
102
|
+
return streamRequest<MessageStreamEvent>(
|
|
103
|
+
HttpClientRequest.post("/v1/messages", {
|
|
104
|
+
body: HttpBody.unsafeJson({ ...request, stream: true })
|
|
105
|
+
})
|
|
106
|
+
).pipe(
|
|
107
|
+
Stream.mapAccumEffect(new Map<number, ContentPart | ToolCallPart>(), (acc, chunk) => {
|
|
108
|
+
const parts: Array<StreamChunkPart> = []
|
|
109
|
+
switch (chunk.type) {
|
|
110
|
+
case "message_start": {
|
|
111
|
+
usage.id = chunk.message.id
|
|
112
|
+
usage.model = chunk.message.model
|
|
113
|
+
usage.inputTokens = chunk.message.usage.input_tokens
|
|
114
|
+
break
|
|
115
|
+
}
|
|
116
|
+
case "message_delta": {
|
|
117
|
+
usage.finishReasons = [chunk.delta.stop_reason]
|
|
118
|
+
usage.outputTokens = chunk.usage.output_tokens
|
|
119
|
+
parts.push(usage as UsagePart)
|
|
120
|
+
break
|
|
121
|
+
}
|
|
122
|
+
case "message_stop": {
|
|
123
|
+
break
|
|
124
|
+
}
|
|
125
|
+
case "content_block_start": {
|
|
126
|
+
const content = chunk.content_block
|
|
127
|
+
if (content.type === "tool_use") {
|
|
128
|
+
acc.set(chunk.index, {
|
|
129
|
+
_tag: "ToolCall",
|
|
130
|
+
id: content.id,
|
|
131
|
+
name: content.name,
|
|
132
|
+
arguments: ""
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
break
|
|
136
|
+
}
|
|
137
|
+
case "content_block_delta": {
|
|
138
|
+
switch (chunk.delta.type) {
|
|
139
|
+
// TODO: add support for citations (?)
|
|
140
|
+
case "citations_delta": {
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
case "input_json_delta": {
|
|
144
|
+
const toolCall = acc.get(chunk.index) as ToolCallPart
|
|
145
|
+
acc.set(chunk.index, {
|
|
146
|
+
...toolCall,
|
|
147
|
+
arguments: toolCall.arguments + chunk.delta.partial_json
|
|
148
|
+
})
|
|
149
|
+
break
|
|
150
|
+
}
|
|
151
|
+
case "text_delta": {
|
|
152
|
+
parts.push({
|
|
153
|
+
_tag: "Content",
|
|
154
|
+
content: chunk.delta.text
|
|
155
|
+
})
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
case "content_block_stop": {
|
|
162
|
+
if (acc.has(chunk.index)) {
|
|
163
|
+
const toolCall = acc.get(chunk.index) as ToolCallPart
|
|
164
|
+
try {
|
|
165
|
+
const args = JSON.parse(toolCall.arguments as string)
|
|
166
|
+
parts.push({
|
|
167
|
+
_tag: "ToolCall",
|
|
168
|
+
id: toolCall.id,
|
|
169
|
+
name: toolCall.name,
|
|
170
|
+
arguments: args
|
|
171
|
+
})
|
|
172
|
+
// eslint-disable-next-line no-empty
|
|
173
|
+
} catch {}
|
|
174
|
+
}
|
|
175
|
+
break
|
|
176
|
+
}
|
|
177
|
+
case "error": {
|
|
178
|
+
return Effect.die(
|
|
179
|
+
new AiError.AiError({
|
|
180
|
+
module: "AnthropicClient",
|
|
181
|
+
method: "stream",
|
|
182
|
+
description: `${chunk.error.type}: ${chunk.error.message}`
|
|
183
|
+
})
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return Effect.succeed([
|
|
188
|
+
acc,
|
|
189
|
+
parts.length === 0
|
|
190
|
+
? Option.none()
|
|
191
|
+
: Option.some(new StreamChunk({ parts }))
|
|
192
|
+
])
|
|
193
|
+
}),
|
|
194
|
+
Stream.filterMap(identity)
|
|
195
|
+
)
|
|
196
|
+
})
|
|
197
|
+
return AnthropicClient.of({ client, streamRequest, stream })
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @since 1.0.0
|
|
202
|
+
* @category layers
|
|
203
|
+
*/
|
|
204
|
+
export const layer = (options: {
|
|
205
|
+
readonly apiKey?: Redacted.Redacted | undefined
|
|
206
|
+
readonly apiUrl?: string | undefined
|
|
207
|
+
readonly anthropicVersion?: string
|
|
208
|
+
readonly transformClient?: (
|
|
209
|
+
client: HttpClient.HttpClient
|
|
210
|
+
) => HttpClient.HttpClient
|
|
211
|
+
}): Layer.Layer<AnthropicClient, never, HttpClient.HttpClient> => Layer.effect(AnthropicClient, make(options))
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @since 1.0.0
|
|
215
|
+
* @category layers
|
|
216
|
+
*/
|
|
217
|
+
export const layerConfig = (
|
|
218
|
+
options: Config.Config.Wrap<{
|
|
219
|
+
readonly apiKey?: Redacted.Redacted | undefined
|
|
220
|
+
readonly apiUrl?: string | undefined
|
|
221
|
+
readonly anthropicVersion?: string
|
|
222
|
+
readonly transformClient?: (
|
|
223
|
+
client: HttpClient.HttpClient
|
|
224
|
+
) => HttpClient.HttpClient
|
|
225
|
+
}>
|
|
226
|
+
): Layer.Layer<AnthropicClient, ConfigError, HttpClient.HttpClient> =>
|
|
227
|
+
Config.unwrap(options).pipe(
|
|
228
|
+
Effect.flatMap(make),
|
|
229
|
+
Layer.effect(AnthropicClient)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @since 1.0.0
|
|
234
|
+
* @category models
|
|
235
|
+
*/
|
|
236
|
+
export type StreamCompletionRequest = Omit<
|
|
237
|
+
typeof Generated.CreateMessageParams.Encoded,
|
|
238
|
+
"stream"
|
|
239
|
+
>
|
|
240
|
+
|
|
241
|
+
type MessageStreamEvent =
|
|
242
|
+
| ErrorEvent
|
|
243
|
+
| MessageStartEvent
|
|
244
|
+
| MessageDeltaEvent
|
|
245
|
+
| MessageStopEvent
|
|
246
|
+
| ContentBlockStartEvent
|
|
247
|
+
| ContentBlockDeltaEvent
|
|
248
|
+
| ContentBlockStopEvent
|
|
249
|
+
|
|
250
|
+
interface MessageStartEvent {
|
|
251
|
+
readonly type: "message_start"
|
|
252
|
+
readonly message: typeof Generated.Message.Encoded
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
interface MessageDeltaEvent {
|
|
256
|
+
readonly type: "message_delta"
|
|
257
|
+
readonly delta: {
|
|
258
|
+
readonly stop_reason:
|
|
259
|
+
| "end_turn"
|
|
260
|
+
| "max_tokens"
|
|
261
|
+
| "stop_sequence"
|
|
262
|
+
| "tool_use"
|
|
263
|
+
readonly stop_sequence: string | null
|
|
264
|
+
}
|
|
265
|
+
readonly usage: {
|
|
266
|
+
readonly output_tokens: number
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
interface MessageStopEvent {
|
|
271
|
+
readonly type: "message_stop"
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
interface ContentBlockStartEvent {
|
|
275
|
+
readonly type: "content_block_start"
|
|
276
|
+
readonly index: number
|
|
277
|
+
readonly content_block:
|
|
278
|
+
| typeof Generated.ResponseTextBlock.Encoded
|
|
279
|
+
| typeof Generated.ResponseToolUseBlock.Encoded
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface ContentBlockDeltaEvent {
|
|
283
|
+
readonly type: "content_block_delta"
|
|
284
|
+
readonly index: number
|
|
285
|
+
readonly delta:
|
|
286
|
+
| CitationsDelta
|
|
287
|
+
| InputJsonContentBlockDelta
|
|
288
|
+
| TextContentBlockDelta
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
interface CitationsDelta {
|
|
292
|
+
readonly type: "citations_delta"
|
|
293
|
+
readonly citation: NonNullable<
|
|
294
|
+
(typeof Generated.ResponseTextBlock.Encoded)["citations"]
|
|
295
|
+
>[number]
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
interface InputJsonContentBlockDelta {
|
|
299
|
+
readonly type: "input_json_delta"
|
|
300
|
+
readonly partial_json: string
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
interface TextContentBlockDelta {
|
|
304
|
+
readonly type: "text_delta"
|
|
305
|
+
readonly text: string
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
interface ContentBlockStopEvent {
|
|
309
|
+
readonly type: "content_block_stop"
|
|
310
|
+
readonly index: number
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
interface ErrorEvent {
|
|
314
|
+
readonly type: "error"
|
|
315
|
+
readonly error: {
|
|
316
|
+
readonly type:
|
|
317
|
+
| "api_error"
|
|
318
|
+
| "authentication_error"
|
|
319
|
+
| "invalid_request_error"
|
|
320
|
+
| "not_found_error"
|
|
321
|
+
| "overloaded_error"
|
|
322
|
+
| "permission_error"
|
|
323
|
+
| "rate_limit_error"
|
|
324
|
+
| "request_too_large"
|
|
325
|
+
readonly message: string
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @since 1.0.0
|
|
331
|
+
* @category models
|
|
332
|
+
*/
|
|
333
|
+
export class StreamChunk extends Data.Class<{
|
|
334
|
+
readonly parts: Array<StreamChunkPart>
|
|
335
|
+
}> {
|
|
336
|
+
/**
|
|
337
|
+
* @since 1.0.0
|
|
338
|
+
*/
|
|
339
|
+
get text(): Option.Option<string> {
|
|
340
|
+
return this.parts[0]?._tag === "Content"
|
|
341
|
+
? Option.some(this.parts[0].content)
|
|
342
|
+
: Option.none()
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* @since 1.0.0
|
|
346
|
+
*/
|
|
347
|
+
get asAiResponse(): AiResponse.AiResponse {
|
|
348
|
+
if (this.parts.length === 0) {
|
|
349
|
+
return AiResponse.AiResponse.fromText({
|
|
350
|
+
role: AiRole.model,
|
|
351
|
+
content: ""
|
|
352
|
+
})
|
|
353
|
+
}
|
|
354
|
+
const part = this.parts[0]
|
|
355
|
+
switch (part._tag) {
|
|
356
|
+
case "Content":
|
|
357
|
+
return AiResponse.AiResponse.fromText({
|
|
358
|
+
role: AiRole.model,
|
|
359
|
+
content: part.content
|
|
360
|
+
})
|
|
361
|
+
case "ToolCall":
|
|
362
|
+
return new AiResponse.AiResponse({
|
|
363
|
+
role: AiRole.model,
|
|
364
|
+
parts: Chunk.of(
|
|
365
|
+
AiResponse.ToolCallPart.fromUnknown({
|
|
366
|
+
id: part.id,
|
|
367
|
+
name: part.name,
|
|
368
|
+
params: part.arguments
|
|
369
|
+
})
|
|
370
|
+
)
|
|
371
|
+
})
|
|
372
|
+
case "Usage":
|
|
373
|
+
return AiResponse.AiResponse.empty
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* @since 1.0.0
|
|
380
|
+
* @category models
|
|
381
|
+
*/
|
|
382
|
+
export type StreamChunkPart = ContentPart | ToolCallPart | UsagePart
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* @since 1.0.0
|
|
386
|
+
* @category models
|
|
387
|
+
*/
|
|
388
|
+
export interface ContentPart {
|
|
389
|
+
readonly _tag: "Content"
|
|
390
|
+
readonly content: string
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* @since 1.0.0
|
|
395
|
+
* @category models
|
|
396
|
+
*/
|
|
397
|
+
export interface ToolCallPart {
|
|
398
|
+
readonly _tag: "ToolCall"
|
|
399
|
+
readonly id: string
|
|
400
|
+
readonly name: string
|
|
401
|
+
readonly arguments: unknown
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* @since 1.0.0
|
|
406
|
+
* @category models
|
|
407
|
+
*/
|
|
408
|
+
export interface UsagePart {
|
|
409
|
+
readonly _tag: "Usage"
|
|
410
|
+
readonly id: string
|
|
411
|
+
readonly model: string
|
|
412
|
+
readonly inputTokens: number
|
|
413
|
+
readonly outputTokens: number
|
|
414
|
+
readonly finishReasons: ReadonlyArray<string>
|
|
415
|
+
}
|