package probe import ( "context" "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "time" ) var ErrAuthFailed = errors.New("upstream auth failed") type ModelsResult struct { RawModels []string HTTPStatus int LatencyMs int64 Error string } func ProviderModels(ctx context.Context, baseURL, apiKey string) (*ModelsResult, error) { client := &http.Client{Timeout: 15 * time.Second} requestURL, err := joinGatewayPath(baseURL, "/v1/models") if err != nil { return nil, fmt.Errorf("resolve models endpoint: %w", err) } req, err := http.NewRequestWithContext(ctx, http.MethodGet, requestURL, nil) if err != nil { return nil, fmt.Errorf("build models request: %w", err) } if token := strings.TrimSpace(apiKey); token != "" { req.Header.Set("Authorization", "Bearer "+token) } startedAt := time.Now() resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("request models: %w", err) } defer resp.Body.Close() result := &ModelsResult{ RawModels: []string{}, HTTPStatus: resp.StatusCode, LatencyMs: time.Since(startedAt).Milliseconds(), } var payload struct { Data []struct { ID string `json:"id"` } `json:"data"` Error any `json:"error"` } if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil { return nil, fmt.Errorf("decode models response: %w", err) } switch resp.StatusCode { case http.StatusUnauthorized, http.StatusForbidden: result.Error = "auth_failed" return result, ErrAuthFailed } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { result.Error = fmt.Sprintf("unexpected_status_%d", resp.StatusCode) return result, fmt.Errorf("models endpoint returned status %d", resp.StatusCode) } for _, item := range payload.Data { if modelID := strings.TrimSpace(item.ID); modelID != "" { result.RawModels = append(result.RawModels, modelID) } } return result, nil } func joinGatewayPath(baseURL, path string) (string, error) { parsedURL, err := url.Parse(strings.TrimSpace(baseURL)) if err != nil { return "", err } if parsedURL.Scheme == "" || parsedURL.Host == "" { return "", fmt.Errorf("base url must include scheme and host") } resolvedPath := strings.TrimSpace(path) if !strings.HasPrefix(resolvedPath, "/") { resolvedPath = "/" + resolvedPath } return parsedURL.ResolveReference(&url.URL{Path: resolvedPath}).String(), nil }