@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.
- package/dist/OpenAiClient.d.ts +198 -50
- package/dist/OpenAiClient.d.ts.map +1 -1
- package/dist/OpenAiClient.js +57 -9
- package/dist/OpenAiClient.js.map +1 -1
- package/dist/OpenAiConfig.d.ts +76 -10
- package/dist/OpenAiConfig.d.ts.map +1 -1
- package/dist/OpenAiConfig.js +44 -7
- package/dist/OpenAiConfig.js.map +1 -1
- package/dist/OpenAiEmbeddingModel.d.ts +90 -0
- package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
- package/dist/OpenAiEmbeddingModel.js +122 -0
- package/dist/OpenAiEmbeddingModel.js.map +1 -0
- package/dist/OpenAiError.d.ts +109 -35
- package/dist/OpenAiError.d.ts.map +1 -1
- package/dist/OpenAiError.js +14 -1
- package/dist/OpenAiLanguageModel.d.ts +219 -13
- package/dist/OpenAiLanguageModel.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.js +42 -19
- package/dist/OpenAiLanguageModel.js.map +1 -1
- package/dist/OpenAiTelemetry.d.ts +35 -23
- package/dist/OpenAiTelemetry.d.ts.map +1 -1
- package/dist/OpenAiTelemetry.js +6 -4
- package/dist/OpenAiTelemetry.js.map +1 -1
- package/dist/index.d.ts +70 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +70 -6
- package/dist/index.js.map +1 -1
- package/dist/internal/errors.js +4 -4
- package/dist/internal/errors.js.map +1 -1
- package/package.json +3 -3
- package/src/OpenAiClient.ts +232 -49
- package/src/OpenAiConfig.ts +77 -11
- package/src/OpenAiEmbeddingModel.ts +208 -0
- package/src/OpenAiError.ts +111 -35
- package/src/OpenAiLanguageModel.ts +273 -30
- package/src/OpenAiTelemetry.ts +36 -24
- package/src/index.ts +71 -6
- 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
|
+
})
|
package/src/OpenAiError.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
}
|