@effect/ai-openai-compat 4.0.0-beta.7 → 4.0.0-beta.70

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 (38) hide show
  1. package/dist/OpenAiClient.d.ts +198 -50
  2. package/dist/OpenAiClient.d.ts.map +1 -1
  3. package/dist/OpenAiClient.js +57 -9
  4. package/dist/OpenAiClient.js.map +1 -1
  5. package/dist/OpenAiConfig.d.ts +76 -10
  6. package/dist/OpenAiConfig.d.ts.map +1 -1
  7. package/dist/OpenAiConfig.js +44 -7
  8. package/dist/OpenAiConfig.js.map +1 -1
  9. package/dist/OpenAiEmbeddingModel.d.ts +90 -0
  10. package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
  11. package/dist/OpenAiEmbeddingModel.js +122 -0
  12. package/dist/OpenAiEmbeddingModel.js.map +1 -0
  13. package/dist/OpenAiError.d.ts +109 -35
  14. package/dist/OpenAiError.d.ts.map +1 -1
  15. package/dist/OpenAiError.js +14 -1
  16. package/dist/OpenAiLanguageModel.d.ts +219 -13
  17. package/dist/OpenAiLanguageModel.d.ts.map +1 -1
  18. package/dist/OpenAiLanguageModel.js +42 -19
  19. package/dist/OpenAiLanguageModel.js.map +1 -1
  20. package/dist/OpenAiTelemetry.d.ts +35 -23
  21. package/dist/OpenAiTelemetry.d.ts.map +1 -1
  22. package/dist/OpenAiTelemetry.js +6 -4
  23. package/dist/OpenAiTelemetry.js.map +1 -1
  24. package/dist/index.d.ts +70 -6
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +70 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/internal/errors.js +4 -4
  29. package/dist/internal/errors.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/OpenAiClient.ts +232 -49
  32. package/src/OpenAiConfig.ts +77 -11
  33. package/src/OpenAiEmbeddingModel.ts +208 -0
  34. package/src/OpenAiError.ts +111 -35
  35. package/src/OpenAiLanguageModel.ts +273 -30
  36. package/src/OpenAiTelemetry.ts +36 -24
  37. package/src/index.ts +71 -6
  38. package/src/internal/errors.ts +4 -4
