/** * The request Atelier accepts, which is OpenAI's `POST /v1/images/generations` body verbatim. * * Kept as their vocabulary rather than a translated one of our own: a caller that already knows the * OpenAI API knows this, and a provider added later is the one that has to do the translating — * which is where the knowledge of that provider already lives. */ export type ImageRequest = { prompt: string; model?: string; n?: number; quality?: 'auto' | 'high' | 'medium' | 'low' | 'hd' | 'standard'; size?: string; response_format?: 'url' | 'b64_json'; output_format?: 'png' | 'jpeg' | 'webp'; output_compression?: number; background?: 'transparent' | 'opaque' | 'auto'; moderation?: 'low' | 'auto'; style?: 'vivid' | 'natural'; stream?: boolean; partial_images?: number; user?: string; }; /** * OpenAI's `POST /v1/images/edits` body, minus the files themselves. * * A separate type rather than flags on ImageRequest, because the parameter sets genuinely differ: * edits take `input_fidelity` and no `style` or `moderation`, its `quality` has no `hd`, and its * model default is gpt-image-1.5 where generations default to dall-e-2. */ export type ImageEditRequest = { prompt: string; model?: string; n?: number; quality?: 'auto' | 'high' | 'medium' | 'low' | 'standard'; size?: string; response_format?: 'url' | 'b64_json'; output_format?: 'png' | 'jpeg' | 'webp'; output_compression?: number; background?: 'transparent' | 'opaque' | 'auto'; input_fidelity?: 'high' | 'low'; stream?: boolean; partial_images?: number; user?: string; }; /** An uploaded file on its way to a provider. */ export type UploadedImage = { filename: string; bytes: Uint8Array; type: string }; /** OpenAI's usage object. Stored raw so a cost computed with the wrong price can be recomputed. */ export type ImageUsage = { total_tokens?: number; input_tokens?: number; output_tokens?: number; input_tokens_details?: { text_tokens?: number; image_tokens?: number }; }; /** One generated image, as the provider returned it. */ export type ProviderImage = { /** Raw bytes. Providers hand back base64; decoding is theirs to do, so the queue only writes files. */ bytes: Uint8Array; /** png / jpeg / webp — decides the extension on disk and the served content type. */ format: string; revisedPrompt?: string; }; export type ProviderResult = { images: ProviderImage[]; usage?: ImageUsage; /** Echoed back by the provider: the size, quality and background actually used. */ echoed: Record; }; /** * An adapter: how to talk to one vendor's image API. * * Deliberately holds no key, no chosen model and no enabled flag. Those live in a `providers` row, * because they are decisions rather than knowledge, and several rows share one adapter. */ export type ImageAdapter = { readonly name: string; /** For display — "OpenAI" rather than "openai", used to name providers built from it. */ readonly label: string; /** Models this adapter is known to serve. Advisory — an unknown model is passed through. */ readonly models: readonly string[]; readonly defaultModel: string; /** * This adapter authenticates with nothing, because it talks to a service of OURS rather than a * vendor's. Availability is then a fact about the code, not about what is in the secret store. * * The alternative was storing a dummy secret so the existing "has a key?" test would pass. That would * make the store lie about what is configured, and the store is the one place that is supposed to be * the truth about credentials. A flag that says "this question does not apply" is smaller and honest. * * A category rather than an exception: whisper, OCR and TTS already run on this machine too. */ readonly keyless?: boolean; /** * The key is passed in rather than read here: it can come from the database or the environment, and * which one is a question about configuration, not about how to talk to this provider. */ generate(request: ImageRequest, apiKey: string, signal?: AbortSignal): Promise; /** * Edit. Optional: a provider that cannot do it says so by not having it, and the route answers 400 * rather than the queue discovering it after the row is written. */ edit?( request: ImageEditRequest, images: UploadedImage[], mask: UploadedImage | undefined, apiKey: string, signal?: AbortSignal, ): Promise; /** USD for the reported usage, or null when this model's price is not known. */ costUsd(model: string, usage: ImageUsage | undefined): string | null; };