package services import ( "context" "git.golem15.com/golem15/sm-golem-plugin/models" "gorm.io/gorm" ) // The functions below port the WinterCMS plugin's Settings model over the // golem15_golem_models rows (in sort_order, then id order). Every config // they return is an operator's model: Trusted is set. A nil db means no // model is configured. // Models is Settings::getModels: the enabled models, in order. Each keeps // its Position among all models, enabled or not, as PHP's array_filter // keeps the repeater keys. func Models(ctx context.Context, db *gorm.DB) ([]*ModelConfig, error) { if db == nil { return nil, nil } var rows []models.AiModel if err := db.WithContext(ctx).Order("sort_order, id").Find(&rows).Error; err != nil { return nil, err } var out []*ModelConfig for i := range rows { if !rows[i].IsEnabled { continue } c := modelConfigFrom(&rows[i]) c.Position = i out = append(out, c) } return out, nil } // DefaultModel is Settings::getDefaultModel: the first enabled model // marked default, else the model at position 0 when it is enabled. PHP's // `$models[0] ?? null` reads the filtered array by key, so a disabled // first model means no default even when later models are enabled. func DefaultModel(ctx context.Context, db *gorm.DB) (*ModelConfig, error) { all, err := Models(ctx, db) if err != nil { return nil, err } for _, m := range all { if m.isDefault { return m, nil } } for _, m := range all { if m.Position == 0 { return m, nil } } return nil, nil } // VisionModel is Settings::getVisionModel: the first enabled model that // accepts images. func VisionModel(ctx context.Context, db *gorm.DB) (*ModelConfig, error) { return firstModel(ctx, db, func(m *ModelConfig) bool { return m.AcceptsImages }) } // ImageModel is Settings::getImageModel: the first enabled model that // generates images. func ImageModel(ctx context.Context, db *gorm.DB) (*ModelConfig, error) { return firstModel(ctx, db, func(m *ModelConfig) bool { return m.GeneratesImages }) } // FileModel is Settings::getFileModel: the first enabled model with a // files endpoint. func FileModel(ctx context.Context, db *gorm.DB) (*ModelConfig, error) { return firstModel(ctx, db, func(m *ModelConfig) bool { return m.HasFilesEndpoint }) } // ModelByName is Settings::getModelByName: the first enabled model named // name. func ModelByName(ctx context.Context, db *gorm.DB, name string) (*ModelConfig, error) { return firstModel(ctx, db, func(m *ModelConfig) bool { return m.Name == name }) } func firstModel(ctx context.Context, db *gorm.DB, match func(*ModelConfig) bool) (*ModelConfig, error) { all, err := Models(ctx, db) if err != nil { return nil, err } for _, m := range all { if match(m) { return m, nil } } return nil, nil } func modelConfigFrom(row *models.AiModel) *ModelConfig { c := &ModelConfig{ Name: row.Name, Adapter: row.Adapter, APIKey: row.APIKey.Reveal(), Model: row.Model, HasFilesEndpoint: row.HasFilesEndpoint, AcceptsImages: row.AcceptsImages, GeneratesImages: row.GeneratesImages, Trusted: true, isDefault: row.IsDefault, } if row.BaseURL != nil { c.BaseURL = *row.BaseURL } if row.SystemPrompt != nil { c.SystemPrompt = *row.SystemPrompt } if row.MaxCompletionTokens != nil { c.MaxCompletionTokens = *row.MaxCompletionTokens } return c }