package providers import ( "git.gobha.me/xcaliber/chat-switchboard/capabilities" "git.gobha.me/xcaliber/chat-switchboard/models" "context" "encoding/json" "fmt" "io" "net/http" "strings" ) // VeniceProvider handles the Venice.ai API. // Venice is OpenAI-compatible with extensions: venice_parameters for // web search, thinking controls, and web scraping. // // Ported from ai-editor js/providers/venice.js type VeniceProvider struct{} func (p *VeniceProvider) ID() string { return "venice" } // ── Chat Completion ───────────────────────── // Delegates to OpenAI provider after injecting venice_parameters. func (p *VeniceProvider) ChatCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (*CompletionResponse, error) { oai := &OpenAIProvider{} return oai.ChatCompletion(ctx, cfg, req) } func (p *VeniceProvider) StreamCompletion(ctx context.Context, cfg ProviderConfig, req CompletionRequest) (<-chan StreamEvent, error) { oai := &OpenAIProvider{} return oai.StreamCompletion(ctx, cfg, req) } // ── List Models ───────────────────────────── // Venice /v1/models returns model_spec with capabilities, pricing, // context tokens, and traits. We parse all of it. func (p *VeniceProvider) ListModels(ctx context.Context, cfg ProviderConfig) ([]Model, error) { endpoint := strings.TrimSuffix(cfg.Endpoint, "/") + "/models" httpReq, err := http.NewRequestWithContext(ctx, "GET", endpoint, nil) if err != nil { return nil, err } if cfg.APIKey != "" { httpReq.Header.Set("Authorization", "Bearer "+cfg.APIKey) } resp, err := http.DefaultClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("venice list models: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { b, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("venice list models: HTTP %d: %s", resp.StatusCode, string(b)) } var result veniceModelsResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("venice decode models: %w", err) } out := make([]Model, 0, len(result.Data)) for _, m := range result.Data { spec := m.ModelSpec vcaps := spec.Capabilities caps := models.ModelCapabilities{ Streaming: true, ToolCalling: vcaps.SupportsFunctionCalling, Vision: vcaps.SupportsVision, Reasoning: vcaps.SupportsReasoning, WebSearch: vcaps.SupportsWebSearch, CodeOptimized: vcaps.OptimizedForCode, MaxContext: spec.AvailableContextTokens, } // Venice doesn't report max output tokens directly. // Try known table, then derive from context. if known, ok := capabilities.LookupKnownModel(m.ID); ok && known.MaxOutputTokens > 0 { caps.MaxOutputTokens = known.MaxOutputTokens } var pricing *models.ModelPricing if spec.Pricing.Input.USD > 0 { pricing = &models.ModelPricing{ InputPerM: spec.Pricing.Input.USD, OutputPerM: spec.Pricing.Output.USD, } } name := spec.Name if name == "" { name = m.ID } out = append(out, Model{ ID: m.ID, Name: name, OwnedBy: "venice", Capabilities: caps, Pricing: pricing, }) } return out, nil } // ── Venice Wire Types ─────────────────────── type veniceModelsResponse struct { Data []veniceModel `json:"data"` } type veniceModel struct { ID string `json:"id"` Type string `json:"type"` OwnedBy string `json:"owned_by"` ModelSpec veniceModelSpec `json:"model_spec"` } type veniceModelSpec struct { Name string `json:"name"` Description string `json:"description"` AvailableContextTokens int `json:"availableContextTokens"` Capabilities veniceCapabilities `json:"capabilities"` Pricing venicePricing `json:"pricing"` Traits []string `json:"traits"` Offline bool `json:"offline"` } type veniceCapabilities struct { SupportsFunctionCalling bool `json:"supportsFunctionCalling"` SupportsVision bool `json:"supportsVision"` SupportsReasoning bool `json:"supportsReasoning"` SupportsWebSearch bool `json:"supportsWebSearch"` SupportsResponseSchema bool `json:"supportsResponseSchema"` SupportsAudioInput bool `json:"supportsAudioInput"` SupportsLogProbs bool `json:"supportsLogProbs"` OptimizedForCode bool `json:"optimizedForCode"` } type venicePricing struct { Input venicePriceUnit `json:"input"` Output venicePriceUnit `json:"output"` } type venicePriceUnit struct { USD float64 `json:"usd"` }