/** * @factory/ai โ€” core types (ai-architecture ยง2.1). * * These are the gateway's canonical types. Provider SDK types never leak past * the adapter boundary: adapters map provider payloads onto these shapes, and * everything above the gateway speaks only this dialect. */ /** The provider families the gateway understands. */ export type ProviderId = "openai" | "anthropic" | "google" | "oss" | "custom"; /** A model the gateway can route to (from a provider catalog or static table). */ export interface ModelInfo { id: string; provider: ProviderId; kind: "chat" | "embed" | "chat+embed"; contextWindow: number; maxOutput: number; toolCalls: boolean; vision: boolean; /** USD per 1k input tokens (0 for self-hosted OSS). */ pricePer1kIn: number; /** USD per 1k output tokens (0 for self-hosted OSS). */ pricePer1kOut: number; /** Embedding dimension (0 for chat-only models). */ dimension: number; } export type Role = "system" | "user" | "assistant" | "tool"; /** A content part: plain text, or an image_url part (vision models). */ export type ContentPart = | { type: "text"; text: string } | { type: "image_url"; image_url: { url: string } }; export interface ToolCall { id: string; name: string; arguments: Record; } /** * A tool definition. The ONLY tool-schema dialect the gateway accepts is a * strict JSON-Schema subset (see `tools.ts`): object root; * `string|number|boolean|integer|array|object` types; `enum`, `items`, * `required`, `description` โ€” no `pattern`, no `$ref`. Adapters translate to * provider shapes (OpenAI `function.parameters`, Anthropic `input_schema`, * Google `parameters`). */ export interface ToolSpec { name: string; description: string; parameters: Record; } export interface ChatMessage { role: Role; content: string | ContentPart[]; /** Assistant messages only: the tool calls the model requested. */ toolCalls?: ToolCall[]; /** Tool-result messages only: which call this is the result for. */ toolCallId?: string; /** Tool name (tool messages). */ name?: string; } export interface ChatRequest { model: string; messages: ChatMessage[]; tools?: ToolSpec[]; toolChoice?: "auto" | "none" | { name: string }; temperature?: number; maxTokens?: number; stream?: boolean; /** {capability, workspaceId, userId, requestId} โ€” used for usage metering. */ metadata?: Record; /** * TASK-0122: optional caller cancellation. When aborted the adapter stops * pulling the provider stream and throws an abort error (the gateway maps * it to a non-retryable `ai_aborted` error so no retry/fallback re-sends). */ signal?: AbortSignal; } /** One streaming increment (the base primitive; non-streaming collects these). */ export interface ChatChunk { delta?: string; toolCallDelta?: ToolCall; usage?: { in: number; out: number; total: number }; finishReason?: ChatResult["finishReason"]; done: boolean; } export interface ChatResult { content: string; toolCalls: ToolCall[]; usage: { in: number; out: number; total: number }; model: string; latencyMs: number; provider: ProviderId; finishReason: "stop" | "tool_calls" | "length" | "error"; } export interface EmbedRequest { model: string; texts: string[]; metadata?: Record; } export interface EmbedResult { model: string; dimension: number; vectors: number[][]; usage: { tokens: number }; latencyMs: number; provider: ProviderId; } /** * The provider contract. `chat` is streaming-only at the adapter level; the * gateway collects the stream for non-streaming callers. `embed` is * Anthropic-unsupported (the gateway routes embed to a fallback provider). */ export interface ProviderAdapter { id: ProviderId; models(): Promise; chat(req: ChatRequest): AsyncIterable; embed(req: EmbedRequest): Promise; health(): Promise<{ ok: boolean; latencyMs: number }>; }