@effect/ai-anthropic 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/cjs/AnthropicClient.js +3 -2
  2. package/dist/cjs/AnthropicClient.js.map +1 -1
  3. package/dist/cjs/AnthropicCompletions.js +60 -7
  4. package/dist/cjs/AnthropicCompletions.js.map +1 -1
  5. package/dist/cjs/AnthropicConfig.js.map +1 -1
  6. package/dist/cjs/AnthropicTokenizer.js +6 -2
  7. package/dist/cjs/AnthropicTokenizer.js.map +1 -1
  8. package/dist/cjs/Generated.js +1 -1
  9. package/dist/cjs/Generated.js.map +1 -1
  10. package/dist/dts/AnthropicClient.d.ts +2 -1
  11. package/dist/dts/AnthropicClient.d.ts.map +1 -1
  12. package/dist/dts/AnthropicCompletions.d.ts +51 -1
  13. package/dist/dts/AnthropicCompletions.d.ts.map +1 -1
  14. package/dist/dts/AnthropicConfig.d.ts +1 -3
  15. package/dist/dts/AnthropicConfig.d.ts.map +1 -1
  16. package/dist/dts/AnthropicTokenizer.d.ts +5 -0
  17. package/dist/dts/AnthropicTokenizer.d.ts.map +1 -1
  18. package/dist/dts/Generated.d.ts +100 -1056
  19. package/dist/dts/Generated.d.ts.map +1 -1
  20. package/dist/esm/AnthropicClient.js +3 -2
  21. package/dist/esm/AnthropicClient.js.map +1 -1
  22. package/dist/esm/AnthropicCompletions.js +57 -6
  23. package/dist/esm/AnthropicCompletions.js.map +1 -1
  24. package/dist/esm/AnthropicConfig.js.map +1 -1
  25. package/dist/esm/AnthropicTokenizer.js +5 -1
  26. package/dist/esm/AnthropicTokenizer.js.map +1 -1
  27. package/dist/esm/Generated.js +1 -1
  28. package/dist/esm/Generated.js.map +1 -1
  29. package/package.json +5 -5
  30. package/src/AnthropicClient.ts +8 -2
  31. package/src/AnthropicCompletions.ts +197 -90
  32. package/src/AnthropicConfig.ts +1 -12
  33. package/src/AnthropicTokenizer.ts +5 -1
  34. package/src/Generated.ts +6 -7
@@ -3,22 +3,25 @@
3
3
  */
4
4
  import { AiError } from "@effect/ai/AiError"
5
5
  import type * as AiInput from "@effect/ai/AiInput"
6
+ import * as AiModel from "@effect/ai/AiModel"
6
7
  import * as AiResponse from "@effect/ai/AiResponse"
7
8
  import * as AiRole from "@effect/ai/AiRole"
8
9
  import { addGenAIAnnotations } from "@effect/ai/AiTelemetry"
9
10
  import * as Completions from "@effect/ai/Completions"
10
- import type * as Tokenizer from "@effect/ai/Tokenizer"
11
+ import * as Tokenizer from "@effect/ai/Tokenizer"
11
12
  import * as Arr from "effect/Array"
12
13
  import * as Chunk from "effect/Chunk"
14
+ import * as Context from "effect/Context"
13
15
  import * as Effect from "effect/Effect"
16
+ import { dual } from "effect/Function"
14
17
  import * as Layer from "effect/Layer"
15
18
  import * as Option from "effect/Option"
16
19
  import * as Predicate from "effect/Predicate"
17
20
  import * as Stream from "effect/Stream"
18
21
  import type { Span } from "effect/Tracer"
22
+ import type { Simplify } from "effect/Types"
19
23
  import type { StreamChunk } from "./AnthropicClient.js"
20
24
  import { AnthropicClient } from "./AnthropicClient.js"
21
- import { AnthropicConfig } from "./AnthropicConfig.js"
22
25
  import * as AnthropicTokenizer from "./AnthropicTokenizer.js"
23
26
  import type * as Generated from "./Generated.js"
24
27
 
@@ -28,97 +31,166 @@ import type * as Generated from "./Generated.js"
28
31
  */
29
32
  export type Model = typeof Generated.ModelEnum.Encoded
30
33
 
