* Introduce project_personas table with foreign keys to projects and agent_personas * Add project_skills table with foreign key to projects and agent_skills * Include override boolean field in agent_persona_skills and project_skills * Update schema and migration files to reflect new tables and fields * Enhance CORS handling to reflect request origin
This commit is contained in:
@@ -234,6 +234,7 @@ func (db *DB) GetPersona(ctx context.Context, name string, detail bool, override
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Tags: s.Tags,
|
||||
Override: s.Override,
|
||||
}
|
||||
if detail {
|
||||
entry.Content = s.Content
|
||||
@@ -341,6 +342,7 @@ func (db *DB) GetPersonaManifest(ctx context.Context, name string) (ext.PersonaM
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Override: s.Override,
|
||||
})
|
||||
}
|
||||
for _, g := range guardrails {
|
||||
@@ -567,11 +569,12 @@ func (db *DB) RemovePersonaPart(ctx context.Context, personaName, partName strin
|
||||
// Persona-Skill links
|
||||
// ──────────────────────────────────────────────
|
||||
|
||||
func (db *DB) AddPersonaSkill(ctx context.Context, personaID, skillID int64) error {
|
||||
func (db *DB) AddPersonaSkill(ctx context.Context, personaID, skillID int64, override bool) error {
|
||||
_, err := db.pool.Exec(ctx, `
|
||||
insert into agent_persona_skills (persona_id, skill_id)
|
||||
values ($1, $2) on conflict do nothing
|
||||
`, personaID, skillID)
|
||||
insert into agent_persona_skills (persona_id, skill_id, override)
|
||||
values ($1, $2, $3)
|
||||
on conflict (persona_id, skill_id) do update set override = excluded.override
|
||||
`, personaID, skillID, override)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add persona skill: %w", err)
|
||||
}
|
||||
@@ -1058,7 +1061,7 @@ func (db *DB) fetchPartsByNames(ctx context.Context, names []string) ([]rawPart,
|
||||
|
||||
func (db *DB) listPersonaSkills(ctx context.Context, personaID int64) ([]AgentSkillRow, error) {
|
||||
rows, err := db.pool.Query(ctx, `
|
||||
select s.id, s.name, s.description, s.content, s.tags::text[]
|
||||
select s.id, s.name, s.description, s.content, s.tags::text[], aps.override
|
||||
from agent_skills s
|
||||
join agent_persona_skills aps on aps.skill_id = s.id
|
||||
where aps.persona_id = $1
|
||||
@@ -1073,7 +1076,7 @@ func (db *DB) listPersonaSkills(ctx context.Context, personaID int64) ([]AgentSk
|
||||
for rows.Next() {
|
||||
var s AgentSkillRow
|
||||
var tags []string
|
||||
if err := rows.Scan(&s.ID, &s.Name, &s.Description, &s.Content, &tags); err != nil {
|
||||
if err := rows.Scan(&s.ID, &s.Name, &s.Description, &s.Content, &tags, &s.Override); err != nil {
|
||||
return nil, fmt.Errorf("scan persona skill: %w", err)
|
||||
}
|
||||
s.Tags = nilToEmptyStrings(tags)
|
||||
@@ -1139,6 +1142,7 @@ type AgentSkillRow struct {
|
||||
Description string
|
||||
Content string
|
||||
Tags []string
|
||||
Override bool
|
||||
}
|
||||
|
||||
type AgentGuardrailRow struct {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
|
||||
ext "git.warky.dev/wdevs/amcs/internal/types"
|
||||
)
|
||||
|
||||
func (db *DB) AddProjectPersona(ctx context.Context, projectID, personaID int64, isDefault bool) error {
|
||||
tx, err := db.pool.Begin(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("begin add project persona: %w", err)
|
||||
}
|
||||
defer func() { _ = tx.Rollback(ctx) }()
|
||||
|
||||
if _, err := tx.Exec(ctx, `select pg_advisory_xact_lock($1)`, projectID); err != nil {
|
||||
return fmt.Errorf("lock project persona defaults: %w", err)
|
||||
}
|
||||
if isDefault {
|
||||
if _, err := tx.Exec(ctx, `update project_personas set is_default = false where project_id = $1`, projectID); err != nil {
|
||||
return fmt.Errorf("clear default project persona: %w", err)
|
||||
}
|
||||
}
|
||||
if _, err := tx.Exec(ctx, `
|
||||
insert into project_personas (project_id, persona_id, is_default)
|
||||
values ($1, $2, $3)
|
||||
on conflict (project_id, persona_id) do update set is_default = excluded.is_default
|
||||
`, projectID, personaID, isDefault); err != nil {
|
||||
return fmt.Errorf("add project persona: %w", err)
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return fmt.Errorf("commit add project persona: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) RemoveProjectPersona(ctx context.Context, projectID, personaID int64) error {
|
||||
tag, err := db.pool.Exec(ctx, `delete from project_personas where project_id = $1 and persona_id = $2`, projectID, personaID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove project persona: %w", err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("project persona link not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) ListProjectPersonas(ctx context.Context, projectID int64) ([]ext.ProjectPersona, error) {
|
||||
rows, err := db.pool.Query(ctx, `
|
||||
select p.id, p.guid, p.name, p.description, p.summary, p.detail,
|
||||
p.compiled_summary, p.compiled_detail, p.compiled_at, p.tags::text[],
|
||||
p.created_at, p.updated_at, pp.is_default
|
||||
from agent_personas p
|
||||
join project_personas pp on pp.persona_id = p.id
|
||||
where pp.project_id = $1
|
||||
order by pp.is_default desc, p.name
|
||||
`, projectID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list project personas: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var result []ext.ProjectPersona
|
||||
for rows.Next() {
|
||||
var item ext.ProjectPersona
|
||||
var tags []string
|
||||
if err := rows.Scan(&item.Persona.ID, &item.Persona.GUID, &item.Persona.Name,
|
||||
&item.Persona.Description, &item.Persona.Summary, &item.Persona.Detail,
|
||||
&item.Persona.CompiledSummary, &item.Persona.CompiledDetail, &item.Persona.CompiledAt,
|
||||
&tags, &item.Persona.CreatedAt, &item.Persona.UpdatedAt, &item.IsDefault); err != nil {
|
||||
return nil, fmt.Errorf("scan project persona: %w", err)
|
||||
}
|
||||
item.Persona.Tags = nilToEmptyStrings(tags)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (db *DB) GetDefaultProjectPersona(ctx context.Context, projectID int64) (*ext.Persona, error) {
|
||||
row := db.pool.QueryRow(ctx, `
|
||||
select p.id, p.guid, p.name, p.description, p.summary, p.detail,
|
||||
p.compiled_summary, p.compiled_detail, p.compiled_at, p.tags::text[],
|
||||
p.created_at, p.updated_at
|
||||
from agent_personas p
|
||||
join project_personas pp on pp.persona_id = p.id
|
||||
where pp.project_id = $1 and pp.is_default
|
||||
`, projectID)
|
||||
persona, err := scanPersona(row)
|
||||
if err == pgx.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get default project persona: %w", err)
|
||||
}
|
||||
return &persona, nil
|
||||
}
|
||||
@@ -116,6 +116,24 @@ func scanSkill(row skillScanner) (ext.AgentSkill, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func normalizeSkillSlices(skill *ext.AgentSkill) {
|
||||
if skill.Tags == nil {
|
||||
skill.Tags = []string{}
|
||||
}
|
||||
if skill.LanguageTags == nil {
|
||||
skill.LanguageTags = []string{}
|
||||
}
|
||||
if skill.LibraryTags == nil {
|
||||
skill.LibraryTags = []string{}
|
||||
}
|
||||
if skill.FrameworkTags == nil {
|
||||
skill.FrameworkTags = []string{}
|
||||
}
|
||||
if skill.DomainTags == nil {
|
||||
skill.DomainTags = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
func (db *DB) GetSkill(ctx context.Context, id int64) (ext.AgentSkill, error) {
|
||||
row := db.pool.QueryRow(ctx, `select `+skillSelectCols+` from agent_skills where id = $1`, id)
|
||||
s, err := scanSkill(row)
|
||||
@@ -278,12 +296,12 @@ func (db *DB) GetGuardrail(ctx context.Context, id int64) (ext.AgentGuardrail, e
|
||||
|
||||
// Project Skills
|
||||
|
||||
func (db *DB) AddProjectSkill(ctx context.Context, projectID, skillID int64) error {
|
||||
func (db *DB) AddProjectSkill(ctx context.Context, projectID, skillID int64, override bool) error {
|
||||
_, err := db.pool.Exec(ctx, `
|
||||
insert into project_skills (project_id, skill_id)
|
||||
values ($1, $2)
|
||||
on conflict do nothing
|
||||
`, projectID, skillID)
|
||||
insert into project_skills (project_id, skill_id, override)
|
||||
values ($1, $2, $3)
|
||||
on conflict (project_id, skill_id) do update set override = excluded.override
|
||||
`, projectID, skillID, override)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add project skill: %w", err)
|
||||
}
|
||||
@@ -305,7 +323,9 @@ func (db *DB) RemoveProjectSkill(ctx context.Context, projectID, skillID int64)
|
||||
|
||||
func (db *DB) ListProjectSkills(ctx context.Context, projectID int64) ([]ext.AgentSkill, error) {
|
||||
rows, err := db.pool.Query(ctx, `
|
||||
select s.`+skillSelectCols+`
|
||||
select s.id, s.name, s.description, s.content, s.tags::text[],
|
||||
s.language_tags::text[], s.library_tags::text[], s.framework_tags::text[],
|
||||
s.domain_tags::text[], s.created_at, s.updated_at, ps.override
|
||||
from agent_skills s
|
||||
join project_skills ps on ps.skill_id = s.id
|
||||
where ps.project_id = $1
|
||||
@@ -318,10 +338,13 @@ func (db *DB) ListProjectSkills(ctx context.Context, projectID int64) ([]ext.Age
|
||||
|
||||
var skills []ext.AgentSkill
|
||||
for rows.Next() {
|
||||
s, err := scanSkill(rows)
|
||||
if err != nil {
|
||||
var s ext.AgentSkill
|
||||
if err := rows.Scan(&s.ID, &s.Name, &s.Description, &s.Content, &s.Tags,
|
||||
&s.LanguageTags, &s.LibraryTags, &s.FrameworkTags, &s.DomainTags,
|
||||
&s.CreatedAt, &s.UpdatedAt, &s.Override); err != nil {
|
||||
return nil, fmt.Errorf("scan project skill: %w", err)
|
||||
}
|
||||
normalizeSkillSlices(&s)
|
||||
skills = append(skills, s)
|
||||
}
|
||||
return skills, rows.Err()
|
||||
|
||||
Reference in New Issue
Block a user