@effect/ai-openai 4.0.0-beta.9 → 4.0.0-beta.90

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 (61) hide show
  1. package/dist/Generated.d.ts +66675 -37672
  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 +170 -29
  6. package/dist/OpenAiClient.d.ts.map +1 -1
  7. package/dist/OpenAiClient.js +321 -46
  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 +88 -10
  14. package/dist/OpenAiConfig.d.ts.map +1 -1
  15. package/dist/OpenAiConfig.js +42 -7
  16. package/dist/OpenAiConfig.js.map +1 -1
  17. package/dist/OpenAiEmbeddingModel.d.ts +188 -0
  18. package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
  19. package/dist/OpenAiEmbeddingModel.js +194 -0
  20. package/dist/OpenAiEmbeddingModel.js.map +1 -0
  21. package/dist/OpenAiError.d.ts +168 -35
  22. package/dist/OpenAiError.d.ts.map +1 -1
  23. package/dist/OpenAiError.js +1 -1
  24. package/dist/OpenAiLanguageModel.d.ts +357 -63
  25. package/dist/OpenAiLanguageModel.d.ts.map +1 -1
  26. package/dist/OpenAiLanguageModel.js +390 -168
  27. package/dist/OpenAiLanguageModel.js.map +1 -1
  28. package/dist/OpenAiSchema.d.ts +2325 -0
  29. package/dist/OpenAiSchema.d.ts.map +1 -0
  30. package/dist/OpenAiSchema.js +811 -0
  31. package/dist/OpenAiSchema.js.map +1 -0
  32. package/dist/OpenAiTelemetry.d.ts +63 -22
  33. package/dist/OpenAiTelemetry.d.ts.map +1 -1
  34. package/dist/OpenAiTelemetry.js +20 -10
  35. package/dist/OpenAiTelemetry.js.map +1 -1
  36. package/dist/OpenAiTool.d.ts +157 -67
  37. package/dist/OpenAiTool.d.ts.map +1 -1
  38. package/dist/OpenAiTool.js +125 -39
  39. package/dist/OpenAiTool.js.map +1 -1
  40. package/dist/index.d.ts +19 -33
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +19 -33
  43. package/dist/index.js.map +1 -1
  44. package/dist/internal/errors.js +4 -4
  45. package/dist/internal/errors.js.map +1 -1
  46. package/dist/internal/utilities.js +0 -4
  47. package/dist/internal/utilities.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/Generated.ts +9720 -4890
  50. package/src/OpenAiClient.ts +499 -98
  51. package/src/OpenAiClientGenerated.ts +202 -0
  52. package/src/OpenAiConfig.ts +89 -11
  53. package/src/OpenAiEmbeddingModel.ts +332 -0
  54. package/src/OpenAiError.ts +170 -35
  55. package/src/OpenAiLanguageModel.ts +776 -169
  56. package/src/OpenAiSchema.ts +1286 -0
  57. package/src/OpenAiTelemetry.ts +69 -28
  58. package/src/OpenAiTool.ts +126 -40
  59. package/src/index.ts +22 -33
  60. package/src/internal/errors.ts +6 -4
  61. package/src/internal/utilities.ts +0 -6
@@ -0,0 +1,202 @@
1
+ /**
2
+ * @since 4.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 4.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 4.0.0
41
+ * @category options
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 4.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 4.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 4.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,35 +1,56 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * The `OpenAiConfig` module lets a workflow temporarily customize the HTTP
3
+ * client used by `@effect/ai-openai` request helpers. OpenAI client, language
4
+ * model, and embedding code read this scoped transform when they execute
5
+ * provider calls.
6
+ *
7
+ * @since 4.0.0
3
8
  */
9
+ import * as Context from "effect/Context"
4
10
  import * as Effect from "effect/Effect"
5
11
  import { dual } from "effect/Function"
6
- import * as ServiceMap from "effect/ServiceMap"
7
12
  import type { HttpClient } from "effect/unstable/http/HttpClient"
8
13
 
9
14
  /**
10
- * @since 1.0.0
15
+ * Context service for scoped OpenAI configuration used by provider operations.
16
+ *
17
+ * **When to use**
18
+ *
19
+ * Use to provide scoped OpenAI client configuration, such as an HTTP client
20
+ * transform, to OpenAI provider operations without passing it through each call.
21
+ *
22
+ * @see {@link withClientTransform} for scoping an HTTP client transformation
23
+ *
11
24
  * @category services
25
+ * @since 4.0.0
12
26
  */
