package providers import ( "context" "encoding/json" ) // ── Provider Interface ────────────────────── // Provider is the contract for LLM API adapters. type Provider interface { // ID returns the provider identifier (e.g. "openai", "anthropic"). ID() string // ChatCompletion sends a non-streaming request and returns the full response. ChatCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (*CompletionResponse, error) // StreamCompletion sends a streaming request and returns a channel of events. // The channel is closed when the stream ends. Errors are sent as StreamEvents. StreamCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (<-chan StreamEvent, error) // ListModels returns available models from this provider. ListModels(ctx context.Context, cfg ProviderConfig) ([]Model, error) } // ── Configuration ─────────────────────────── // ProviderConfig holds credentials and endpoint for a configured provider. // Populated from the api_configs table at call time. type ProviderConfig struct { Endpoint string APIKey string CustomHeaders map[string]string // Extra HTTP headers (e.g. OpenRouter HTTP-Referer) Settings map[string]interface{} // Provider-specific settings from provider_settings JSONB } // ── Request / Response Types ──────────────── // Message represents a chat message in the conversation. type Message struct { Role string `json:"role"` // user, assistant, system, tool Content string `json:"content"` // Tool calling fields (assistant → tool_calls, tool → tool_call_id) ToolCalls []ToolCall `json:"tool_calls,omitempty"` // set when assistant requests tools ToolCallID string `json:"tool_call_id,omitempty"` // set for role="tool" result messages Name string `json:"name,omitempty"` // tool name for role="tool" } // ToolCall represents a function call requested by the LLM. type ToolCall struct { ID string `json:"id"` Type string `json:"type"` // "function" Function FunctionCall `json:"function"` } // FunctionCall is the function name and JSON arguments. type FunctionCall struct { Name string `json:"name"` Arguments string `json:"arguments"` } // ToolDef describes a tool available to the LLM. type ToolDef struct { Type string `json:"type"` // "function" Function FunctionDef `json:"function"` } // FunctionDef is the schema for a tool function. type FunctionDef struct { Name string `json:"name"` Description string `json:"description"` Parameters json.RawMessage `json:"parameters"` } // CompletionRequest is the normalized request sent to any provider. type CompletionRequest struct { Model string `json:"model"` Messages []Message `json:"messages"` MaxTokens int `json:"max_tokens,omitempty"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` Stream bool `json:"stream,omitempty"` Tools []ToolDef `json:"tools,omitempty"` // available tools for function calling } // CompletionResponse is the normalized non-streaming response. type CompletionResponse struct { Content string `json:"content"` Model string `json:"model"` FinishReason string `json:"finish_reason"` InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"` ToolCalls []ToolCall `json:"tool_calls,omitempty"` // set when finish_reason is "tool_calls" } // StreamEvent is a single chunk from a streaming response. type StreamEvent struct { // Delta is the incremental text content (empty for non-content events). Delta string `json:"delta,omitempty"` // Done is true when the stream is complete. Done bool `json:"done,omitempty"` // FinishReason is set on the final event. // "stop" = normal, "tool_calls" = LLM wants to call tools. FinishReason string `json:"finish_reason,omitempty"` // ToolCalls accumulates tool call data during streaming. // Fully populated on the final event when FinishReason is "tool_calls". ToolCalls []ToolCall `json:"tool_calls,omitempty"` // Model echoes back the model used. Model string `json:"model,omitempty"` // Error is set if the stream encountered an error. Error error `json:"-"` } // Model represents an available model from a provider, with capabilities. type Model struct { ID string `json:"id"` Name string `json:"name,omitempty"` OwnedBy string `json:"owned_by,omitempty"` Capabilities ModelCapabilities `json:"capabilities"` Pricing *ModelPricing `json:"pricing,omitempty"` } // ModelPricing holds per-million-token costs. type ModelPricing struct { InputPerM float64 `json:"input_per_m,omitempty"` OutputPerM float64 `json:"output_per_m,omitempty"` }