- golem15_golem_models (encrypted write-only api_key) with its cabana list and form under golem15.golem.access_settings, and the Settings lookups (Models, DefaultModel with PHP's position-0 quirk, Vision, Image, File, ByName), every one Trusted - golem:import-settings copies the WinterCMS golem_settings repeater once - AnthropicAdapter; SendStream, SendFile/SendFilePath/SendFileReader, SendWithModel, SendToImageModel, SendToVisionModel, GenerateImage, Ask - PromptFactory; README documents the config keys, the environment variable, the command and the admin screen
91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
package controllers
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"git.golem15.com/golem15/sm-golem-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/cabana"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// PermissionAccessSettings guards the AI models screen.
|
|
const PermissionAccessSettings = "golem15.golem.access_settings"
|
|
|
|
// ModelsAdminController is the AI models list and form
|
|
// (golem15.golem.models). The API key is write-only: the form shows it
|
|
// redacted, and an update that sends it back empty or redacted keeps the
|
|
// stored key.
|
|
type ModelsAdminController struct{}
|
|
|
|
var (
|
|
_ pact.AdminController = ModelsAdminController{}
|
|
_ pact.AdminPermissioned = ModelsAdminController{}
|
|
_ pact.AdminRecordSource = ModelsAdminController{}
|
|
_ pact.FormBeforeCreate = ModelsAdminController{}
|
|
_ pact.FormBeforeUpdate = ModelsAdminController{}
|
|
)
|
|
|
|
// ID is golem15.golem.models.
|
|
func (ModelsAdminController) ID() string { return "golem15.golem.models" }
|
|
|
|
// ModelName is the model's WinterCMS class name.
|
|
func (ModelsAdminController) ModelName() string { return `Golem15\Golem\Models\AiModel` }
|
|
|
|
// ConfigDir holds config_list.yaml and config_form.yaml.
|
|
func (ModelsAdminController) ConfigDir() string { return "controllers/aimodels" }
|
|
|
|
// RequiredPermissions is golem15.golem.access_settings.
|
|
func (ModelsAdminController) RequiredPermissions() []string {
|
|
return []string{PermissionAccessSettings}
|
|
}
|
|
|
|
// NewRecord returns an empty AI model.
|
|
func (ModelsAdminController) NewRecord() any { return &models.AiModel{} }
|
|
|
|
// redacted is how lagoon.Encrypted serializes, so how the form shows the
|
|
// stored key.
|
|
const redacted = "[redacted]"
|
|
|
|
func keyMissing(m *models.AiModel) bool {
|
|
k := m.APIKey.Reveal()
|
|
return k == "" || k == redacted
|
|
}
|
|
|
|
// FormBeforeCreate requires an API key.
|
|
func (ModelsAdminController) FormBeforeCreate(_ context.Context, model any) error {
|
|
m, ok := model.(*models.AiModel)
|
|
if !ok {
|
|
return errors.New("golem: unexpected admin record")
|
|
}
|
|
if keyMissing(m) {
|
|
return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"The api key field is required."}}}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// FormBeforeUpdate keeps the stored key when the form sent it back empty
|
|
// or redacted.
|
|
func (ModelsAdminController) FormBeforeUpdate(ctx context.Context, model any) error {
|
|
m, ok := model.(*models.AiModel)
|
|
if !ok {
|
|
return errors.New("golem: unexpected admin record")
|
|
}
|
|
if !keyMissing(m) {
|
|
return nil
|
|
}
|
|
tx, ok := cabana.TxFromContext(ctx)
|
|
if !ok {
|
|
return errors.New("golem: no transaction for the model update")
|
|
}
|
|
var stored models.AiModel
|
|
if err := tx.Select("api_key").Where("id = ?", m.ID).Take(&stored).Error; err != nil {
|
|
return err
|
|
}
|
|
m.APIKey = stored.APIKey
|
|
if keyMissing(m) {
|
|
return &cabana.ValidationError{Details: map[string]any{"api_key": []string{"The api key field is required."}}}
|
|
}
|
|
return nil
|
|
}
|