package llm import ( "context" "encoding/json" "fmt" "io" "log/slog" "net/http" ) // ListModels returns available models from the LLM server. func (c *Client) ListModels(ctx context.Context) ([]Model, error) { ctx, cancel := context.WithTimeout(ctx, c.timeout) defer cancel() req, err := http.NewRequestWithContext( ctx, http.MethodGet, c.baseURL+"/models", nil, ) if err != nil { return nil, fmt.Errorf("create request: %w", err) } if c.key != "" { req.Header.Set("Authorization", "Bearer "+c.key) } req.Header.Set("Content-Type", "application/json") res, err := c.httpc.Do(req) if err != nil { return nil, fmt.Errorf("list models: %w", err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { body, err := io.ReadAll(res.Body) if err != nil { c.log.ErrorContext( ctx, "list models: failed to read error response body", slog.Any("error", err), ) return nil, fmt.Errorf( "list models: status code %d", res.StatusCode, ) } var apiError struct { Error *struct { Type string `json:"type"` Code string `json:"code"` Message string `json:"message"` } `json:"error"` } if err := json.Unmarshal( body, &apiError, ); err == nil && apiError.Error != nil { c.log.ErrorContext( ctx, "list models: request error", slog.Int("status", res.StatusCode), slog.String("code", apiError.Error.Code), slog.String( "message", apiError.Error.Message, ), slog.String("type", apiError.Error.Type), ) return nil, fmt.Errorf( "list models: %s: %s", apiError.Error.Code, apiError.Error.Message, ) } c.log.ErrorContext( ctx, "list models: API error", slog.Int("status", res.StatusCode), slog.String("body", string(body)), ) return nil, fmt.Errorf( "list models: %d: %s", res.StatusCode, string(body), ) } var body ModelsResponse if err := json.NewDecoder(res.Body).Decode(&body); err != nil { return nil, fmt.Errorf( "list models: decode response: %w", err, ) } return body.Data, nil }