Changeset 0.10.4 (#60)
This commit is contained in:
@@ -70,6 +70,7 @@ func ModelsForUser(ctx context.Context, stores store.Stores, userID string) ([]m
|
||||
ID: entry.ModelID,
|
||||
DisplayName: displayName(entry.DisplayName, entry.ModelID),
|
||||
ModelID: entry.ModelID,
|
||||
ModelType: entry.ModelType,
|
||||
Source: "catalog",
|
||||
ProviderConfigID: entry.ProviderConfigID,
|
||||
ConfigID: entry.ProviderConfigID,
|
||||
@@ -104,6 +105,7 @@ func ModelsForUser(ctx context.Context, stores store.Stores, userID string) ([]m
|
||||
ID: entry.ModelID,
|
||||
DisplayName: displayName(entry.DisplayName, entry.ModelID),
|
||||
ModelID: entry.ModelID,
|
||||
ModelType: entry.ModelType,
|
||||
Source: "catalog",
|
||||
ProviderConfigID: prov.ID,
|
||||
ConfigID: prov.ID,
|
||||
@@ -141,6 +143,7 @@ func ModelsForUser(ctx context.Context, stores store.Stores, userID string) ([]m
|
||||
ID: entry.ModelID,
|
||||
DisplayName: displayName(entry.DisplayName, entry.ModelID),
|
||||
ModelID: entry.ModelID,
|
||||
ModelType: entry.ModelType,
|
||||
Source: "catalog",
|
||||
ProviderConfigID: prov.ID,
|
||||
ConfigID: prov.ID,
|
||||
|
||||
14
server/database/migrations/005_model_type.sql
Normal file
14
server/database/migrations/005_model_type.sql
Normal file
@@ -0,0 +1,14 @@
|
||||
-- Migration 005: Add model_type to model_catalog
|
||||
--
|
||||
-- Providers like Venice return a "type" field per model (e.g. "text", "embedding", "image").
|
||||
-- This column captures that classification so role dropdowns can filter appropriately:
|
||||
-- - "embedding" role → only embedding models
|
||||
-- - "utility" role → only chat/text models
|
||||
--
|
||||
-- Default is "chat" (the overwhelming majority of models). Provider sync will
|
||||
-- populate from the wire response when available — NO hardcoding of model types.
|
||||
|
||||
ALTER TABLE model_catalog ADD COLUMN IF NOT EXISTS model_type VARCHAR(20) DEFAULT 'chat';
|
||||
|
||||
-- Index for role UI filtering (e.g. "show me all embedding models for this provider")
|
||||
CREATE INDEX IF NOT EXISTS idx_model_catalog_type ON model_catalog(model_type);
|
||||
@@ -196,6 +196,19 @@ func TruncateAll(t *testing.T) {
|
||||
}'::jsonb)
|
||||
ON CONFLICT (key) DO NOTHING
|
||||
`)
|
||||
|
||||
// Re-seed platform_policies — also wiped by CASCADE from users truncation
|
||||
// (platform_policies.updated_by REFERENCES users(id)).
|
||||
DB.Exec(`
|
||||
INSERT INTO platform_policies (key, value) VALUES
|
||||
('allow_user_byok', 'false'),
|
||||
('allow_user_personas', 'false'),
|
||||
('allow_raw_model_access', 'true'),
|
||||
('allow_registration', 'true'),
|
||||
('default_user_active', 'false'),
|
||||
('allow_team_providers', 'true')
|
||||
ON CONFLICT (key) DO NOTHING
|
||||
`)
|
||||
}
|
||||
|
||||
// SeedTestUser creates a test user and returns the user ID.
|
||||
|
||||
@@ -66,6 +66,7 @@ func syncProviderModels(ctx context.Context, stores store.Stores, cfg *models.Pr
|
||||
syncEntries[i] = store.CatalogSyncEntry{
|
||||
ModelID: m.ID,
|
||||
DisplayName: m.Name,
|
||||
ModelType: m.Type,
|
||||
Capabilities: m.Capabilities,
|
||||
Pricing: m.Pricing,
|
||||
}
|
||||
|
||||
@@ -157,6 +157,7 @@ type CatalogEntry struct {
|
||||
ProviderConfigID string `json:"provider_config_id" db:"provider_config_id"`
|
||||
ModelID string `json:"model_id" db:"model_id"`
|
||||
DisplayName string `json:"display_name,omitempty" db:"display_name"`
|
||||
ModelType string `json:"model_type" db:"model_type"` // "chat", "embedding", "image", etc.
|
||||
Capabilities ModelCapabilities `json:"capabilities" db:"capabilities"`
|
||||
Pricing *ModelPricing `json:"pricing,omitempty" db:"pricing"`
|
||||
Visibility string `json:"visibility" db:"visibility"`
|
||||
@@ -470,7 +471,8 @@ type UserModel struct {
|
||||
ID string `json:"id"`
|
||||
DisplayName string `json:"display_name"`
|
||||
ModelID string `json:"model_id"`
|
||||
Source string `json:"source"` // "catalog", "persona", "live"
|
||||
ModelType string `json:"model_type"` // "chat", "embedding", "image", etc.
|
||||
Source string `json:"source"` // "catalog", "persona", "live"
|
||||
|
||||
ProviderConfigID string `json:"provider_config_id"`
|
||||
ConfigID string `json:"config_id"` // Alias of ProviderConfigID for frontend compat
|
||||
|
||||
@@ -263,6 +263,7 @@ func (p *OpenAIProvider) ListModels(ctx context.Context, cfg ProviderConfig) ([]
|
||||
|
||||
out = append(out, Model{
|
||||
ID: m.ID,
|
||||
Type: m.Type, // passthrough from API if present (empty → "chat" at sync time)
|
||||
OwnedBy: m.OwnedBy,
|
||||
Capabilities: caps,
|
||||
})
|
||||
@@ -506,6 +507,7 @@ type openaiModelsResponse struct {
|
||||
Data []struct {
|
||||
ID string `json:"id"`
|
||||
OwnedBy string `json:"owned_by"`
|
||||
Type string `json:"type,omitempty"` // Some APIs (e.g. Venice-compat) include model type
|
||||
ContextLength int `json:"context_length,omitempty"` // Ollama, OpenRouter
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
@@ -141,6 +141,7 @@ type EmbeddingResponse struct {
|
||||
type Model struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type string `json:"type,omitempty"` // "chat", "embedding", "image" — from provider API
|
||||
OwnedBy string `json:"owned_by,omitempty"`
|
||||
Capabilities models.ModelCapabilities `json:"capabilities"`
|
||||
Pricing *models.ModelPricing `json:"pricing,omitempty"`
|
||||
|
||||
@@ -99,9 +99,23 @@ func (p *VeniceProvider) ListModels(ctx context.Context, cfg ProviderConfig) ([]
|
||||
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,
|
||||
|
||||
@@ -86,6 +86,7 @@ type CatalogStore interface {
|
||||
type CatalogSyncEntry struct {
|
||||
ModelID string
|
||||
DisplayName string
|
||||
ModelType string // "chat", "embedding", "image" — from provider API
|
||||
Capabilities models.ModelCapabilities
|
||||
Pricing *models.ModelPricing
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ type CatalogStore struct{}
|
||||
|
||||
func NewCatalogStore() *CatalogStore { return &CatalogStore{} }
|
||||
|
||||
const catalogCols = `id, provider_config_id, model_id, display_name,
|
||||
const catalogCols = `id, provider_config_id, model_id, display_name, model_type,
|
||||
capabilities, pricing, visibility, last_synced_at, created_at, updated_at`
|
||||
|
||||
// catalogColsMC is catalogCols with mc. prefix for use in JOINs
|
||||
// where id/created_at/updated_at are ambiguous.
|
||||
const catalogColsMC = `mc.id, mc.provider_config_id, mc.model_id, mc.display_name,
|
||||
const catalogColsMC = `mc.id, mc.provider_config_id, mc.model_id, mc.display_name, mc.model_type,
|
||||
mc.capabilities, mc.pricing, mc.visibility, mc.last_synced_at, mc.created_at, mc.updated_at`
|
||||
|
||||
// UpsertFromSync bulk-inserts or updates catalog entries from a provider API fetch.
|
||||
@@ -31,6 +31,12 @@ func (s *CatalogStore) UpsertFromSync(ctx context.Context, providerConfigID stri
|
||||
capsJSON := ToJSON(e.Capabilities)
|
||||
pricingJSON := ToJSON(e.Pricing) // nil → "{}", always valid JSONB
|
||||
|
||||
// Normalize model type: empty → "chat" (the default)
|
||||
modelType := e.ModelType
|
||||
if modelType == "" {
|
||||
modelType = "chat"
|
||||
}
|
||||
|
||||
var existingID string
|
||||
err := DB.QueryRowContext(ctx,
|
||||
"SELECT id FROM model_catalog WHERE provider_config_id = $1 AND model_id = $2",
|
||||
@@ -40,10 +46,10 @@ func (s *CatalogStore) UpsertFromSync(ctx context.Context, providerConfigID stri
|
||||
if err == sql.ErrNoRows {
|
||||
// Insert new (disabled by default)
|
||||
_, err = DB.ExecContext(ctx, `
|
||||
INSERT INTO model_catalog (provider_config_id, model_id, display_name,
|
||||
INSERT INTO model_catalog (provider_config_id, model_id, display_name, model_type,
|
||||
capabilities, pricing, visibility, last_synced_at)
|
||||
VALUES ($1, $2, $3, $4, $5, 'disabled', $6)`,
|
||||
providerConfigID, e.ModelID, e.DisplayName, capsJSON, pricingJSON, now)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, 'disabled', $7)`,
|
||||
providerConfigID, e.ModelID, e.DisplayName, modelType, capsJSON, pricingJSON, now)
|
||||
if err != nil {
|
||||
return added, updated, fmt.Errorf("insert %s: %w", e.ModelID, err)
|
||||
}
|
||||
@@ -51,10 +57,10 @@ func (s *CatalogStore) UpsertFromSync(ctx context.Context, providerConfigID stri
|
||||
} else if err == nil {
|
||||
// Update existing (preserve visibility)
|
||||
_, err = DB.ExecContext(ctx, `
|
||||
UPDATE model_catalog SET display_name = $1, capabilities = $2,
|
||||
pricing = $3, last_synced_at = $4
|
||||
WHERE id = $5`,
|
||||
e.DisplayName, capsJSON, pricingJSON, now, existingID)
|
||||
UPDATE model_catalog SET display_name = $1, model_type = $2, capabilities = $3,
|
||||
pricing = $4, last_synced_at = $5
|
||||
WHERE id = $6`,
|
||||
e.DisplayName, modelType, capsJSON, pricingJSON, now, existingID)
|
||||
if err != nil {
|
||||
return added, updated, fmt.Errorf("update %s: %w", e.ModelID, err)
|
||||
}
|
||||
@@ -190,7 +196,7 @@ func scanCatalogEntry(row *sql.Row) (*models.CatalogEntry, error) {
|
||||
var displayName sql.NullString
|
||||
var lastSynced sql.NullTime
|
||||
err := row.Scan(
|
||||
&e.ID, &e.ProviderConfigID, &e.ModelID, &displayName,
|
||||
&e.ID, &e.ProviderConfigID, &e.ModelID, &displayName, &e.ModelType,
|
||||
&capsJSON, &pricingJSON, &e.Visibility, &lastSynced,
|
||||
&e.CreatedAt, &e.UpdatedAt,
|
||||
)
|
||||
@@ -217,7 +223,7 @@ func scanCatalogEntries(rows *sql.Rows) ([]models.CatalogEntry, error) {
|
||||
var displayName sql.NullString
|
||||
var lastSynced sql.NullTime
|
||||
err := rows.Scan(
|
||||
&e.ID, &e.ProviderConfigID, &e.ModelID, &displayName,
|
||||
&e.ID, &e.ProviderConfigID, &e.ModelID, &displayName, &e.ModelType,
|
||||
&capsJSON, &pricingJSON, &e.Visibility, &lastSynced,
|
||||
&e.CreatedAt, &e.UpdatedAt,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user