feat: add agent skills and guardrails functionality
- Introduced new tools for managing agent skills and guardrails, including add, remove, and list operations. - Updated README.md to document new commands and usage patterns for skills and guardrails. - Enhanced server configuration to support longer read and write timeouts. - Increased maximum upload size for files to 100 MB and adjusted related configurations. - Created database migrations for agent skills, guardrails, and their associations with projects. - Updated relevant code files to integrate new skills and guardrails into the application logic.
This commit is contained in:
294
internal/store/skills.go
Normal file
294
internal/store/skills.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
ext "git.warky.dev/wdevs/amcs/internal/types"
|
||||
)
|
||||
|
||||
// Agent Skills
|
||||
|
||||
func (db *DB) AddSkill(ctx context.Context, skill ext.AgentSkill) (ext.AgentSkill, error) {
|
||||
if skill.Tags == nil {
|
||||
skill.Tags = []string{}
|
||||
}
|
||||
row := db.pool.QueryRow(ctx, `
|
||||
insert into agent_skills (name, description, content, tags)
|
||||
values ($1, $2, $3, $4)
|
||||
returning id, created_at, updated_at
|
||||
`, skill.Name, skill.Description, skill.Content, skill.Tags)
|
||||
|
||||
created := skill
|
||||
if err := row.Scan(&created.ID, &created.CreatedAt, &created.UpdatedAt); err != nil {
|
||||
return ext.AgentSkill{}, fmt.Errorf("insert agent skill: %w", err)
|
||||
}
|
||||
return created, nil
|
||||
}
|
||||
|
||||
func (db *DB) RemoveSkill(ctx context.Context, id uuid.UUID) error {
|
||||
tag, err := db.pool.Exec(ctx, `delete from agent_skills where id = $1`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete agent skill: %w", err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("skill not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) ListSkills(ctx context.Context, tag string) ([]ext.AgentSkill, error) {
|
||||
q := `select id, name, description, content, tags, created_at, updated_at from agent_skills`
|
||||
args := []any{}
|
||||
if t := strings.TrimSpace(tag); t != "" {
|
||||
args = append(args, t)
|
||||
q += fmt.Sprintf(" where $%d = any(tags)", len(args))
|
||||
}
|
||||
q += " order by name"
|
||||
|
||||
rows, err := db.pool.Query(ctx, q, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list agent skills: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var skills []ext.AgentSkill
|
||||
for rows.Next() {
|
||||
var s ext.AgentSkill
|
||||
var desc *string
|
||||
if err := rows.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan agent skill: %w", err)
|
||||
}
|
||||
s.Description = strVal(desc)
|
||||
if s.Tags == nil {
|
||||
s.Tags = []string{}
|
||||
}
|
||||
skills = append(skills, s)
|
||||
}
|
||||
return skills, rows.Err()
|
||||
}
|
||||
|
||||
func (db *DB) GetSkill(ctx context.Context, id uuid.UUID) (ext.AgentSkill, error) {
|
||||
row := db.pool.QueryRow(ctx, `
|
||||
select id, name, description, content, tags, created_at, updated_at
|
||||
from agent_skills where id = $1
|
||||
`, id)
|
||||
|
||||
var s ext.AgentSkill
|
||||
var desc *string
|
||||
if err := row.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil {
|
||||
return ext.AgentSkill{}, fmt.Errorf("get agent skill: %w", err)
|
||||
}
|
||||
s.Description = strVal(desc)
|
||||
if s.Tags == nil {
|
||||
s.Tags = []string{}
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Agent Guardrails
|
||||
|
||||
func (db *DB) AddGuardrail(ctx context.Context, g ext.AgentGuardrail) (ext.AgentGuardrail, error) {
|
||||
if g.Tags == nil {
|
||||
g.Tags = []string{}
|
||||
}
|
||||
if g.Severity == "" {
|
||||
g.Severity = "medium"
|
||||
}
|
||||
row := db.pool.QueryRow(ctx, `
|
||||
insert into agent_guardrails (name, description, content, severity, tags)
|
||||
values ($1, $2, $3, $4, $5)
|
||||
returning id, created_at, updated_at
|
||||
`, g.Name, g.Description, g.Content, g.Severity, g.Tags)
|
||||
|
||||
created := g
|
||||
if err := row.Scan(&created.ID, &created.CreatedAt, &created.UpdatedAt); err != nil {
|
||||
return ext.AgentGuardrail{}, fmt.Errorf("insert agent guardrail: %w", err)
|
||||
}
|
||||
return created, nil
|
||||
}
|
||||
|
||||
func (db *DB) RemoveGuardrail(ctx context.Context, id uuid.UUID) error {
|
||||
tag, err := db.pool.Exec(ctx, `delete from agent_guardrails where id = $1`, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete agent guardrail: %w", err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("guardrail not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) ListGuardrails(ctx context.Context, tag, severity string) ([]ext.AgentGuardrail, error) {
|
||||
args := []any{}
|
||||
conditions := []string{}
|
||||
|
||||
if t := strings.TrimSpace(tag); t != "" {
|
||||
args = append(args, t)
|
||||
conditions = append(conditions, fmt.Sprintf("$%d = any(tags)", len(args)))
|
||||
}
|
||||
if s := strings.TrimSpace(severity); s != "" {
|
||||
args = append(args, s)
|
||||
conditions = append(conditions, fmt.Sprintf("severity = $%d", len(args)))
|
||||
}
|
||||
|
||||
q := `select id, name, description, content, severity, tags, created_at, updated_at from agent_guardrails`
|
||||
if len(conditions) > 0 {
|
||||
q += " where " + strings.Join(conditions, " and ")
|
||||
}
|
||||
q += " order by name"
|
||||
|
||||
rows, err := db.pool.Query(ctx, q, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list agent guardrails: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var guardrails []ext.AgentGuardrail
|
||||
for rows.Next() {
|
||||
var g ext.AgentGuardrail
|
||||
var desc *string
|
||||
if err := rows.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan agent guardrail: %w", err)
|
||||
}
|
||||
g.Description = strVal(desc)
|
||||
if g.Tags == nil {
|
||||
g.Tags = []string{}
|
||||
}
|
||||
guardrails = append(guardrails, g)
|
||||
}
|
||||
return guardrails, rows.Err()
|
||||
}
|
||||
|
||||
func (db *DB) GetGuardrail(ctx context.Context, id uuid.UUID) (ext.AgentGuardrail, error) {
|
||||
row := db.pool.QueryRow(ctx, `
|
||||
select id, name, description, content, severity, tags, created_at, updated_at
|
||||
from agent_guardrails where id = $1
|
||||
`, id)
|
||||
|
||||
var g ext.AgentGuardrail
|
||||
var desc *string
|
||||
if err := row.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil {
|
||||
return ext.AgentGuardrail{}, fmt.Errorf("get agent guardrail: %w", err)
|
||||
}
|
||||
g.Description = strVal(desc)
|
||||
if g.Tags == nil {
|
||||
g.Tags = []string{}
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// Project Skills
|
||||
|
||||
func (db *DB) AddProjectSkill(ctx context.Context, projectID, skillID uuid.UUID) error {
|
||||
_, err := db.pool.Exec(ctx, `
|
||||
insert into project_skills (project_id, skill_id)
|
||||
values ($1, $2)
|
||||
on conflict do nothing
|
||||
`, projectID, skillID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add project skill: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) RemoveProjectSkill(ctx context.Context, projectID, skillID uuid.UUID) error {
|
||||
tag, err := db.pool.Exec(ctx, `
|
||||
delete from project_skills where project_id = $1 and skill_id = $2
|
||||
`, projectID, skillID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove project skill: %w", err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("project skill link not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) ListProjectSkills(ctx context.Context, projectID uuid.UUID) ([]ext.AgentSkill, error) {
|
||||
rows, err := db.pool.Query(ctx, `
|
||||
select s.id, s.name, s.description, s.content, s.tags, s.created_at, s.updated_at
|
||||
from agent_skills s
|
||||
join project_skills ps on ps.skill_id = s.id
|
||||
where ps.project_id = $1
|
||||
order by s.name
|
||||
`, projectID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list project skills: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var skills []ext.AgentSkill
|
||||
for rows.Next() {
|
||||
var s ext.AgentSkill
|
||||
var desc *string
|
||||
if err := rows.Scan(&s.ID, &s.Name, &desc, &s.Content, &s.Tags, &s.CreatedAt, &s.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan project skill: %w", err)
|
||||
}
|
||||
s.Description = strVal(desc)
|
||||
if s.Tags == nil {
|
||||
s.Tags = []string{}
|
||||
}
|
||||
skills = append(skills, s)
|
||||
}
|
||||
return skills, rows.Err()
|
||||
}
|
||||
|
||||
// Project Guardrails
|
||||
|
||||
func (db *DB) AddProjectGuardrail(ctx context.Context, projectID, guardrailID uuid.UUID) error {
|
||||
_, err := db.pool.Exec(ctx, `
|
||||
insert into project_guardrails (project_id, guardrail_id)
|
||||
values ($1, $2)
|
||||
on conflict do nothing
|
||||
`, projectID, guardrailID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("add project guardrail: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) RemoveProjectGuardrail(ctx context.Context, projectID, guardrailID uuid.UUID) error {
|
||||
tag, err := db.pool.Exec(ctx, `
|
||||
delete from project_guardrails where project_id = $1 and guardrail_id = $2
|
||||
`, projectID, guardrailID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("remove project guardrail: %w", err)
|
||||
}
|
||||
if tag.RowsAffected() == 0 {
|
||||
return fmt.Errorf("project guardrail link not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *DB) ListProjectGuardrails(ctx context.Context, projectID uuid.UUID) ([]ext.AgentGuardrail, error) {
|
||||
rows, err := db.pool.Query(ctx, `
|
||||
select g.id, g.name, g.description, g.content, g.severity, g.tags, g.created_at, g.updated_at
|
||||
from agent_guardrails g
|
||||
join project_guardrails pg on pg.guardrail_id = g.id
|
||||
where pg.project_id = $1
|
||||
order by g.name
|
||||
`, projectID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list project guardrails: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var guardrails []ext.AgentGuardrail
|
||||
for rows.Next() {
|
||||
var g ext.AgentGuardrail
|
||||
var desc *string
|
||||
if err := rows.Scan(&g.ID, &g.Name, &desc, &g.Content, &g.Severity, &g.Tags, &g.CreatedAt, &g.UpdatedAt); err != nil {
|
||||
return nil, fmt.Errorf("scan project guardrail: %w", err)
|
||||
}
|
||||
g.Description = strVal(desc)
|
||||
if g.Tags == nil {
|
||||
g.Tags = []string{}
|
||||
}
|
||||
guardrails = append(guardrails, g)
|
||||
}
|
||||
return guardrails, rows.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user