1/// <reference types="@cloudflare/workers-types" />
2
3import * as Effect from "effect/Effect";
4import { makeLanguageModelLayer } from "../AI/LanguageModel.ts";
5import { AI, WorkersAIError, type AIClient } from "./AI.ts";
6import * as Binding from "./Binding.ts";
7import { makeBindingLayer } from "./BindingLayer.ts";
8
9/** The binding value produced by calling {@link AI} (declared on `env` or `yield*`-ed). */
10export type AIBinding = Binding.Binding<AI["key"], AIClient, AI>;
11
12/**
13 * The layer that provides the Effect-native interface for the Cloudflare
14 * Workers AI binding.
15 *
16 * Provide it on the Worker effect (`Effect.provide(Cloudflare.Workers.AIBinding)`)
17 * so that yielding an {@link AI} binding attaches the native `ai` binding to
18 * the surrounding Worker at deploy time and, at runtime, resolves to the
19 * Effect-native {@link AIClient} (wrapping the raw `Ai` handle so `run` /
20 * `models` return Effects and `model(...)` yields a `LanguageModel` layer).
21 */
22export const AIBinding = makeBindingLayer<AI, Ai, AIClient>(AI, (raw) => {
23 const self: AIClient = {
24 raw,
25 run: (model, inputs, options) =>
26 Effect.gen(function* () {
27 const ai = yield* raw;
28 return yield* tryPromise(() => ai.run(model, inputs, options));
29 }),
30 models: (params) =>
31 Effect.gen(function* () {
32 const ai = yield* raw;
33 return yield* tryPromise(() => ai.models(params));
34 }),
35 model: (options) =>
36 makeLanguageModelLayer({
37 ...options,
38 client: self,
39 }),
40 };
41 return self;
42});
43
44const tryPromise = <T>(
45 fn: () => Promise<T>,
46): Effect.Effect<T, WorkersAIError> =>
47 Effect.tryPromise({
48 try: fn,
49 catch: (error) =>
50 new WorkersAIError({
51 message:
52 error instanceof Error
53 ? error.message
54 : "Unknown Workers AI runtime error",
55 cause: error,
56 }),
57 });