Enforce strict Effect tsgo migrations

This commit is contained in:
elpresidank 2026-06-01 23:19:54 -05:00
parent 64fb23e7d0
commit f6878d4dd7
49 changed files with 5547 additions and 3250 deletions

View file

@ -4,11 +4,13 @@
* Python reference: trustgraph-flow/trustgraph/embeddings/ollama/processor.py
*/
import { Effect, Layer } from "effect";
import { Config, Effect, Layer } from "effect";
import * as O from "effect/Option";
import * as S from "effect/Schema";
import {
Embeddings,
embeddingsError,
EmbeddingsError,
errorMessage,
makeEmbeddingsService,
makeEmbeddingsSpecs,
type EmbeddingsServiceShape,
@ -22,18 +24,58 @@ export interface OllamaEmbeddingsConfig extends ProcessorConfig {
fetch?: typeof fetch;
}
interface OllamaEmbedResponse {
embeddings: number[][];
const EmbeddingVector = S.Array(S.Number);
const OllamaEmbedResponse = S.Struct({
embeddings: S.Array(EmbeddingVector),
});
type OllamaEmbedResponse = typeof OllamaEmbedResponse.Type;
interface ResolvedOllamaEmbeddingsConfig {
readonly defaultModel: string;
readonly ollamaHost: string;
readonly fetchImpl: typeof fetch;
}
const ollamaEmbeddingsError = (operation: string, cause: unknown): EmbeddingsError =>
EmbeddingsError.make({
operation,
message: errorMessage(cause),
provider: "ollama",
});
const ollamaEmbeddingsMessageError = (operation: string, message: string): EmbeddingsError =>
EmbeddingsError.make({
operation,
message,
provider: "ollama",
});
const optionalStringConfig = Effect.fn("OllamaEmbeddings.optionalStringConfig")(function*(name: string) {
return O.getOrUndefined(yield* Config.string(name).pipe(Config.option));
});
const loadOllamaEmbeddingsConfig = Effect.fn("OllamaEmbeddings.loadConfig")(function*(
config: OllamaEmbeddingsConfig,
) {
return {
defaultModel: config.model ?? "mxbai-embed-large",
ollamaHost:
config.ollamaHost ??
(yield* optionalStringConfig("OLLAMA_URL")) ??
(yield* optionalStringConfig("OLLAMA_HOST")) ??
"http://localhost:11434",
fetchImpl: config.fetch ?? globalThis.fetch,
} satisfies ResolvedOllamaEmbeddingsConfig;
});
export function makeOllamaEmbeddings(config: OllamaEmbeddingsConfig): EmbeddingsServiceShape {
const defaultModel = config.model ?? "mxbai-embed-large";
const ollamaHost =
config.ollamaHost ??
process.env.OLLAMA_URL ??
process.env.OLLAMA_HOST ??
"http://localhost:11434";
const fetchImpl = config.fetch ?? globalThis.fetch;
const {
defaultModel,
ollamaHost,
fetchImpl,
} = Effect.runSync(loadOllamaEmbeddingsConfig(config)) satisfies ResolvedOllamaEmbeddingsConfig;
return {
embed: Effect.fn("OllamaEmbeddings.embed")((texts: ReadonlyArray<string>, model?: string) => {
@ -49,29 +91,38 @@ export function makeOllamaEmbeddings(config: OllamaEmbeddingsConfig): Embeddings
model: useModel,
input: Array.from(texts),
}).pipe(
Effect.mapError((error) => embeddingsError("ollama.encode-request", error, "ollama")),
Effect.mapError((error) => ollamaEmbeddingsError("ollama.encode-request", error))
);
return yield* Effect.tryPromise({
try: async () => {
const response = await fetchImpl(url, {
const response = yield* Effect.tryPromise({
try: () =>
fetchImpl(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body,
});
if (!response.ok) {
const errorBody = await response.text();
throw new Error(
`Ollama embeddings request failed (${response.status}): ${errorBody}`,
);
}
const data = (await response.json()) as OllamaEmbedResponse;
return data.embeddings;
},
catch: (error) => embeddingsError("ollama.embed", error, "ollama"),
}),
catch: (error) => ollamaEmbeddingsError("ollama.fetch", error),
});
if (!response.ok) {
const errorBody = yield* Effect.tryPromise({
try: () => response.text(),
catch: (error) => ollamaEmbeddingsError("ollama.error-body", error),
});
return yield* ollamaEmbeddingsMessageError(
"ollama.embed",
`Ollama embeddings request failed (${response.status}): ${errorBody}`,
);
}
const data = yield* Effect.tryPromise({
try: () => response.json() as Promise<unknown>,
catch: (error) => ollamaEmbeddingsError("ollama.response-json", error),
});
const decoded = yield* S.decodeUnknownEffect(OllamaEmbedResponse)(data).pipe(
Effect.mapError((error) => ollamaEmbeddingsError("ollama.decode-response", error))
);
return Array.from(decoded.embeddings, (vector) => Array.from(vector));
});
}),
};
@ -88,9 +139,10 @@ export type OllamaEmbeddingsProcessor = ReturnType<typeof makeOllamaEmbeddingsPr
export function makeOllamaEmbeddingsProcessor(config: OllamaEmbeddingsConfig) {
const embeddings = makeOllamaEmbeddings(config);
console.log(
`[OllamaEmbeddings] Initialized (host=${config.ollamaHost ?? process.env.OLLAMA_URL ?? process.env.OLLAMA_HOST ?? "http://localhost:11434"}, model=${config.model ?? "mxbai-embed-large"})`,
);
const resolved = Effect.runSync(loadOllamaEmbeddingsConfig(config)) satisfies ResolvedOllamaEmbeddingsConfig;
Effect.runSync(Effect.log(
`[OllamaEmbeddings] Initialized (host=${resolved.ollamaHost}, model=${resolved.defaultModel})`,
));
return makeEmbeddingsService(config, embeddings);
}
@ -102,6 +154,6 @@ export const program = makeFlowProcessorProgram<OllamaEmbeddingsConfig, never, E
layer: (config) => OllamaEmbeddingsLive(config),
});
export async function run(): Promise<void> {
await Effect.runPromise(program);
export function run(): Promise<void> {
return Effect.runPromise(program);
}