Skip to content

Commit 00add69

Browse files
authored
EFF-726 Add ModelDimensions service to Embeddings module (#1771)
1 parent 9c1a960 commit 00add69

File tree

6 files changed

+87
-6
lines changed

6 files changed

+87
-6
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"effect": patch
3+
"@effect/ai-openai": patch
4+
"@effect/ai-openai-compat": patch
5+
---
6+
7+
Add `EmbeddingModel.ModelDimensions` and require dimensions in embedding provider `model` constructors.

packages/ai/openai-compat/src/OpenAiEmbeddingModel.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,25 @@ export class Config extends ServiceMap.Service<
4949
*/
5050
export const model = (
5151
model: string,
52-
config?: Omit<typeof Config.Service, "model">
53-
): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel, OpenAiClient> =>
54-
AiModel.make("openai", model, layer({ model, config }))
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+
)
5571

5672
/**
5773
* Creates an OpenAI embedding model service.

packages/ai/openai-compat/test/OpenAiEmbeddingModel.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import { EmbeddingModel } from "effect/unstable/ai"
55
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
66

77
describe("OpenAiEmbeddingModel", () => {
8+
it.effect("model provides dimensions service", () =>
9+
Effect.gen(function*() {
10+
const dimensions = yield* EmbeddingModel.Dimensions
11+
assert.strictEqual(dimensions, 1536)
12+
}).pipe(
13+
Effect.provide(OpenAiEmbeddingModel.model("text-embedding-3-small", { dimensions: 1536 })),
14+
Effect.provideService(OpenAiClient.OpenAiClient, noopOpenAiClient)
15+
))
16+
817
it.effect("reorders embeddings by provider index", () =>
918
Effect.gen(function*() {
1019
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
@@ -198,3 +207,10 @@ const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
198207
}
199208
return yield* Effect.die(new Error("Expected Uint8Array body"))
200209
})
210+
211+
const noopOpenAiClient: OpenAiClient.Service = {
212+
client: undefined as unknown as OpenAiClient.Service["client"],
213+
createResponse: () => Effect.die(new Error("noop")),
214+
createResponseStream: () => Effect.die(new Error("noop")),
215+
createEmbedding: () => Effect.die(new Error("noop"))
216+
}

packages/ai/openai/src/OpenAiEmbeddingModel.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,25 @@ export class Config extends ServiceMap.Service<
4949
*/
5050
export const model = (
5151
model: (string & {}) | Model,
52-
config?: Omit<typeof Config.Service, "model">
53-
): AiModel.Model<"openai", EmbeddingModel.EmbeddingModel, OpenAiClient> =>
54-
AiModel.make("openai", model, layer({ model, config }))
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+
)
5571

5672
/**
5773
* Creates an OpenAI embedding model service.

packages/ai/openai/test/OpenAiEmbeddingModel.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import { EmbeddingModel } from "effect/unstable/ai"
55
import { HttpClient, type HttpClientError, type HttpClientRequest, HttpClientResponse } from "effect/unstable/http"
66

77
describe("OpenAiEmbeddingModel", () => {
8+
it.effect("model provides dimensions service", () =>
9+
Effect.gen(function*() {
10+
const dimensions = yield* EmbeddingModel.Dimensions
11+
assert.strictEqual(dimensions, 1536)
12+
}).pipe(
13+
Effect.provide(OpenAiEmbeddingModel.model("text-embedding-3-small", { dimensions: 1536 })),
14+
Effect.provideService(OpenAiClient.OpenAiClient, noopOpenAiClient)
15+
))
16+
817
it.effect("reorders embeddings by provider index", () =>
918
Effect.gen(function*() {
1019
let capturedRequest: HttpClientRequest.HttpClientRequest | undefined
@@ -240,3 +249,10 @@ const getRequestBody = (request: HttpClientRequest.HttpClientRequest) =>
240249
}
241250
return yield* Effect.die(new Error("Expected Uint8Array body"))
242251
})
252+
253+
const noopOpenAiClient: OpenAiClient.Service = {
254+
client: undefined as unknown as OpenAiClient.Service["client"],
255+
createResponse: () => Effect.die(new Error("noop")),
256+
createResponseStream: () => Effect.die(new Error("noop")),
257+
createEmbedding: () => Effect.die(new Error("noop"))
258+
}

packages/effect/src/unstable/ai/EmbeddingModel.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ export class EmbeddingModel extends ServiceMap.Service<EmbeddingModel, Service>(
3232
"effect/unstable/ai/EmbeddingModel"
3333
) {}
3434

35+
/**
36+
* Service tag that provides the current embedding dimensions.
37+
*
38+
* @since 4.0.0
39+
* @category services
40+
*/
41+
export class Dimensions extends ServiceMap.Service<Dimensions, number>()(
42+
"effect/unstable/ai/EmbeddingModel/Dimensions"
43+
) {}
44+
3545
/**
3646
* Token usage metadata for embedding operations.
3747
*

0 commit comments

Comments
 (0)