This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/providers/venice.go
Jeffrey Smith 11fd8c1e57 step 1: rename module chat-switchboard → switchboard-core
- go.mod module name
- All 714 import references across 289 Go files
- VERSION: 0.1.0
- CI DB names: switchboard_core_{ci,dev,test}
- Docker image: gobha/switchboard-core
- Test fixtures: JWT issuer, repo names
- .env.example, docker-compose container name
- Compiles clean (go build exit 0)
2026-03-25 19:48:04 -04:00

169 lines
5.1 KiB
Go

package providers
import (
"switchboard-core/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)
}
func (p *VeniceProvider) Embed(ctx context.Context, cfg ProviderConfig, req EmbeddingRequest) (*EmbeddingResponse, error) {
oai := &OpenAIProvider{}
return oai.Embed(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 := cfg.Client().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.
// ResolveMaxOutput will derive from context window at resolution time.
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
}
// Normalize Venice type → our model type
// Venice returns: "text", "embedding", "image", "code"
// We normalize to: "chat", "embedding", "image"
modelType := "chat"
switch m.Type {
case "embedding":
modelType = "embedding"
case "image":
modelType = "image"
default:
modelType = "chat" // "text", "code", etc. are all chat-capable
}
out = append(out, Model{
ID: m.ID,
Name: name,
Type: modelType,
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"`
}