@@ -0,0 +1,208 @@
1
+ /**
2
+ * OpenAI Embedding Model implementation.
3
+ *
4
+ * Provides an EmbeddingModel implementation for OpenAI-compatible embeddings APIs.
5
+ *
6
+ * @since 4.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 type { CreateEmbedding200, CreateEmbeddingRequestJson } from "./OpenAiClient.ts"
17
+ import { OpenAiClient } from "./OpenAiClient.ts"
18
+
19
+ /**
20
+ * A model identifier accepted by an OpenAI-compatible embeddings endpoint.
21
+ *
22
+ * @category models
23
+ * @since 4.0.0
24
+ */
25
+ export type Model = string
26
+
27
+ /**
28
+ * Service definition for OpenAI embedding model configuration.
29
+ *
30
+ * @category context
31
+ * @since 4.0.0
32
+ */
33
+ export class Config extends Context.Service<
34
+ Config,
35
+ Simplify<
36
+ & Partial<
37
+ Omit<
38
+ CreateEmbeddingRequestJson,
39
+ "input"
40
+ >
41
+ >
42
+ & {
43
+ readonly [x: string]: unknown
44
+ }
45
+ >
46
+ >()("@effect/ai-openai-compat/OpenAiEmbeddingModel/Config") {}
47
+
48
+ /**
49
+ * Creates an OpenAI-compatible embedding model that can be used with
50
+ * `AiModel.provide`.
51
+ *
52
+ * @category constructors
53
+ * @since 4.0.0
54
+ */
55
+ export const model = (
56
+ model: string,
57
+ options: {
58
+ readonly dimensions: number
59
+ readonly config?: Omit<typeof Config.Service, "model" | "dimensions">
60
+ }
61
+ ): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel | EmbeddingModel.Dimensions, OpenAiClient> =>
62
+ AiModel.make(
63
+ "openai",
64
+ model,
65
+ Layer.merge(
66
+ layer({
67
+ model,
68
+ config: {
69
+ ...options.config,
70
+ dimensions: options.dimensions
71
+ }
72
+ }),
73
+ Layer.succeed(EmbeddingModel.Dimensions, options.dimensions)
74
+ )
75
+ )
76
+
77
+ /**
78
+ * Creates an OpenAI embedding model service.
79
+ *
80
+ * @category constructors
81
+ * @since 4.0.0
82
+ */
83
+ export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
84
+ readonly model: string
85
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
86
+ }): Effect.fn.Return<EmbeddingModel.Service, never, OpenAiClient> {
87
+ const client = yield* OpenAiClient
88
+
89
+ const makeConfig = Effect.gen(function*() {
90
+ const services = yield* Effect.context<never>()
91
+ return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
92
+ })
93
+
94
+ return yield* EmbeddingModel.make({
95
+ embedMany: Effect.fnUntraced(function*({ inputs }) {
96
+ const config = yield* makeConfig
97
+ const response = yield* client.createEmbedding({ ...config, input: inputs })
98
+ return yield* mapProviderResponse(inputs.length, response)
99
+ })
100
+ })
101
+ })
102
+
103
+ /**
104
+ * Creates a layer for the OpenAI embedding model.
105
+ *
106
+ * @category layers
107
+ * @since 4.0.0
108
+ */
109
+ export const layer = (options: {
110
+ readonly model: string
111
+ readonly config?: Omit<typeof Config.Service, "model"> | undefined
112
+ }): Layer.Layer<EmbeddingModel.EmbeddingModel, never, OpenAiClient> =>
113
+ Layer.effect(EmbeddingModel.EmbeddingModel, make(options))
114
+
115
+ /**
116
+ * Provides config overrides for OpenAI embedding model operations.
117
+ *
118
+ * @category configuration
119
+ * @since 4.0.0
120
+ */
121
+ export const withConfigOverride: {
122
+ /**
123
+ * Provides config overrides for OpenAI embedding model operations.
124
+ *
125
+ * @category configuration
126
+ * @since 4.0.0
127
+ */
128
+ (overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
129
+ /**
130
+ * Provides config overrides for OpenAI embedding model operations.
131
+ *
132
+ * @category configuration
133
+ * @since 4.0.0
134
+ */
135
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
136
+ } = dual<
137
+ /**
138
+ * Provides config overrides for OpenAI embedding model operations.
139
+ *
140
+ * @category configuration
141
+ * @since 4.0.0
142
+ */
143
+ (overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
144
+ /**
145
+ * Provides config overrides for OpenAI embedding model operations.
146
+ *
147
+ * @category configuration
148
+ * @since 4.0.0
149
+ */
150
+ <A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
151
+ >(2, (self, overrides) =>
152
+ Effect.flatMap(
153
+ Effect.serviceOption(Config),
154
+ (config) =>
155
+ Effect.provideService(self, Config, {
156
+ ...(config._tag === "Some" ? config.value : {}),
157
+ ...overrides
158
+ })
159
+ ))
160
+
161
+ const mapProviderResponse = (
162
+ inputLength: number,
163
+ response: CreateEmbedding200
164
+ ): Effect.Effect<EmbeddingModel.ProviderResponse, AiError.AiError> => {
165
+ if (response.data.length !== inputLength) {
166
+ return Effect.fail(
167
+ invalidOutput(`Provider returned ${response.data.length} embeddings but expected ${inputLength}`)
168
+ )
169
+ }
170
+
171
+ const results = new Array<Array<number>>(inputLength)
172
+ const seen = new Set<number>()
173
+
174
+ for (const entry of response.data) {
175
+ if (!Number.isInteger(entry.index) || entry.index < 0 || entry.index >= inputLength) {
176
+ return Effect.fail(invalidOutput(`Provider returned invalid embedding index: ${entry.index}`))
177
+ }
178
+ if (seen.has(entry.index)) {
179
+ return Effect.fail(invalidOutput(`Provider returned duplicate embedding index: ${entry.index}`))
180
+ }
181
+ if (!Array.isArray(entry.embedding)) {
182
+ return Effect.fail(invalidOutput(`Provider returned non-vector embedding at index ${entry.index}`))
183
+ }
184
+
185
+ seen.add(entry.index)
186
+ results[entry.index] = [...entry.embedding]
187
+ }
188
+
189
+ if (seen.size !== inputLength) {
190
+ return Effect.fail(
191
+ invalidOutput(`Provider returned embeddings for ${seen.size} inputs but expected ${inputLength}`)
192
+ )
193
+ }
194
+
195
+ return Effect.succeed({
196
+ results,
197
+ usage: {
198
+ inputTokens: response.usage?.prompt_tokens
199
+ }
200
+ })
201
+ }
202
+
203
+ const invalidOutput = (description: string): AiError.AiError =>
204
+ AiError.make({
205
+ module: "OpenAiEmbeddingModel",
206
+ method: "embedMany",
207
+ reason: new AiError.InvalidOutputError({ description })
208
+ })
@@ -1,12 +1,25 @@
1
1
  /**
2
- * @since 1.0.0
2
+ * The `OpenAiError` module defines OpenAI-specific metadata that can be
3
+ * attached to the shared `AiError` error types used by the AI packages. It is
4
+ * primarily used by OpenAI-compatible clients to preserve provider details
5
+ * such as error codes, error types, request IDs, and rate limit headers while
6
+ * still exposing errors through the provider-neutral Effect AI error model.
7
+ *
8
+ * Use this module when mapping OpenAI API failures into `AiError` values and
9
+ * when consumers need enough structured metadata to debug failed requests,
10
+ * inspect quota or rate limit responses, or correlate an error with OpenAI
11
+ * support. The exported types are metadata shapes only; the module augmentation
12
+ * makes those shapes available on the corresponding shared AI error metadata
13
+ * interfaces without defining new runtime error classes.
14
+ *
15
+ * @since 4.0.0
3
16
  */
4
17
 
5
18
  /**
6
19
  * OpenAI-specific error metadata fields.
7
20
  *
8
- * @since 1.0.0
9
21
  * @category models
22
+ * @since 4.0.0
10
23
  */
11
24
  export type OpenAiErrorMetadata = {
12
25
  /**
@@ -26,11 +39,13 @@ export type OpenAiErrorMetadata = {
26
39
  /**
27
40
  * OpenAI-specific rate limit metadata fields.
28
41
  *
42
+ * **Details**
43
+ *
29
44
  * Extends base error metadata with rate limit specific information from
30
45
  * OpenAI's rate limit headers.
31
46
  *
32
- * @since 1.0.0
33
47
  * @category models
48
+ * @since 4.0.0
34
49
  */
35
50
  export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
36
51
  /**
@@ -52,51 +67,112 @@ export type OpenAiRateLimitMetadata = OpenAiErrorMetadata & {
52
67
  }
53
68
 
54
69
  declare module "effect/unstable/ai/AiError" {
55
- export interface RateLimitError {
56
- readonly metadata: {
57
- readonly openai?: OpenAiRateLimitMetadata | null
58
- }
70
+ /**
71
+ * Metadata attached to rate limit errors returned by OpenAI-compatible APIs.
72
+ *
73
+ * @category models
74
+ * @since 4.0.0
75
+ */
76
+ export interface RateLimitErrorMetadata {
77
+ readonly openai?: OpenAiRateLimitMetadata | null
59
78
  }
60
79
 
61
- export interface QuotaExhaustedError {
62
- readonly metadata: {
63
- readonly openai?: OpenAiErrorMetadata | null
64
- }
80
+ /**
81
+ * Metadata attached when an OpenAI-compatible provider reports that quota or
82
+ * billing limits have been exhausted.
83
+ *
84
+ * @category models
85
+ * @since 4.0.0
86
+ */
87
+ export interface QuotaExhaustedErrorMetadata {
88
+ readonly openai?: OpenAiErrorMetadata | null
65
89
  }
66
90
 
67
- export interface AuthenticationError {
68
- readonly metadata: {
69
- readonly openai?: OpenAiErrorMetadata | null
70
- }
91
+ /**
92
+ * Metadata attached to authentication failures from OpenAI-compatible APIs,
93
+ * such as invalid, missing, or unauthorized API credentials.
94
+ *
95
+ * @category models
96
+ * @since 4.0.0
97
+ */
98
+ export interface AuthenticationErrorMetadata {
99
+ readonly openai?: OpenAiErrorMetadata | null
71
100
  }
72
101
 
73
- export interface ContentPolicyError {
74
- readonly metadata: {
75
- readonly openai?: OpenAiErrorMetadata | null
76
- }
102
+ /**
103
+ * Metadata attached when an OpenAI-compatible provider rejects content because
104
+ * it violates a safety or usage policy.
105
+ *
106
+ * @category models
107
+ * @since 4.0.0
108
+ */
109
+ export interface ContentPolicyErrorMetadata {
110
+ readonly openai?: OpenAiErrorMetadata | null
77
111
  }
78
112
 
79
- export interface InvalidRequestError {
80
- readonly metadata: {
81
- readonly openai?: OpenAiErrorMetadata | null
82
- }
113
+ /**
114
+ * Metadata attached to malformed or unsupported requests rejected by an
115
+ * OpenAI-compatible API before model execution.
116
+ *
117
+ * @category models
118
+ * @since 4.0.0
119
+ */
120
+ export interface InvalidRequestErrorMetadata {
121
+ readonly openai?: OpenAiErrorMetadata | null
83
122
  }
84
123
 
85
- export interface InternalProviderError {
86
- readonly metadata: {
87
- readonly openai?: OpenAiErrorMetadata | null
88
- }
124
+ /**
125
+ * Metadata attached to unexpected server-side failures reported by an
126
+ * OpenAI-compatible provider.
127
+ *
128
+ * @category models
129
+ * @since 4.0.0
130
+ */
131
+ export interface InternalProviderErrorMetadata {
132
+ readonly openai?: OpenAiErrorMetadata | null
89
133
  }
90
134
 
91
- export interface InvalidOutputError {
92
- readonly metadata: {
93
- readonly openai?: OpenAiErrorMetadata | null
94
- }
135
+ /**
136
+ * Metadata attached when an OpenAI-compatible response cannot be converted
137
+ * into the expected AI package output shape.
138
+ *
139
+ * @category models
140
+ * @since 4.0.0
141
+ */
142
+ export interface InvalidOutputErrorMetadata {
143
+ readonly openai?: OpenAiErrorMetadata | null
144
+ }
145
+
146
+ /**
147
+ * Metadata attached when an OpenAI-compatible structured output response does
148
+ * not satisfy the requested schema or parsing constraints.
149
+ *
150
+ * @category models
151
+ * @since 4.0.0
152
+ */
153
+ export interface StructuredOutputErrorMetadata {
154
+ readonly openai?: OpenAiErrorMetadata | null
95
155
  }
96
156
 
97
- export interface UnknownError {
98
- readonly metadata: {
99
- readonly openai?: OpenAiErrorMetadata | null
100
- }
157
+ /**
158
+ * Metadata attached when an OpenAI-compatible provider cannot support the
159
+ * schema supplied for structured output or tool definitions.
160
+ *
161
+ * @category models
162
+ * @since 4.0.0
163
+ */
164
+ export interface UnsupportedSchemaErrorMetadata {
165
+ readonly openai?: OpenAiErrorMetadata | null
166
+ }
167
+
168
+ /**
169
+ * Metadata attached when an OpenAI-compatible error response cannot be mapped
170
+ * to a more specific shared AI error category.
171
+ *
172
+ * @category models
173
+ * @since 4.0.0
174
+ */
175
+ export interface UnknownErrorMetadata {
176
+ readonly openai?: OpenAiErrorMetadata | null
101
177
  }
102
178
  }