feat: model expert catalog categories
This commit is contained in:
@@ -33,6 +33,7 @@ func runVersionedMigrations(database *gorm.DB) error {
|
||||
{version: 1, name: "normalize_project_identifiers", run: normalizeLegacyProjectIdentifiers},
|
||||
{version: 2, name: "deduplicate_project_tags", run: deduplicateLegacyProjectTags},
|
||||
{version: 3, name: "seed_ai_experts", run: seedAIExperts},
|
||||
{version: 4, name: "normalize_ai_expert_categories", run: normalizeAIExpertCategories},
|
||||
}
|
||||
for _, migration := range migrations {
|
||||
// Connection callbacks can provide an initialized Gorm session. Start each
|
||||
@@ -69,9 +70,10 @@ func runVersionedMigrations(database *gorm.DB) error {
|
||||
var expertCatalogJSON []byte
|
||||
|
||||
type embeddedExpertCatalog struct {
|
||||
Source string `json:"source"`
|
||||
License string `json:"license"`
|
||||
Experts []embeddedExpertCatalogItem `json:"experts"`
|
||||
Source string `json:"source"`
|
||||
Reference string `json:"reference"`
|
||||
License string `json:"license"`
|
||||
Experts []embeddedExpertCatalogItem `json:"experts"`
|
||||
}
|
||||
|
||||
type embeddedExpertCatalogItem struct {
|
||||
@@ -86,17 +88,19 @@ type embeddedExpertCatalogItem struct {
|
||||
}
|
||||
|
||||
func seedAIExperts(tx *gorm.DB) (map[string]int, error) {
|
||||
var catalog embeddedExpertCatalog
|
||||
if err := json.Unmarshal(expertCatalogJSON, &catalog); err != nil {
|
||||
return nil, fmt.Errorf("decode embedded AI expert catalog: %w", err)
|
||||
catalog, categories, err := loadAIExpertCatalog(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
records := make([]SenlinAgentAIExpert, 0, len(catalog.Experts))
|
||||
for _, expert := range catalog.Experts {
|
||||
category := categories[expert.Category]
|
||||
records = append(records, SenlinAgentAIExpert{
|
||||
Slug: expert.Slug, Category: expert.Category, CategoryName: expert.CategoryName,
|
||||
Slug: expert.Slug, CategoryID: category.ID, CategoryIdentity: category.Identity,
|
||||
Category: expert.Category, CategoryName: expert.CategoryName,
|
||||
Name: expert.Name, Description: expert.Description, Emoji: expert.Emoji,
|
||||
Color: expert.Color, SystemPrompt: expert.SystemPrompt, Source: catalog.Source,
|
||||
SourceLicense: catalog.License, Enabled: true,
|
||||
Reference: catalog.Reference, SourceLicense: catalog.License, Enabled: true,
|
||||
})
|
||||
}
|
||||
if len(records) > 0 {
|
||||
@@ -107,6 +111,63 @@ func seedAIExperts(tx *gorm.DB) (map[string]int, error) {
|
||||
return map[string]int{"inserted": len(records)}, nil
|
||||
}
|
||||
|
||||
func normalizeAIExpertCategories(tx *gorm.DB) (map[string]int, error) {
|
||||
catalog, categories, err := loadAIExpertCatalog(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linked := 0
|
||||
for _, expert := range catalog.Experts {
|
||||
category := categories[expert.Category]
|
||||
result := tx.Model(&SenlinAgentAIExpert{}).Where("slug = ?", expert.Slug).Updates(map[string]any{
|
||||
"category_id": category.ID,
|
||||
"category_identity": category.Identity,
|
||||
"reference": catalog.Reference,
|
||||
})
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
linked += int(result.RowsAffected)
|
||||
}
|
||||
return map[string]int{"categories": len(categories), "linked": linked}, nil
|
||||
}
|
||||
|
||||
func loadAIExpertCatalog(tx *gorm.DB) (embeddedExpertCatalog, map[string]SenlinAgentAIExpertCategory, error) {
|
||||
var catalog embeddedExpertCatalog
|
||||
if err := json.Unmarshal(expertCatalogJSON, &catalog); err != nil {
|
||||
return catalog, nil, fmt.Errorf("decode embedded AI expert catalog: %w", err)
|
||||
}
|
||||
|
||||
categoryNames := make(map[string]string)
|
||||
categoryOrder := make([]string, 0)
|
||||
for _, expert := range catalog.Experts {
|
||||
if _, exists := categoryNames[expert.Category]; exists {
|
||||
continue
|
||||
}
|
||||
categoryNames[expert.Category] = expert.CategoryName
|
||||
categoryOrder = append(categoryOrder, expert.Category)
|
||||
}
|
||||
|
||||
categories := make(map[string]SenlinAgentAIExpertCategory, len(categoryOrder))
|
||||
for _, slug := range categoryOrder {
|
||||
category := SenlinAgentAIExpertCategory{Slug: slug}
|
||||
if err := tx.Where("slug = ?", slug).
|
||||
Attrs(SenlinAgentAIExpertCategory{Name: categoryNames[slug]}).
|
||||
FirstOrCreate(&category).Error; err != nil {
|
||||
return catalog, nil, err
|
||||
}
|
||||
if category.Name != categoryNames[slug] {
|
||||
category.Name = categoryNames[slug]
|
||||
if err := tx.Model(&category).Update("name", category.Name).Error; err != nil {
|
||||
return catalog, nil, err
|
||||
}
|
||||
}
|
||||
categories[slug] = category
|
||||
}
|
||||
return catalog, categories, nil
|
||||
}
|
||||
|
||||
func normalizeLegacyProjectIdentifiers(tx *gorm.DB) (map[string]int, error) {
|
||||
var projects []SenlinAgentProject
|
||||
if err := tx.Model(&SenlinAgentProject{}).Select("id", "owner_id", "identifier").Order("owner_id asc, id asc").Find(&projects).Error; err != nil {
|
||||
|
||||
Reference in New Issue
Block a user