package llm import ( "fmt" "log/slog" "net/http" "time" ) const ( // chatCompletionsPath is the endpoint for chat completion requests. chatCompletionsPath = "/chat/completions" // chatTemplateEnableThinking is the chat-template argument that selects // whether a reasoning model emits reasoning tokens. chatTemplateEnableThinking = "enable_thinking" // defaultTimeout limits requests when Config.Timeout is zero. defaultTimeout = 5 * time.Minute // sseDone is the sentinel that terminates a chat completion stream. sseDone = "[DONE]" ) // Config configures a Client. type Config struct { // Key is sent as a bearer token. An empty key disables authentication. Key string `yaml:"key"` // Provider is returned unchanged by Client.Provider. It is not sent to the // inference endpoint. Provider string `yaml:"provider"` // Timeout limits each request, including reading a streaming response. // It defaults to five minutes when zero. Timeout time.Duration `yaml:"timeout"` // URL is the endpoint root. Client appends /chat/completions or /models. URL string `yaml:"url"` } // Validate reports whether Timeout is non-negative and URL is non-empty. func (cfg Config) Validate() error { if cfg.Timeout < 0 { return fmt.Errorf("invalid timeout %v: must not be negative", cfg.Timeout) } if cfg.URL == "" { return fmt.Errorf("missing URL") } return nil } // Client is an OpenAI-compatible inference client. type Client struct { baseURL string httpc *http.Client key string log *slog.Logger provider string timeout time.Duration } // NewClient creates a client from cfg. func NewClient(cfg Config, log *slog.Logger) (*Client, error) { if err := cfg.Validate(); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } if log == nil { return nil, fmt.Errorf("missing logger") } timeout := cfg.Timeout if timeout == 0 { timeout = defaultTimeout } return &Client{ baseURL: cfg.URL, key: cfg.Key, log: log, provider: cfg.Provider, httpc: &http.Client{}, timeout: timeout, }, nil } // Provider returns the application-defined provider name supplied in Config. func (c *Client) Provider() string { return c.provider }