@effect/ai-openai 4.0.0-beta.6 → 4.0.0-beta.60

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 (52) hide show
  1. package/dist/Generated.d.ts +66011 -38686
  2. package/dist/Generated.d.ts.map +1 -1
  3. package/dist/Generated.js +1 -1
  4. package/dist/Generated.js.map +1 -1
  5. package/dist/OpenAiClient.d.ts +63 -17
  6. package/dist/OpenAiClient.d.ts.map +1 -1
  7. package/dist/OpenAiClient.js +210 -33
  8. package/dist/OpenAiClient.js.map +1 -1
  9. package/dist/OpenAiClientGenerated.d.ts +91 -0
  10. package/dist/OpenAiClientGenerated.d.ts.map +1 -0
  11. package/dist/OpenAiClientGenerated.js +84 -0
  12. package/dist/OpenAiClientGenerated.js.map +1 -0
  13. package/dist/OpenAiConfig.d.ts +2 -2
  14. package/dist/OpenAiConfig.d.ts.map +1 -1
  15. package/dist/OpenAiConfig.js +3 -3
  16. package/dist/OpenAiConfig.js.map +1 -1
  17. package/dist/OpenAiEmbeddingModel.d.ts +85 -0
  18. package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
  19. package/dist/OpenAiEmbeddingModel.js +119 -0
  20. package/dist/OpenAiEmbeddingModel.js.map +1 -0
  21. package/dist/OpenAiError.d.ts +22 -32
  22. package/dist/OpenAiError.d.ts.map +1 -1
  23. package/dist/OpenAiLanguageModel.d.ts +43 -49
  24. package/dist/OpenAiLanguageModel.d.ts.map +1 -1
  25. package/dist/OpenAiLanguageModel.js +296 -152
  26. package/dist/OpenAiLanguageModel.js.map +1 -1
  27. package/dist/OpenAiSchema.d.ts +1920 -0
  28. package/dist/OpenAiSchema.d.ts.map +1 -0
  29. package/dist/OpenAiSchema.js +536 -0
  30. package/dist/OpenAiSchema.js.map +1 -0
  31. package/dist/OpenAiTool.d.ts +8 -7
  32. package/dist/OpenAiTool.d.ts.map +1 -1
  33. package/dist/OpenAiTool.js +2 -1
  34. package/dist/OpenAiTool.js.map +1 -1
  35. package/dist/index.d.ts +18 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/internal/errors.js +4 -4
  40. package/dist/internal/errors.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/Generated.ts +7416 -4257
  43. package/src/OpenAiClient.ts +377 -81
  44. package/src/OpenAiClientGenerated.ts +202 -0
  45. package/src/OpenAiConfig.ts +3 -3
  46. package/src/OpenAiEmbeddingModel.ts +203 -0
  47. package/src/OpenAiError.ts +24 -32
  48. package/src/OpenAiLanguageModel.ts +420 -144
  49. package/src/OpenAiSchema.ts +875 -0
  50. package/src/OpenAiTool.ts +2 -1
  51. package/src/index.ts +21 -0
  52. package/src/internal/errors.ts +6 -4
