diff --git a/bun.lockb b/bun.lockb index a1e2534..2cafe94 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 8550066..792141c 100644 --- a/package.json +++ b/package.json @@ -92,6 +92,7 @@ "mobx": "^6.13.1", "npm-check-updates": "^17.1.1", "npm-sort": "^0.0.4", + "openai": "^4.57.3", "tsup": "^8.2.4", "tsx": "^4.19.0", "typescript": "^5.5.4" diff --git a/src/Layers/OpenAIClient.ts b/src/Layers/OpenAIClient.ts new file mode 100644 index 0000000..cfab980 --- /dev/null +++ b/src/Layers/OpenAIClient.ts @@ -0,0 +1,68 @@ +import { Config, Context, Effect, Layer } from "effect" +import type { OpenAI } from "openai" + + +export class OpenAIClient extends Context.Tag("OpenAIClient")() {} + +export class OpenAIClientService { + constructor( + readonly openai: Effect.Effect.Success, + readonly client: OpenAI, + ) {} + + try( + try_: ( + client: OpenAI, + signal: AbortSignal, + ) => Promise + ) { + return Effect.tryPromise({ + try: signal => try_(this.client, signal), + catch: e => e instanceof this.openai.OpenAIError + ? e + : new Error(`Unknown OpenAIClient error: ${ e }`), + }) + } +} + + +const importOpenAI = Effect.tryPromise({ + try: () => import("openai"), + catch: cause => new Error("Could not import 'openai'. Make sure it is installed.", { cause }), +}) + +export const OpenAIClientLive = ( + config: { + readonly apiKey: Config.Config + readonly organization?: Config.Config + readonly project?: Config.Config + readonly baseURL?: Config.Config + readonly timeout?: Config.Config + readonly maxRetries?: Config.Config + + readonly httpAgent?: any + readonly fetch?: any + readonly defaultHeaders?: { [x: string]: string } + readonly defaultQuery?: { [x: string]: string } + } +) => Layer.effect(OpenAIClient, Effect.gen(function*() { + const openai = yield* importOpenAI + + return new OpenAIClientService( + openai, + + new openai.OpenAI({ + apiKey: yield* config.apiKey, + organization: (yield* config.organization || Config.succeed(undefined)) || null, + project: (yield* config.project || Config.succeed(undefined)) || null, + baseURL: (yield* config.baseURL || Config.succeed(undefined)) || "https://api.openai.com/v1", + timeout: yield* config.timeout || Config.succeed(undefined), + maxRetries: yield* config.maxRetries || Config.succeed(undefined), + + httpAgent: config.httpAgent, + fetch: config.fetch, + defaultHeaders: config.defaultHeaders, + defaultQuery: config.defaultQuery, + }), + ) +})) diff --git a/src/Layers/index.ts b/src/Layers/index.ts index 6a72a2d..2f143bb 100644 --- a/src/Layers/index.ts +++ b/src/Layers/index.ts @@ -1,2 +1,3 @@ export * from "./express" export * as JSONWebToken from "./JSONWebToken" +export * as OpenAIClient from "./OpenAIClient"