13
- export class OpenAiConfig extends ServiceMap.Service<
27
+ export class OpenAiConfig extends Context.Service<
14
28
  OpenAiConfig,
15
29
  OpenAiConfig.Service
16
30
  >()("@effect/ai-openai/OpenAiConfig") {
17
31
  /**
18
- * @since 1.0.0
32
+ * Gets the configured OpenAI service from the current context when present.
33
+ *
34
+ * @since 4.0.0
19
35
  */
20
36
  static readonly getOrUndefined: Effect.Effect<typeof OpenAiConfig.Service | undefined> = Effect.map(
21
- Effect.services<never>(),
37
+ Effect.context<never>(),
22
38
  (context) => context.mapUnsafe.get(OpenAiConfig.key)
23
39
  )
24
40
  }
25
41
 
26
42
  /**
27
- * @since 1.0.0
43
+ * Types used by the `OpenAiConfig` context service.
44
+ *
45
+ * @since 4.0.0
28
46
  */
29
47
  export declare namespace OpenAiConfig {
30
48
  /**
31
- * @since 1.0.
49
+ * Configuration values read by OpenAI provider operations when executing
50
+ * requests.
51
+ *
32
52
  * @category models
53
+ * @since 4.0.0
33
54
  */
34
55
  export interface Service {
35
56
  readonly transformClient?: ((client: HttpClient) => HttpClient) | undefined
@@ -37,18 +58,75 @@ export declare namespace OpenAiConfig {
37
58
  }
38
59
 
39
60
  /**
40
- * @since 1.0.0
61
+ * Provides a scoped transform for the OpenAI HTTP client used by provider
62
+ * operations.
63
+ *
64
+ * **When to use**
65
+ *
66
+ * Use when you need temporary OpenAI HTTP client customization for a single
67
+ * effect or workflow without rebuilding the client layer.
68
+ *
69
+ * **Details**
70
+ *
71
+ * Supports both data-first and data-last forms. The transform is stored in the
72
+ * scoped `OpenAiConfig` service and read by OpenAI provider operations while
73
+ * running the supplied effect.
74
+ *
75
+ * **Gotchas**
76
+ *
77
+ * If a transform is already present in the scoped config, this helper replaces
78
+ * it. Compose transforms manually when both should apply.
79
+ *
41
80
  * @category configuration
81
+ * @since 4.0.0
42
82
  */
43
83
  export const withClientTransform: {
44
84
  /**
45
- * @since 1.0.0
85
+ * Provides a scoped transform for the OpenAI HTTP client used by provider
86
+ * operations.
87
+ *
88
+ * **When to use**
89
+ *
90
+ * Use when you need temporary OpenAI HTTP client customization for a single
91
+ * effect or workflow without rebuilding the client layer.
92
+ *
93
+ * **Details**
94
+ *
95
+ * Supports both data-first and data-last forms. The transform is stored in the
96
+ * scoped `OpenAiConfig` service and read by OpenAI provider operations while
97
+ * running the supplied effect.
98
+ *
99
+ * **Gotchas**
100
+ *
101
+ * If a transform is already present in the scoped config, this helper replaces
102
+ * it. Compose transforms manually when both should apply.
103
+ *
46
104
  * @category configuration
105
+ * @since 4.0.0
47
106
  */
48
107
  (transform: (client: HttpClient) => HttpClient): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>
49
108
  /**
50
- * @since 1.0.0
109
+ * Provides a scoped transform for the OpenAI HTTP client used by provider
110
+ * operations.
111
+ *
112
+ * **When to use**
113
+ *
114
+ * Use when you need temporary OpenAI HTTP client customization for a single
115
+ * effect or workflow without rebuilding the client layer.
116
+ *
117
+ * **Details**
118
+ *
119
+ * Supports both data-first and data-last forms. The transform is stored in the
120
+ * scoped `OpenAiConfig` service and read by OpenAI provider operations while
121
+ * running the supplied effect.
122
+ *
123
+ * **Gotchas**
124
+ *
125
+ * If a transform is already present in the scoped config, this helper replaces
126
+ * it. Compose transforms manually when both should apply.
127
+ *
51
128
  * @category configuration
129
+ * @since 4.0.0
52
130
  */
53
131
  <A, E, R>(
54
132
  self: Effect.Effect<A, E, R>,
@@ -0,0 +1,332 @@
1
+ /**
2
+ * The `OpenAiEmbeddingModel` module provides the OpenAI implementation of
3
+ * Effect AI's `EmbeddingModel` service. It sends embedding requests through
4
+ * `OpenAiClient`, exposes constructors for layers and `AiModel` values,
5
+ * supports scoped request configuration overrides, and checks that OpenAI
6
+ * returns one numeric vector for each requested input.
7
+ *
8
+ * @since 4.0.0
9
+ */
10
+ import * as Context from "effect/Context"
11
+ import * as Effect from "effect/Effect"
12
+ import { dual } from "effect/Function"
13
+ import * as Layer from "effect/Layer"
14
+ import type { Simplify } from "effect/Types"
15
+ import * as AiError from "effect/unstable/ai/AiError"
16
+ import * as EmbeddingModel from "effect/unstable/ai/EmbeddingModel"
17
+ import * as AiModel from "effect/unstable/ai/Model"
18
+ import { OpenAiClient } from "./OpenAiClient.ts"
19
+ import type * as OpenAiSchema from "./OpenAiSchema.ts"
20
+
21
+ /**
22
+ * Model identifiers supported by OpenAI's embeddings API.
23
+ *
24
+ * @category models
25
+ * @since 4.0.0
26
+ */
27
+ export type Model = "text-embedding-ada-002" | "text-embedding-3-small" | "text-embedding-3-large"
28
+
29
+ /**
30
+ * Context service for OpenAI embedding model configuration.
31
+ *
32
+ * **When to use**
33
+ *
34
+ * Use when you need scoped OpenAI request defaults or overrides for embedding
35
+ * requests from Effect context.
36
+ *
37
+ * **Details**
38
+ *
39
+ * The service stores the OpenAI create-embedding request payload without
40
+ * `input`, carrying options such as `model`, `dimensions`, `encoding_format`,
41
+ * and `user`.
42
+ *
43
+ * @see {@link withConfigOverride} for scoping embedding request overrides
44
+ *
45
+ * @category services
46
+ * @since 4.0.0
47
+ */
48
+ export class Config extends Context.Service<
49
+ Config,
50
+ Simplify<
51
+ & Partial<
52
+ Omit<
53
+ typeof OpenAiSchema.CreateEmbeddingRequest.Encoded,
54
+ "input"
55
+ >
56
+ >
57
+ & {
58
+ readonly [x: string]: unknown
59
+ }
60
+ >
61
+ >()("@effect/ai-openai/OpenAiEmbeddingModel/Config") {}
62
+
63
+ /**
64
+ * Creates an `AiModel` for an OpenAI embedding model with its configured vector dimensions.
65
+ *
66
+ * **When to use**
67
+ *
68
+ * Use to provide an OpenAI `EmbeddingModel` and its `Dimensions` service to an
69
+ * Effect program.
70
+ *
71
+ * @see {@link layer} for providing only the embedding model service
72
+ * @see {@link withConfigOverride} for scoped request configuration overrides
73
+ *
74
+ * @category constructors
75
+ * @since 4.0.0
76
+ */
77
+ export const model = (
78
+ model: (string & {}) | Model,
79
+ options: {
80
+ readonly dimensions: number
81
+ readonly config?: Omit<typeof Config.Service, "model" | "dimensions">
82
+ }
83
+ ): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel | EmbeddingModel.Dimensions, OpenAiClient> =>
84
+ AiModel.make(
85
+ "openai",
86
+ model,
87
+ Layer.merge(
88
+ layer({
89
+ model,
90
+ config: {
91
+ ...options.config,
92
+ dimensions: options.dimensions
93
+ }
94
+ }),
95
+ Layer.succeed(EmbeddingModel.Dimensions, options.dimensions)
96
+ )
97
+ )
98
+
99
+ /**
100
+ * Creates an OpenAI embedding model service.
101
+ *
102
+ * **When to use**
103
+ *
104
+ * Use to construct the `EmbeddingModel.Service` effectfully when
105
+ * `OpenAiClient` is already available in the environment.
106
+ *
107
+ * **Details**
108
+ *
109
+ * The `model` option is sent with each embedding request. Constructor `config`
110
+ * supplies create-embedding request fields other than `model` and `input`, and
111
+ * scoped overrides from `withConfigOverride` are merged last for each request.
112
+ *
113
+ * **Gotchas**
114
+ *
115
+ * The service expects numeric embedding vectors. It fails with
116
+ * `InvalidOutputError` when the provider returns base64 embeddings,
117
+ * out-of-range indexes, duplicate indexes, or an unexpected number of
118
+ * embeddings.
119
+ *
120
+ * @see {@link layer} for providing the embedding model service as a layer
121
+ * @see {@link model} for creating an `AiModel` that also provides dimensions
122
+ * @see {@link withConfigOverride} for scoped request configuration overrides
123
+ *
124
+ * @category constructors
125
+ * @since 4.0.0
126
+ */
127
+ export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
128
+ readonly model: (string & {}) | Model
129
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
130
+ }): Effect.fn.Return<EmbeddingModel.Service, never, OpenAiClient> {
131
+ const client = yield* OpenAiClient
132
+
133
+ const makeConfig = Effect.gen(function*() {
134
+ const services = yield* Effect.context<never>()
135
+ return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
136
+ })
137
+
138
+ return yield* EmbeddingModel.make({
139
+ embedMany: Effect.fnUntraced(function*({ inputs }) {
140
+ const config = yield* makeConfig
141
+ const response = yield* client.createEmbedding({ ...config, input: inputs })
142
+ return yield* mapProviderResponse(inputs.length, response)
143
+ })
144
+ })
145
+ })
146
+
147
+ /**
148
+ * Creates a layer for the OpenAI embedding model.
149
+ *
150
+ * **When to use**
151
+ *
152
+ * Use when composing application layers and you want OpenAI to satisfy
153
+ * `EmbeddingModel.EmbeddingModel` while supplying `OpenAiClient` from another
154
+ * layer.
155
+ *
156
+ * **Gotchas**
157
+ *
158
+ * Use the default floating-point embedding format. The service expects numeric
159
+ * vectors and fails with `InvalidOutputError` if OpenAI returns base64
160
+ * embeddings.
161
+ *
162
+ * @see {@link make} for constructing the embedding model service effectfully
163
+ * @see {@link model} for creating an `AiModel` that also provides embedding dimensions
164
+ *
165
+ * @category layers
166
+ * @since 4.0.0
167
+ */
168
+ export const layer = (options: {
169
+ readonly model: (string & {}) | Model
170
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
171
+ }): Layer.Layer<EmbeddingModel.EmbeddingModel, never, OpenAiClient> =>
172
+ Layer.effect(EmbeddingModel.EmbeddingModel, make(options))
173
+
174
+ /**
175
+ * Provides config overrides for OpenAI embedding model operations.
176
+ *
177
+ * **When to use**
178
+ *
179
+ * Use when you need scoped OpenAI embedding request defaults for a single
180
+ * effect or workflow without rebuilding the embedding model service.
181
+ *
182
+ * **Details**
183
+ *
184
+ * Supports both data-first and data-last forms. Existing scoped config is read
185
+ * first, then the provided overrides are applied so override fields take
186
+ * precedence.
187
+ *
188
+ * @see {@link Config} for the scoped embedding request configuration service
189
+ *
190
+ * @category configuration
191
+ * @since 4.0.0
192
+ */
193
+ export const withConfigOverride: {
194
+ /**
195
+ * Provides config overrides for OpenAI embedding model operations.
196
+ *
197
+ * **When to use**
198
+ *
199
+ * Use when you need scoped OpenAI embedding request defaults for a single
200
+ * effect or workflow without rebuilding the embedding model service.
201
+ *
202
+ * **Details**
203
+ *
204
+ * Supports both data-first and data-last forms. Existing scoped config is read
205
+ * first, then the provided overrides are applied so override fields take
206
+ * precedence.
207
+ *
208
+ * @see {@link Config} for the scoped embedding request configuration service
209
+ *
210
+ * @category configuration
211
+ * @since 4.0.0
212
+ */
213
+ (overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
214
+ /**
215
+ * Provides config overrides for OpenAI embedding model operations.
216
+ *
217
+ * **When to use**
218
+ *
219
+ * Use when you need scoped OpenAI embedding request defaults for a single
220
+ * effect or workflow without rebuilding the embedding model service.
221
+ *
222
+ * **Details**
223
+ *
224
+ * Supports both data-first and data-last forms. Existing scoped config is read
225
+ * first, then the provided overrides are applied so override fields take
226
+ * precedence.
227
+ *
228
+ * @see {@link Config} for the scoped embedding request configuration service
229
+ *
230
+ * @category configuration
231
+ * @since 4.0.0
232
+ */
233
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
234
+ } = dual<
235
+ /**
236
+ * Provides config overrides for OpenAI embedding model operations.
237
+ *
238
+ * **When to use**
239
+ *
240
+ * Use when you need scoped OpenAI embedding request defaults for a single
241
+ * effect or workflow without rebuilding the embedding model service.
242
+ *
243
+ * **Details**
244
+ *
245
+ * Supports both data-first and data-last forms. Existing scoped config is read
246
+ * first, then the provided overrides are applied so override fields take
247
+ * precedence.
248
+ *
249
+ * @see {@link Config} for the scoped embedding request configuration service
250
+ *
251
+ * @category configuration
252
+ * @since 4.0.0
253
+ */
254
+ (overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
255
+ /**
256
+ * Provides config overrides for OpenAI embedding model operations.
257
+ *
258
+ * **When to use**
259
+ *
260
+ * Use when you need scoped OpenAI embedding request defaults for a single
261
+ * effect or workflow without rebuilding the embedding model service.
262
+ *
263
+ * **Details**
264
+ *
265
+ * Supports both data-first and data-last forms. Existing scoped config is read
266
+ * first, then the provided overrides are applied so override fields take
267
+ * precedence.
268
+ *
269
+ * @see {@link Config} for the scoped embedding request configuration service
270
+ *
271
+ * @category configuration
272
+ * @since 4.0.0
273
+ */
274
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
275
+ >(2, (self, overrides) =>
276
+ Effect.flatMap(
277
+ Effect.serviceOption(Config),
278
+ (config) =>
279
+ Effect.provideService(self, Config, {
280
+ ...(config._tag === "Some" ? config.value : {}),
281
+ ...overrides
282
+ })
283
+ ))
284
+
285
+ const mapProviderResponse = (
286
+ inputLength: number,
287
+ response: typeof OpenAiSchema.CreateEmbeddingResponse.Type
288
+ ): Effect.Effect<EmbeddingModel.ProviderResponse, AiError.AiError> => {
289
+ if (response.data.length !== inputLength) {
290
+ return Effect.fail(
291
+ invalidOutput("Provider returned " + response.data.length + " embeddings but expected " + inputLength)
292
+ )
293
+ }
294
+
295
+ const results = new Array<Array<number>>(inputLength)
296
+ const seen = new Set<number>()
297
+
298
+ for (const entry of response.data) {
299
+ if (!Number.isInteger(entry.index) || entry.index < 0 || entry.index >= inputLength) {
300
+ return Effect.fail(invalidOutput("Provider returned invalid embedding index: " + entry.index))
301
+ }
302
+ if (seen.has(entry.index)) {
303
+ return Effect.fail(invalidOutput("Provider returned duplicate embedding index: " + entry.index))
304
+ }
305
+ if (!Array.isArray(entry.embedding)) {
306
+ return Effect.fail(invalidOutput("Provider returned non-vector embedding at index " + entry.index))
307
+ }
308
+
309
+ seen.add(entry.index)
310
+ results[entry.index] = [...entry.embedding]
311
+ }
312
+
313
+ if (seen.size !== inputLength) {
314
+ return Effect.fail(
315
+ invalidOutput("Provider returned embeddings for " + seen.size + " inputs but expected " + inputLength)
316
+ )
317
+ }
318
+
319
+ return Effect.succeed({
320
+ results,
321
+ usage: {
322
+ inputTokens: response.usage?.prompt_tokens
323
+ }
324
+ })
325
+ }
326
+
327
+ const invalidOutput = (description: string): AiError.AiError =>
328
+ AiError.make({
329
+ module: "OpenAiEmbeddingModel",
330
+ method: "embedMany",
331
+ reason: new AiError.InvalidOutputError({ description })
332
+ })