@@ -0,0 +1,202 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as Array from "effect/Array"
5
+ import type * as Config from "effect/Config"
6
+ import * as Context from "effect/Context"
7
+ import * as Effect from "effect/Effect"
8
+ import { identity } from "effect/Function"
9
+ import * as Function from "effect/Function"
10
+ import * as Layer from "effect/Layer"
11
+ import * as Predicate from "effect/Predicate"
12
+ import * as Redacted from "effect/Redacted"
13
+ import * as Headers from "effect/unstable/http/Headers"
14
+ import * as HttpClient from "effect/unstable/http/HttpClient"
15
+ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
16
+ import * as Generated from "./Generated.ts"
17
+ import { OpenAiConfig } from "./OpenAiConfig.ts"
18
+
19
+ // =============================================================================
20
+ // Service Identifier
21
+ // =============================================================================
22
+
23
+ /**
24
+ * Service identifier for the generated OpenAI client.
25
+ *
26
+ * @since 1.0.0
27
+ * @category service
28
+ */
29
+ export class OpenAiClientGenerated extends Context.Service<OpenAiClientGenerated, Generated.OpenAiClient>()(
30
+ "@effect/ai-openai/OpenAiClientGenerated"
31
+ ) {}
32
+
33
+ // =============================================================================
34
+ // Options
35
+ // =============================================================================
36
+
37
+ /**
38
+ * Options for configuring the generated OpenAI client.
39
+ *
40
+ * @since 1.0.0
41
+ * @category models
42
+ */
43
+ export type Options = {
44
+ /**
45
+ * The OpenAI API key.
46
+ */
47
+ readonly apiKey?: Redacted.Redacted<string> | undefined
48
+
49
+ /**
50
+ * The base URL for the OpenAI API.
51
+ *
52
+ * @default "https://api.openai.com/v1"
53
+ */
54
+ readonly apiUrl?: string | undefined
55
+
56
+ /**
57
+ * Optional organization ID for multi-org accounts.
58
+ */
59
+ readonly organizationId?: Redacted.Redacted<string> | undefined
60
+
61
+ /**
62
+ * Optional project ID for project-scoped requests.
63
+ */
64
+ readonly projectId?: Redacted.Redacted<string> | undefined
65
+
66
+ /**
67
+ * Optional transformer for the HTTP client.
68
+ */
69
+ readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
70
+ }
71
+
72
+ const RedactedOpenAiHeaders = {
73
+ OpenAiOrganization: "OpenAI-Organization",
74
+ OpenAiProject: "OpenAI-Project"
75
+ }
76
+
77
+ // =============================================================================
78
+ // Constructor
79
+ // =============================================================================
80
+
81
+ /**
82
+ * Creates a generated OpenAI client service with the given options.
83
+ *
84
+ * @since 1.0.0
85
+ * @category constructors
86
+ */
87
+ export const make = Effect.fnUntraced(
88
+ function*(options: Options): Effect.fn.Return<Generated.OpenAiClient, never, HttpClient.HttpClient> {
89
+ const baseClient = yield* HttpClient.HttpClient
90
+ const apiUrl = options.apiUrl ?? "https://api.openai.com/v1"
91
+
92
+ const httpClient = baseClient.pipe(
93
+ HttpClient.mapRequest(Function.flow(
94
+ HttpClientRequest.prependUrl(apiUrl),
95
+ options.apiKey
96
+ ? HttpClientRequest.bearerToken(Redacted.value(options.apiKey))
97
+ : identity,
98
+ options.organizationId
99
+ ? HttpClientRequest.setHeader(
100
+ RedactedOpenAiHeaders.OpenAiOrganization,
101
+ Redacted.value(options.organizationId)
102
+ )
103
+ : identity,
104
+ options.projectId
105
+ ? HttpClientRequest.setHeader(
106
+ RedactedOpenAiHeaders.OpenAiProject,
107
+ Redacted.value(options.projectId)
108
+ )
109
+ : identity,
110
+ HttpClientRequest.acceptJson
111
+ )),
112
+ options.transformClient
113
+ ? options.transformClient
114
+ : identity
115
+ )
116
+
117
+ return Generated.make(httpClient, {
118
+ transformClient: Effect.fnUntraced(function*(client) {
119
+ const config = yield* OpenAiConfig.getOrUndefined
120
+ if (Predicate.isNotUndefined(config?.transformClient)) {
121
+ return config.transformClient(client)
122
+ }
123
+ return client
124
+ })
125
+ })
126
+ },
127
+ Effect.updateService(
128
+ Headers.CurrentRedactedNames,
129
+ Array.appendAll(Object.values(RedactedOpenAiHeaders))
130
+ )
131
+ )
132
+
133
+ // =============================================================================
134
+ // Layers
135
+ // =============================================================================
136
+
137
+ /**
138
+ * Creates a layer for the generated OpenAI client with the given options.
139
+ *
140
+ * @since 1.0.0
141
+ * @category layers
142
+ */
143
+ export const layer = (options: Options): Layer.Layer<OpenAiClientGenerated, never, HttpClient.HttpClient> =>
144
+ Layer.effect(OpenAiClientGenerated, make(options))
145
+
146
+ /**
147
+ * Creates a layer for the generated OpenAI client, loading the requisite
148
+ * configuration via Effect's `Config` module.
149
+ *
150
+ * @since 1.0.0
151
+ * @category layers
152
+ */
153
+ export const layerConfig = (options?: {
154
+ /**
155
+ * The config value to load for the API key.
156
+ */
157
+ readonly apiKey?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
158
+
159
+ /**
160
+ * The config value to load for the API URL.
161
+ */
162
+ readonly apiUrl?: Config.Config<string> | undefined
163
+
164
+ /**
165
+ * The config value to load for the organization ID.
166
+ */
167
+ readonly organizationId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
168
+
169
+ /**
170
+ * The config value to load for the project ID.
171
+ */
172
+ readonly projectId?: Config.Config<Redacted.Redacted<string> | undefined> | undefined
173
+
174
+ /**
175
+ * Optional transformer for the HTTP client.
176
+ */
177
+ readonly transformClient?: ((client: HttpClient.HttpClient) => HttpClient.HttpClient) | undefined
178
+ }): Layer.Layer<OpenAiClientGenerated, Config.ConfigError, HttpClient.HttpClient> =>
179
+ Layer.effect(
180
+ OpenAiClientGenerated,
181
+ Effect.gen(function*() {
182
+ const apiKey = Predicate.isNotUndefined(options?.apiKey)
183
+ ? yield* options.apiKey :
184
+ undefined
185
+ const apiUrl = Predicate.isNotUndefined(options?.apiUrl)
186
+ ? yield* options.apiUrl :
187
+ undefined
188
+ const organizationId = Predicate.isNotUndefined(options?.organizationId)
189
+ ? yield* options.organizationId
190
+ : undefined
191
+ const projectId = Predicate.isNotUndefined(options?.projectId)
192
+ ? yield* options.projectId :
193
+ undefined
194
+ return yield* make({
195
+ apiKey,
196
+ apiUrl,
197
+ organizationId,
198
+ projectId,
199
+ transformClient: options?.transformClient
200
+ })
201
+ })
202
+ )
@@ -1,16 +1,16 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
+ import * as Context from "effect/Context"
4
5
  import * as Effect from "effect/Effect"
