diff --git a/packages/llm/src/adapter/auth-options.ts b/packages/llm/src/adapter/auth-options.ts index a8be0d335f..f7b0447383 100644 --- a/packages/llm/src/adapter/auth-options.ts +++ b/packages/llm/src/adapter/auth-options.ts @@ -1,7 +1,7 @@ -import type { Auth, SecretInput } from "./auth" +import type { Config, Redacted } from "effect" +import type { Auth } from "./auth" export type ApiKeyMode = "optional" | "required" -export type ApiKeyInput = SecretInput export type AuthOverride = { readonly auth: Auth @@ -9,12 +9,12 @@ export type AuthOverride = { } export type OptionalApiKeyAuth = { - readonly apiKey?: ApiKeyInput + readonly apiKey?: string | Redacted.Redacted | Config.Config> readonly auth?: never } export type RequiredApiKeyAuth = { - readonly apiKey: ApiKeyInput + readonly apiKey: string | Redacted.Redacted | Config.Config> readonly auth?: never } diff --git a/packages/llm/src/adapter/auth.ts b/packages/llm/src/adapter/auth.ts index 09e8bc6c4d..6ef37eafb7 100644 --- a/packages/llm/src/adapter/auth.ts +++ b/packages/llm/src/adapter/auth.ts @@ -2,9 +2,6 @@ import { Config, Effect, Redacted } from "effect" import { Headers } from "effect/unstable/http" import { AuthenticationReason, InvalidRequestReason, LLMError, type LLMRequest } from "../schema" -export type Secret = Redacted.Redacted -export type SecretInput = string | Secret | Config.Config - export class MissingCredentialError extends Error { readonly _tag = "MissingCredentialError" @@ -25,7 +22,7 @@ export interface AuthInput { } export interface Credential { - readonly load: Effect.Effect + readonly load: Effect.Effect, CredentialError> readonly orElse: (that: Credential) => Credential readonly bearer: () => Auth readonly header: (name: string) => Auth @@ -42,7 +39,7 @@ export interface Auth { export const isAuth = (input: unknown): input is Auth => typeof input === "object" && input !== null && "apply" in input && typeof input.apply === "function" -const credential = (load: Effect.Effect): Credential => { +const credential = (load: Effect.Effect, CredentialError>): Credential => { const self: Credential = { load, orElse: (that) => credential(load.pipe(Effect.catch(() => that.load))), @@ -56,7 +53,8 @@ const credential = (load: Effect.Effect): Credential => const auth = (apply: Auth["apply"]): Auth => { const self: Auth = { apply, - andThen: (that) => auth((input) => apply(input).pipe(Effect.flatMap((headers) => that.apply({ ...input, headers })))), + andThen: (that) => + auth((input) => apply(input).pipe(Effect.flatMap((headers) => that.apply({ ...input, headers })))), orElse: (that) => auth((input) => apply(input).pipe(Effect.catch(() => that.apply(input)))), pipe: (f) => f(self), } @@ -65,18 +63,19 @@ const auth = (apply: Auth["apply"]): Auth => { const fromCredential = (source: Credential, render: (secret: string) => Headers.Input) => auth((input) => - source.load.pipe( - Effect.map((secret) => Headers.setAll(input.headers, render(Redacted.value(secret)))), - ), + source.load.pipe(Effect.map((secret) => Headers.setAll(input.headers, render(Redacted.value(secret))))), ) -const secretEffect = (secret: string | Secret, source: string) => { +const secretEffect = (secret: string | Redacted.Redacted, source: string) => { const redacted = typeof secret === "string" ? Redacted.make(secret) : secret if (Redacted.value(redacted) === "") return Effect.fail(new MissingCredentialError(source)) return Effect.succeed(redacted) } -const credentialFromSecret = (secret: SecretInput, source: string) => { +const credentialFromSecret = ( + secret: string | Redacted.Redacted | Config.Config>, + source: string, +) => { if (typeof secret === "string" || Redacted.isRedacted(secret)) return credential(secretEffect(secret, source)) return credential( Effect.gen(function* () { @@ -87,17 +86,22 @@ const credentialFromSecret = (secret: SecretInput, source: string) => { export const value = (secret: string, source = "value") => credentialFromSecret(secret, source) -export const optional = (secret: SecretInput | undefined, source = "optional value") => - secret === undefined ? credential(Effect.fail(new MissingCredentialError(source))) : credentialFromSecret(secret, source) +export const optional = ( + secret: string | Redacted.Redacted | Config.Config> | undefined, + source = "optional value", +) => + secret === undefined + ? credential(Effect.fail(new MissingCredentialError(source))) + : credentialFromSecret(secret, source) -export const config = (name: string) => - credentialFromSecret(Config.redacted(name), name) +export const config = (name: string) => credentialFromSecret(Config.redacted(name), name) -export const effect = (load: Effect.Effect) => credential(load) +export const effect = (load: Effect.Effect, CredentialError>) => credential(load) export const none = auth((input) => Effect.succeed(input.headers)) -export const headers = (input: Headers.Input) => auth((inputAuth) => Effect.succeed(Headers.setAll(inputAuth.headers, input))) +export const headers = (input: Headers.Input) => + auth((inputAuth) => Effect.succeed(Headers.setAll(inputAuth.headers, input))) export const remove = (name: string) => auth((input) => Effect.succeed(Headers.remove(input.headers, name))) @@ -112,12 +116,16 @@ const fromModelApiKey = (from: (apiKey: string) => Headers.Input) => return Effect.succeed(Headers.setAll(headers, from(key))) }) -const credentialInput = (source: SecretInput | Credential) => - typeof source === "string" || Redacted.isRedacted(source) || Config.isConfig(source) ? credentialFromSecret(source, "value") : source +const credentialInput = ( + source: string | Redacted.Redacted | Config.Config> | Credential, +) => + typeof source === "string" || Redacted.isRedacted(source) || Config.isConfig(source) + ? credentialFromSecret(source, "value") + : source export function bearer(): Auth -export function bearer(source: SecretInput | Credential): Auth -export function bearer(source?: SecretInput | Credential) { +export function bearer(source: string | Redacted.Redacted | Config.Config> | Credential): Auth +export function bearer(source?: string | Redacted.Redacted | Config.Config> | Credential) { if (source === undefined) return fromModelApiKey((key) => ({ authorization: `Bearer ${key}` })) return credentialInput(source).bearer() } @@ -126,10 +134,13 @@ export const apiKey = bearer export const apiKeyHeader = (name: string) => fromModelApiKey((key) => ({ [name]: key })) -export function header(name: string): (source: SecretInput | Credential) => Auth -export function header(name: string, source: SecretInput | Credential): Auth -export function header(name: string, source?: SecretInput | Credential) { - if (source === undefined) return (next: SecretInput | Credential) => credentialInput(next).header(name) +export function header(name: string): (source: string | Redacted.Redacted | Config.Config> | Credential) => Auth +export function header(name: string, source: string | Redacted.Redacted | Config.Config> | Credential): Auth +export function header(name: string, source?: string | Redacted.Redacted | Config.Config> | Credential) { + if (source === undefined) { + return (next: string | Redacted.Redacted | Config.Config> | Credential) => + credentialInput(next).header(name) + } return credentialInput(source).header(name) } @@ -138,17 +149,18 @@ const toLLMError = (error: AuthError): LLMError => { return new LLMError({ module: "Auth", method: "apply", - reason: error instanceof MissingCredentialError - ? new AuthenticationReason({ message: error.message, kind: "missing" }) - : new InvalidRequestReason({ message: `Failed to resolve auth config: ${error.message}` }), + reason: + error instanceof MissingCredentialError + ? new AuthenticationReason({ message: error.message, kind: "missing" }) + : new InvalidRequestReason({ message: `Failed to resolve auth config: ${error.message}` }), }) } return error } -export const toEffect = (input: Auth) => (authInput: AuthInput): Effect.Effect => - input.apply(authInput).pipe( - Effect.mapError(toLLMError), - ) +export const toEffect = + (input: Auth) => + (authInput: AuthInput): Effect.Effect => + input.apply(authInput).pipe(Effect.mapError(toLLMError)) export * as Auth from "./auth" diff --git a/packages/llm/src/adapter/index.ts b/packages/llm/src/adapter/index.ts index da7274e977..095f694ffe 100644 --- a/packages/llm/src/adapter/index.ts +++ b/packages/llm/src/adapter/index.ts @@ -19,7 +19,7 @@ export { Endpoint } from "./endpoint" export { Framing } from "./framing" export { Protocol } from "./protocol" export type { Auth as AuthShape, AuthInput, Credential, CredentialError } from "./auth" -export type { ApiKeyInput, ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-options" +export type { ApiKeyMode, AuthOverride, ProviderAuthOption } from "./auth-options" export type { Endpoint as EndpointFn, EndpointInput } from "./endpoint" export type { Framing as FramingDef } from "./framing" export type { Protocol as ProtocolDef } from "./protocol"