Files
goclaw/internal/store/skill_evolution_store.go

125 lines
6.7 KiB
Go

package store
import (
"context"
"encoding/json"
"time"
"github.com/google/uuid"
)
const (
SkillEvolutionModeSuggestOnly = "suggest_only"
SkillEvolutionModeAutoAnalyze = "auto_analyze"
SkillUsageStatusStarted = "started"
SkillUsageStatusSucceeded = "succeeded"
SkillUsageStatusFailed = "failed"
SkillUsageStatusAbandoned = "abandoned"
SkillSuggestionStatusPending = "pending"
SkillSuggestionStatusApproved = "approved"
SkillSuggestionStatusRejected = "rejected"
SkillSuggestionStatusApplied = "applied"
)
// SkillEvolutionSettings stores tenant-scoped self-evolution controls for one skill.
type SkillEvolutionSettings struct {
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
SkillID uuid.UUID `json:"skill_id" db:"skill_id"`
Enabled bool `json:"enabled" db:"enabled"`
Mode string `json:"mode" db:"mode"`
LastAnalyzedAt *time.Time `json:"last_analyzed_at,omitempty" db:"last_analyzed_at"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// SkillUsageMetric is a durable usage event emitted by trusted runtime paths.
type SkillUsageMetric struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
SkillID uuid.UUID `json:"skill_id" db:"skill_id"`
SkillSlug string `json:"skill_slug" db:"skill_slug"`
SkillVersion int `json:"skill_version" db:"skill_version"`
AgentID uuid.UUID `json:"agent_id,omitempty" db:"agent_id"`
UserID string `json:"user_id,omitempty" db:"user_id"`
SessionKey string `json:"session_key,omitempty" db:"session_key"`
TraceID string `json:"trace_id,omitempty" db:"trace_id"`
InvocationID string `json:"invocation_id,omitempty" db:"invocation_id"`
InvocationSource string `json:"invocation_source" db:"invocation_source"`
Status string `json:"status" db:"status"`
FailureReason string `json:"failure_reason,omitempty" db:"failure_reason"`
ToolCallsCount int `json:"tool_calls_count" db:"tool_calls_count"`
DurationMs int64 `json:"duration_ms" db:"duration_ms"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
type SkillFailureReason struct {
Reason string `json:"reason"`
Count int `json:"count"`
LastSeen time.Time `json:"last_seen"`
}
type SkillUsageStats struct {
SkillID uuid.UUID `json:"skill_id"`
TotalCalls int `json:"total_calls"`
Started int `json:"started"`
Succeeded int `json:"succeeded"`
Failed int `json:"failed"`
Abandoned int `json:"abandoned"`
SuccessRate float64 `json:"success_rate"`
FailureRate float64 `json:"failure_rate"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
TopFailureReasons []SkillFailureReason `json:"top_failure_reasons,omitempty"`
}
type SkillImprovementSuggestion struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
SkillID uuid.UUID `json:"skill_id" db:"skill_id"`
SkillSlug string `json:"skill_slug" db:"skill_slug"`
SuggestionType string `json:"suggestion_type" db:"suggestion_type"`
Status string `json:"status" db:"status"`
Reason string `json:"reason" db:"reason"`
Evidence json.RawMessage `json:"evidence,omitempty" db:"evidence"`
DraftPatch json.RawMessage `json:"draft_patch,omitempty" db:"draft_patch"`
TargetFile string `json:"target_file,omitempty" db:"target_file"`
CreatedByActorType string `json:"created_by_actor_type,omitempty" db:"created_by_actor_type"`
CreatedByActorID string `json:"created_by_actor_id,omitempty" db:"created_by_actor_id"`
ReviewedByActorType string `json:"reviewed_by_actor_type,omitempty" db:"reviewed_by_actor_type"`
ReviewedByActorID string `json:"reviewed_by_actor_id,omitempty" db:"reviewed_by_actor_id"`
ReviewedAt *time.Time `json:"reviewed_at,omitempty" db:"reviewed_at"`
AppliedVersion *int `json:"applied_version,omitempty" db:"applied_version"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
type SkillVersion struct {
ID uuid.UUID `json:"id" db:"id"`
TenantID uuid.UUID `json:"tenant_id" db:"tenant_id"`
SkillID uuid.UUID `json:"skill_id" db:"skill_id"`
Version int `json:"version" db:"version"`
ContentHash string `json:"content_hash" db:"content_hash"`
ChangedFiles json.RawMessage `json:"changed_files,omitempty" db:"changed_files"`
CreatedByActorType string `json:"created_by_actor_type,omitempty" db:"created_by_actor_type"`
CreatedByActorID string `json:"created_by_actor_id,omitempty" db:"created_by_actor_id"`
CreatedFromSuggestionID *uuid.UUID `json:"created_from_suggestion_id,omitempty" db:"created_from_suggestion_id"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
type SkillEvolutionStore interface {
GetSettings(ctx context.Context, skillID uuid.UUID) (*SkillEvolutionSettings, error)
UpsertSettings(ctx context.Context, settings SkillEvolutionSettings) (*SkillEvolutionSettings, error)
RecordUsage(ctx context.Context, metric SkillUsageMetric) error
AggregateUsage(ctx context.Context, skillID uuid.UUID, since *time.Time) (*SkillUsageStats, error)
ListUsage(ctx context.Context, skillID uuid.UUID, limit int) ([]SkillUsageMetric, error)
CreateSuggestion(ctx context.Context, suggestion SkillImprovementSuggestion) (*SkillImprovementSuggestion, error)
ListSuggestions(ctx context.Context, skillID uuid.UUID, status string, limit int) ([]SkillImprovementSuggestion, error)
GetSuggestion(ctx context.Context, id uuid.UUID) (*SkillImprovementSuggestion, error)
UpdateSuggestionStatus(ctx context.Context, id uuid.UUID, status, actorType, actorID string) (*SkillImprovementSuggestion, error)
MarkSuggestionApplied(ctx context.Context, id uuid.UUID, version int, actorType, actorID string) (*SkillImprovementSuggestion, error)
CreateSkillVersion(ctx context.Context, version SkillVersion) (*SkillVersion, error)
ListSkillVersions(ctx context.Context, skillID uuid.UUID, limit int) ([]SkillVersion, error)
GetSkillVersion(ctx context.Context, skillID uuid.UUID, version int) (*SkillVersion, error)
}