5
6
  import { dual } from "effect/Function"
6
- import * as ServiceMap from "effect/ServiceMap"
7
7
  import type { HttpClient } from "effect/unstable/http/HttpClient"
8
8
 
9
9
  /**
10
10
  * @since 1.0.0
11
11
  * @category services
12
12
  */
13
- export class OpenAiConfig extends ServiceMap.Service<
13
+ export class OpenAiConfig extends Context.Service<
14
14
  OpenAiConfig,
15
15
  OpenAiConfig.Service
16
16
  >()("@effect/ai-openai/OpenAiConfig") {
@@ -18,7 +18,7 @@ export class OpenAiConfig extends ServiceMap.Service<
18
18
  * @since 1.0.0
19
19
  */
20
20
  static readonly getOrUndefined: Effect.Effect<typeof OpenAiConfig.Service | undefined> = Effect.map(
21
- Effect.services<never>(),
21
+ Effect.context<never>(),
22
22
  (context) => context.mapUnsafe.get(OpenAiConfig.key)
23
23
  )
24
24
  }
@@ -0,0 +1,203 @@
1
+ /**
2
+ * OpenAI Embedding Model implementation.
3
+ *
4
+ * Provides an EmbeddingModel implementation for OpenAI's embeddings API.
5
+ *
6
+ * @since 1.0.0
7
+ */
8
+ import * as Context from "effect/Context"
9
+ import * as Effect from "effect/Effect"
10
+ import { dual } from "effect/Function"
11
+ import * as Layer from "effect/Layer"
12
+ import type { Simplify } from "effect/Types"
13
+ import * as AiError from "effect/unstable/ai/AiError"
14
+ import * as EmbeddingModel from "effect/unstable/ai/EmbeddingModel"
15
+ import * as AiModel from "effect/unstable/ai/Model"
16
+ import { OpenAiClient } from "./OpenAiClient.ts"
17
+ import type * as OpenAiSchema from "./OpenAiSchema.ts"
18
+
19
+ /**
20
+ * @since 1.0.0
21
+ * @category models
22
+ */
23
+ export type Model = "text-embedding-ada-002" | "text-embedding-3-small" | "text-embedding-3-large"
24
+
25
+ /**
26
+ * Service definition for OpenAI embedding model configuration.
27
+ *
28
+ * @since 1.0.0
29
+ * @category services
30
+ */
31
+ export class Config extends Context.Service<
32
+ Config,
33
+ Simplify<
34
+ & Partial<
35
+ Omit<
36
+ typeof OpenAiSchema.CreateEmbeddingRequest.Encoded,
37
+ "input"
38
+ >
39
+ >
40
+ & {
41
+ readonly [x: string]: unknown
42
+ }
43
+ >
44
+ >()("@effect/ai-openai/OpenAiEmbeddingModel/Config") {}
45
+
46
+ /**
47
+ * @since 1.0.0
48
+ * @category constructors
49
+ */
50
+ export const model = (
51
+ model: (string & {}) | Model,
52
+ options: {
53
+ readonly dimensions: number
54
+ readonly config?: Omit<typeof Config.Service, "model" | "dimensions">
55
+ }
56
+ ): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel | EmbeddingModel.Dimensions, OpenAiClient> =>
57
+ AiModel.make(
58
+ "openai",
59
+ model,
60
+ Layer.merge(
61
+ layer({
62
+ model,
63
+ config: {
64
+ ...options.config,
65
+ dimensions: options.dimensions
66
+ }
67
+ }),
68
+ Layer.succeed(EmbeddingModel.Dimensions, options.dimensions)
69
+ )
70
+ )
71
+
72
+ /**
73
+ * Creates an OpenAI embedding model service.
74
+ *
75
+ * @since 1.0.0
76
+ * @category constructors
77
+ */
78
+ export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
79
+ readonly model: (string & {}) | Model
80
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
81
+ }): Effect.fn.Return<EmbeddingModel.Service, never, OpenAiClient> {
82
+ const client = yield* OpenAiClient
83
+
84
+ const makeConfig = Effect.gen(function*() {
85
+ const services = yield* Effect.context<never>()
86
+ return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
87
+ })
88
+
89
+ return yield* EmbeddingModel.make({
90
+ embedMany: Effect.fnUntraced(function*({ inputs }) {
91
+ const config = yield* makeConfig
92
+ const response = yield* client.createEmbedding({ ...config, input: inputs })
93
+ return yield* mapProviderResponse(inputs.length, response)
94
+ })
95
+ })
96
+ })
97
+
98
+ /**
99
+ * Creates a layer for the OpenAI embedding model.
100
+ *
101
+ * @since 1.0.0
102
+ * @category layers
103
+ */
104
+ export const layer = (options: {
105
+ readonly model: (string & {}) | Model
106
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
107
+ }): Layer.Layer<EmbeddingModel.EmbeddingModel, never, OpenAiClient> =>
108
+ Layer.effect(EmbeddingModel.EmbeddingModel, make(options))
109
+
110
+ /**
111
+ * Provides config overrides for OpenAI embedding model operations.
112
+ *
113
+ * @since 1.0.0
114
+ * @category configuration
115
+ */
116
+ export const withConfigOverride: {
117
+ /**
118
+ * Provides config overrides for OpenAI embedding model operations.
119
+ *
120
+ * @since 1.0.0
121
+ * @category configuration
122
+ */
123
+ (overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
124
+ /**
125
+ * Provides config overrides for OpenAI embedding model operations.
126
+ *
127
+ * @since 1.0.0
128
+ * @category configuration
129
+ */
130
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
131
+ } = dual<
132
+ /**
133
+ * Provides config overrides for OpenAI embedding model operations.
134
+ *
135
+ * @since 1.0.0
136
+ * @category configuration
137
+ */
138
+ (overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
139
+ /**
140
+ * Provides config overrides for OpenAI embedding model operations.
141
+ *
142
+ * @since 1.0.0
143
+ * @category configuration
144
+ */
145
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
146
+ >(2, (self, overrides) =>
147
+ Effect.flatMap(
148
+ Effect.serviceOption(Config),
149
+ (config) =>
150
+ Effect.provideService(self, Config, {
151
+ ...(config._tag === "Some" ? config.value : {}),
152
+ ...overrides
153
+ })
154
+ ))
155
+
156
+ const mapProviderResponse = (
157
+ inputLength: number,
158
+ response: typeof OpenAiSchema.CreateEmbeddingResponse.Type
159
+ ): Effect.Effect<EmbeddingModel.ProviderResponse, AiError.AiError> => {
160
+ if (response.data.length !== inputLength) {
161
+ return Effect.fail(
162
+ invalidOutput("Provider returned " + response.data.length + " embeddings but expected " + inputLength)
163
+ )
164
+ }
165
+
166
+ const results = new Array<Array<number>>(inputLength)
167
+ const seen = new Set<number>()
168
+
169
+ for (const entry of response.data) {
170
+ if (!Number.isInteger(entry.index) || entry.index < 0 || entry.index >= inputLength) {
171
+ return Effect.fail(invalidOutput("Provider returned invalid embedding index: " + entry.index))
172
+ }
173
+ if (seen.has(entry.index)) {
174
+ return Effect.fail(invalidOutput("Provider returned duplicate embedding index: " + entry.index))
175
+ }
176
+ if (!Array.isArray(entry.embedding)) {
177
+ return Effect.fail(invalidOutput("Provider returned non-vector embedding at index " + entry.index))
178
+ }
179
+
180
+ seen.add(entry.index)
181
+ results[entry.index] = [...entry.embedding]
182
+ }
183
+
184
+ if (seen.size !== inputLength) {
185
+ return Effect.fail(
186
+ invalidOutput("Provider returned embeddings for " + seen.size + " inputs but expected " + inputLength)
187
+ )
188
+ }
189
+
190
+ return Effect.succeed({
191
+ results,
192
+ usage: {
193
+ inputTokens: response.usage?.prompt_tokens
194
+ }
195
+ })
196
+ }
197
+
198
+ const invalidOutput = (description: string): AiError.AiError =>
199
+ AiError.make({
200
+ module: "OpenAiEmbeddingModel",
201
+ method: "embedMany",
202
+ reason: new AiError.InvalidOutputError({ description })
203
+ })
@@ -57,51 +57,43 @@ export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
57
57
  }
58
58
 
59
59
  declare module "effect/unstable/ai/AiError" {
60
- export interface RateLimitError {
61
- readonly metadata: {
62
- readonly openai?: OpenAiRateLimitMetadata | null
63
- }
60
+ export interface RateLimitErrorMetadata {
61
+ readonly openai?: OpenAiRateLimitMetadata | null
64
62
  }
65
63
 
66
- export interface QuotaExhaustedError {
67
- readonly metadata: {
68
- readonly openai?: OpenAiErrorMetadata | null
69
- }
64
+ export interface QuotaExhaustedErrorMetadata {
65
+ readonly openai?: OpenAiErrorMetadata | null
70
66
  }
71
67
 
72
- export interface AuthenticationError {
73
- readonly metadata: {
74
- readonly openai?: OpenAiErrorMetadata | null
75
- }
68
+ export interface AuthenticationErrorMetadata {
69
+ readonly openai?: OpenAiErrorMetadata | null
76
70
  }
77
71
 
78
- export interface ContentPolicyError {
79
- readonly metadata: {
80
- readonly openai?: OpenAiErrorMetadata | null
81
- }
72
+ export interface ContentPolicyErrorMetadata {
73
+ readonly openai?: OpenAiErrorMetadata | null
82
74
  }
83
75
 
84
- export interface InvalidRequestError {
85
- readonly metadata: {
86
- readonly openai?: OpenAiErrorMetadata | null
87
- }
76
+ export interface InvalidRequestErrorMetadata {
77
+ readonly openai?: OpenAiErrorMetadata | null
88
78
  }
89
79
 
90
- export interface InternalProviderError {
91
- readonly metadata: {
92
- readonly openai?: OpenAiErrorMetadata | null
93
- }
80
+ export interface InternalProviderErrorMetadata {
81
+ readonly openai?: OpenAiErrorMetadata | null
94
82
  }
95
83
 
96
- export interface InvalidOutputError {
97
- readonly metadata: {
98
- readonly openai?: OpenAiErrorMetadata | null
99
- }
84
+ export interface InvalidOutputErrorMetadata {
85
+ readonly openai?: OpenAiErrorMetadata | null
100
86
  }
101
87
 
102
- export interface UnknownError {
103
- readonly metadata: {
104
- readonly openai?: OpenAiErrorMetadata | null
105
- }
88
+ export interface StructuredOutputErrorMetadata {
89
+ readonly openai?: OpenAiErrorMetadata | null
90
+ }
91
+
92
+ export interface UnsupportedSchemaErrorMetadata {
93
+ readonly openai?: OpenAiErrorMetadata | null
94
+ }
95
+
96
+ export interface UnknownErrorMetadata {
97
+ readonly openai?: OpenAiErrorMetadata | null
106
98
  }
107
99
  }