31
- const make = (options: { readonly model: (string & {}) | Model }) =>
32
- Effect.gen(function*() {
33
- const client = yield* AnthropicClient
34
- const config = yield* AnthropicConfig.getOrUndefined
34
+ // =============================================================================
35
+ // Configuration
36
+ // =============================================================================
35
37
 
36
- const makeRequest = ({
37
- input,
38
- required,
39
- system,
40
- tools
41
- }: Completions.CompletionOptions) => {
42
- const useStructured = tools.length === 1 && tools[0].structured
43
- return Effect.map(
44
- Effect.context<never>(),
45
- (context): typeof Generated.CreateMessageParams.Encoded => ({
46
- model: options.model,
47
- // TODO: re-evaluate a better way to do this
48
- max_tokens: 4096,
49
- ...config,
50
- ...context.unsafeMap.get(AnthropicConfig.key),
51
- system: Option.getOrUndefined(system),
52
- messages: makeMessages(input),
53
- tools: tools.length === 0 ? undefined : tools.map((tool) => ({
54
- name: tool.name,
55
- description: tool.description,
56
- input_schema: tool.parameters as any
57
- })),
58
- tool_choice: !useStructured && tools.length > 0
59
- // For non-structured outputs, ensure tools are used if required
60
- ? typeof required === "boolean"
61
- ? required ? { type: "any" } : { type: "auto" }
62
- : { type: "tool", name: required }
63
- // For structured outputs, ensure the json output tool is used
64
- : useStructured
65
- ? { type: "tool", name: tools[0].name }
66
- : undefined
67
- })
38
+ /**
39
+ * @since 1.0.0
40
+ * @category tags
41
+ */
42
+ export class Config extends Context.Tag("@effect/ai-anthropic/AnthropicCompletions/Config")<
43
+ Config,
44
+ Config.Service
45
+ >() {
46
+ /**
47
+ * @since 1.0.0
48
+ */
49
+ static readonly getOrUndefined: Effect.Effect<typeof Config.Service | undefined> = Effect.map(
50
+ Effect.context<never>(),
51
+ (context) => context.unsafeMap.get(Config.key)
52
+ )
53
+ }
54
+
55
+ /**
56
+ * @since 1.0.0
57
+ */
58
+ export declare namespace Config {
59
+ /**
60
+ * @since 1.0.0
61
+ * @category configuration
62
+ */
63
+ export interface Service extends
64
+ Simplify<
65
+ Partial<
66
+ Omit<
67
+ typeof Generated.CreateMessageParams.Encoded,
68
+ "messages" | "tools" | "tool_choice" | "stream"
69
+ >
70
+ >
71
+ >
72
+ {}
73
+ }
74
+
75
+ // =============================================================================
76
+ // Anthropic Completions
77
+ // =============================================================================
78
+
79
+ const modelCacheKey = Symbol.for("@effect/ai-anthropic/AnthropicCompletions/AiModel")
80
+
81
+ /**
82
+ * @since 1.0.0
83
+ * @category ai models
84
+ */
85
+ export const model = (
86
+ model: (string & {}) | Model,
87
+ config?: Omit<Config.Service, "model">
88
+ ): AiModel.AiModel<Completions.Completions | Tokenizer.Tokenizer, AnthropicClient> =>
89
+ AiModel.make({
90
+ model,
91
+ cacheKey: modelCacheKey,
92
+ requires: AnthropicClient,
93
+ provides: make({ model, config }).pipe(
94
+ Effect.map((completions) =>
95
+ Context.merge(
96
+ Context.make(Completions.Completions, completions),
97
+ Context.make(Tokenizer.Tokenizer, AnthropicTokenizer.make)
98
+ )
68
99
  )
100
+ ),
101
+ updateContext: (context) => {
102
+ const innerConfig = context.unsafeMap.get(Config.key) as Config.Service | undefined
103
+ return Context.merge(context, Context.make(Config, { model, ...config, ...innerConfig }))
69
104
  }
105
+ })
70
106
 
71
- return yield* Completions.make({
72
- create({ span, ...options }) {
73
- return makeRequest(options).pipe(
74
- Effect.tap((request) => annotateRequest(span, request)),
75
- Effect.flatMap((payload) => client.client.messagesPost({ params: {}, payload })),
76
- Effect.tap((response) => annotateChatResponse(span, response)),
77
- Effect.flatMap((response) =>
78
- makeResponse(
79
- response,
80
- "create",
81
- options.tools.length === 1 && options.tools[0].structured
82
- ? options.tools[0]
83
- : undefined
84
- )
85
- ),
86
- Effect.catchAll((cause) =>
87
- Effect.fail(
88
- new AiError({
89
- module: "AnthropicCompletions",
90
- method: "create",
91
- description: "An error occurred",
92
- cause
93
- })
94
- )
107
+ const make = Effect.fnUntraced(function*(options: {
108
+ readonly model: (string & {}) | Model
109
+ readonly config?: Omit<Config.Service, "model">
110
+ }) {
111
+ const client = yield* AnthropicClient
112
+
113
+ const makeRequest = ({ input, required, system, tools }: Completions.CompletionOptions) => {
114
+ const useStructured = tools.length === 1 && tools[0].structured
115
+ return Effect.map(
116
+ Effect.context<never>(),
117
+ (context): typeof Generated.CreateMessageParams.Encoded => ({
118
+ model: options.model,
119
+ // TODO: re-evaluate a better way to do this
120
+ max_tokens: 4096,
121
+ ...options.config,
122
+ ...context.unsafeMap.get(Config.key),
123
+ system: Option.getOrUndefined(system),
124
+ messages: makeMessages(input),
125
+ tools: tools.length === 0 ? undefined : tools.map((tool) => ({
126
+ name: tool.name,
127
+ description: tool.description,
128
+ input_schema: tool.parameters as any
129
+ })),
130
+ tool_choice: !useStructured && tools.length > 0
131
+ // For non-structured outputs, ensure tools are used if required
132
+ ? typeof required === "boolean"
133
+ ? required ? { type: "any" } : { type: "auto" }
134
+ : { type: "tool", name: required }
135
+ // For structured outputs, ensure the json output tool is used
136
+ : useStructured
137
+ ? { type: "tool", name: tools[0].name }
138
+ : undefined
139
+ })
140
+ )
141
+ }
142
+
143
+ return yield* Completions.make({
144
+ create({ span, ...options }) {
145
+ return makeRequest(options).pipe(
146
+ Effect.tap((request) => annotateRequest(span, request)),
147
+ Effect.flatMap((payload) => client.client.messagesPost({ params: {}, payload })),
148
+ Effect.tap((response) => annotateChatResponse(span, response)),
149
+ Effect.flatMap((response) =>
150
+ makeResponse(
151
+ response,
152
+ "create",
153
+ options.tools.length === 1 && options.tools[0].structured
154
+ ? options.tools[0]
155
+ : undefined
156
+ )
157
+ ),
158
+ Effect.catchAll((cause) =>
159
+ Effect.fail(
160
+ new AiError({
161
+ module: "AnthropicCompletions",
162
+ method: "create",
163
+ description: "An error occurred",
164
+ cause
165
+ })
95
166
  )
96
167
  )
97
- },
98
- stream({ span, ...options }) {
99
- return makeRequest(options).pipe(
100
- Effect.tap((request) => annotateRequest(span, request)),
101
- Effect.map(client.stream),
102
- Stream.unwrap,
103
- Stream.tap((response) => {
104
- annotateStreamResponse(span, response)
105
- return Effect.void
106
- }),
107
- Stream.map((response) => response.asAiResponse),
108
- Stream.catchAll((cause) =>
109
- Effect.fail(
110
- new AiError({
111
- module: "AnthropicCompletions",
112
- method: "stream",
113
- description: "An error occurred",
114
- cause
115
- })
116
- )
168
+ )
169
+ },
170
+ stream({ span, ...options }) {
171
+ return makeRequest(options).pipe(
172
+ Effect.tap((request) => annotateRequest(span, request)),
173
+ Effect.map(client.stream),
174
+ Stream.unwrap,
175
+ Stream.tap((response) => {
176
+ annotateStreamResponse(span, response)
177
+ return Effect.void
178
+ }),
179
+ Stream.map((response) => response.asAiResponse),
180
+ Stream.catchAll((cause) =>
181
+ Effect.fail(
182
+ new AiError({
183
+ module: "AnthropicCompletions",
184
+ method: "stream",
185
+ description: "An error occurred",
186
+ cause
187
+ })
117
188
  )
118
189
  )
119
- }
120
- })
190
+ )
191
+ }
121
192
  })
193
+ })
122
194
 
123
195
  /**
124
196
  * @since 1.0.0
@@ -126,7 +198,12 @@ const make = (options: { readonly model: (string & {}) | Model }) =>
126
198
  */
127
199
  export const layerCompletions = (options: {
128
200
  readonly model: (string & {}) | Model
129
- }): Layer.Layer<Completions.Completions, never, AnthropicClient> => Layer.effect(Completions.Completions, make(options))
201
+ readonly config?: Omit<Config.Service, "model">
202
+ }): Layer.Layer<Completions.Completions, never, AnthropicClient> =>
203
+ Layer.effect(
204
+ Completions.Completions,
205
+ make({ model: options.model, config: options.config })
206
+ )
130
207
 
131
208
  /**
132
209
  * @since 1.0.0
@@ -134,11 +211,41 @@ export const layerCompletions = (options: {
134
211
  */
135
212
  export const layer = (options: {
136
213
  readonly model: (string & {}) | Model
137
- }): Layer.Layer<
138
- Completions.Completions | Tokenizer.Tokenizer,
139
- never,
140
- AnthropicClient
141
- > => Layer.merge(layerCompletions(options), AnthropicTokenizer.layer)
214
+ readonly config?: Omit<Config.Service, "model">
215
+ }): Layer.Layer<Completions.Completions | Tokenizer.Tokenizer, never, AnthropicClient> =>
216
+ Layer.merge(layerCompletions(options), AnthropicTokenizer.layer)
217
+
218
+ /**
219
+ * @since 1.0.0
220
+ * @category configuration
221
+ */
222
+ export const withConfigOverride: {
223
+ /**
224
+ * @since 1.0.0
225
+ * @category configuration
226
+ */
227
+ (config: Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
228
+ /**
229
+ * @since 1.0.0
230
+ * @category configuration
231
+ */
232
+ <A, E, R>(self: Effect.Effect<A, E, R>, config: Config.Service): Effect.Effect<A, E, R>
233
+ } = dual<
234
+ /**
235
+ * @since 1.0.0
236
+ * @category configuration
237
+ */
238
+ (config: Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>,
239
+ /**
240
+ * @since 1.0.0
241
+ * @category configuration
242
+ */
243
+ <A, E, R>(self: Effect.Effect<A, E, R>, config: Config.Service) => Effect.Effect<A, E, R>
244
+ >(2, (self, overrides) =>
245
+ Effect.flatMap(
246
+ Config.getOrUndefined,
247
+ (config) => Effect.provideService(self, Config, { ...config, ...overrides })
248
+ ))
142
249
 
143
250
  const makeMessages = (
144
251
  aiInput: AiInput.AiInput
@@ -5,8 +5,6 @@ import type { HttpClient } from "@effect/platform/HttpClient"
5
5
  import * as Context from "effect/Context"
6
6
  import * as Effect from "effect/Effect"
7
7
  import { dual } from "effect/Function"
8
- import type { Simplify } from "effect/Types"
9
- import type * as Generated from "./Generated.js"
10
8
 
11
9
  /**
12
10
  * @since 1.0.0
@@ -34,16 +32,7 @@ export declare namespace AnthropicConfig {
34
32
  * @since 1.0.0
35
33
  * @category models
36
34
  */
37
- export interface Service extends
38
- Simplify<
39
- Partial<
40
- Omit<
41
- typeof Generated.CreateMessageParams.Encoded,
42
- "messages" | "tools" | "tool_choice" | "stream"
43
- >
44
- >
45
- >
46
- {
35
+ export interface Service {
47
36
  readonly transformClient?: (client: HttpClient) => HttpClient
48
37
  }
49
38
  }
@@ -10,7 +10,11 @@ import * as Effect from "effect/Effect"
10
10
  import * as Layer from "effect/Layer"
11
11
  import * as Option from "effect/Option"
12
12
 
13
- const make = Tokenizer.make({
13
+ /**
14
+ * @since 1.0.0
15
+ * @category constructors
16
+ */
17
+ export const make = Tokenizer.make({
14
18
  tokenize(content) {
15
19
  return Effect.try({
16
20
  try: () => {
package/src/Generated.ts CHANGED
@@ -14,17 +14,16 @@ export class MessagesPostParams extends S.Struct({
14
14
  }) {}
15
15
 
16
16
  export class ModelEnum extends S.Literal(
17
- "claude-3-5-haiku-latest",
18
- "claude-3-5-haiku-20241022",
17
+ "claude-3-7-sonnet-latest",
18
+ "claude-3-7-sonnet-20250219",
19
19
  "claude-3-5-sonnet-latest",
20
20
  "claude-3-5-sonnet-20241022",
21
21
  "claude-3-5-sonnet-20240620",
22
+ "claude-3-5-haiku-latest",
23
+ "claude-3-5-haiku-20241022",
24
+ "claude-3-5-haiku-20240307",
22
25
  "claude-3-opus-latest",
23
- "claude-3-opus-20240229",
24
- "claude-3-sonnet-20240229",
25
- "claude-3-haiku-20240307",
26
- "claude-2.1",
27
- "claude-2.0"
26
+ "claude-3-opus-20240229"
28
27
  ) {}
29
28
 
30
29
  export class Model extends S.Union(S.String, ModelEnum) {}