Files
poimen-workflows/internal/memory/activities.go
T

289 lines
7.9 KiB
Go
Raw Normal View History

package memory
import (
"context"
"fmt"
"go.temporal.io/sdk/activity"
)
// Activities memory service activities for Temporal workflows
type Activities struct {
service *Service
}
// NewActivities creates memory service activities
func NewActivities(service *Service) *Activities {
return &Activities{
service: service,
}
}
// CreateKnowledgeActivity creates knowledge record from workflow execution
func (a *Activities) CreateKnowledgeActivity(ctx context.Context, record *KnowledgeRecord) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Creating knowledge", "title", record.Title)
id, err := a.service.CreateKnowledge(ctx, record)
if err != nil {
logger.Error("Failed to create knowledge", "error", err)
return "", err
}
logger.Info("Knowledge created", "id", id)
return id, nil
}
// UpdateKnowledgeActivity updates existing knowledge record
func (a *Activities) UpdateKnowledgeActivity(ctx context.Context, record *KnowledgeRecord) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Updating knowledge", "id", record.ID)
id, err := a.service.UpdateKnowledge(ctx, record)
if err != nil {
logger.Error("Failed to update knowledge", "error", err)
return "", err
}
logger.Info("Knowledge updated", "id", id)
return id, nil
}
// SearchKnowledgeActivity searches knowledge base
func (a *Activities) SearchKnowledgeActivity(ctx context.Context, query string, opts *RetrievalOptions) ([]KnowledgeRecord, error) {
logger := activity.GetLogger(ctx)
logger.Info("Searching knowledge", "query", query)
records, err := a.service.RetrieveKnowledge(ctx, query, opts)
if err != nil {
logger.Error("Search failed", "error", err)
return nil, err
}
logger.Info("Found records", "count", len(records))
return records, nil
}
// GetContextActivity retrieves context for tool/task (three-tier retrieval)
func (a *Activities) GetContextActivity(ctx context.Context, tool, task string, budget int) (*ServiceContext, error) {
logger := activity.GetLogger(ctx)
logger.Info("Getting context", "tool", tool, "task", task)
svcCtx, err := a.service.RetrieveContext(ctx, tool, task, budget)
if err != nil {
logger.Error("Get context failed", "error", err)
return nil, err
}
logger.Info("Retrieved context", "tier", svcCtx.Tier, "lessons", len(svcCtx.Lessons))
return svcCtx, nil
}
// GetVaultActivity lists vault files
func (a *Activities) GetVaultActivity(ctx context.Context) ([]VaultInfo, error) {
logger := activity.GetLogger(ctx)
logger.Info("Fetching vault")
files, err := a.service.GetVault(ctx)
if err != nil {
logger.Error("Get vault failed", "error", err)
return nil, err
}
logger.Info("Vault files", "count", len(files))
return files, nil
}
// HealthCheckActivity checks memory service health
func (a *Activities) HealthCheckActivity(ctx context.Context) (bool, error) {
logger := activity.GetLogger(ctx)
logger.Info("Checking memory service health")
if !a.service.IsHealthy(ctx) {
logger.Warn("Memory service is unhealthy")
return false, fmt.Errorf("memory service unhealthy")
}
logger.Info("Memory service is healthy")
return true, nil
}
// LearnFromExecutionActivity learns from task execution result
func (a *Activities) LearnFromExecutionActivity(ctx context.Context, taskID string, result string, tags []string) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Learning from task execution", "taskID", taskID)
metadata := map[string]interface{}{
"task_id": taskID,
"type": "execution_result",
}
if len(tags) > 0 {
metadata["tags"] = tags
}
id, err := a.service.CreateKnowledge(ctx, &KnowledgeRecord{
Level: "L1",
Title: fmt.Sprintf("Task Execution: %s", taskID),
Content: result,
Source: fmt.Sprintf("workflow://task/%s", taskID),
Metadata: metadata,
})
if err != nil {
logger.Error("Failed to learn from execution", "error", err)
return "", err
}
logger.Info("Learned from execution", "id", id)
return id, nil
}
// DiagnoseIssueActivity diagnoses issue using memory context
func (a *Activities) DiagnoseIssueActivity(ctx context.Context, tool, issue string) ([]string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Diagnosing issue", "tool", tool, "issue", issue)
svcCtx, err := a.service.RetrieveContext(ctx, tool, issue, 8192)
if err != nil {
logger.Error("Diagnosis failed", "error", err)
return nil, err
}
// Extract recommendations
recommendations := make([]string, 0)
// Add tier-1 lessons (highest confidence)
for _, lesson := range svcCtx.Lessons {
if lesson.Tier == 1 {
recommendations = append(recommendations, fmt.Sprintf("[Tier 1] %s", lesson.Text))
}
}
// Add skills
for _, skill := range svcCtx.Skills {
recommendations = append(recommendations, fmt.Sprintf("[Skill] %s: %s", skill.Name, skill.Why))
}
// Add tier-2 lessons if no tier-1
if len(recommendations) == 0 {
for _, lesson := range svcCtx.Lessons {
if lesson.Tier == 2 {
recommendations = append(recommendations, fmt.Sprintf("[Tier 2] %s", lesson.Text))
}
}
}
logger.Info("Generated recommendations", "count", len(recommendations))
return recommendations, nil
}
// AnalyzeErrorActivity analyzes error and retrieves relevant knowledge
func (a *Activities) AnalyzeErrorActivity(ctx context.Context, errorMsg string) ([]KnowledgeRecord, error) {
logger := activity.GetLogger(ctx)
logger.Info("Analyzing error")
// Search for relevant knowledge
records, err := a.service.RetrieveKnowledge(ctx, errorMsg, &RetrievalOptions{
Limit: 10,
LevelFilter: []string{"L1", "L2"},
Floor: 0.6,
})
if err != nil {
logger.Error("Error analysis failed", "error", err)
return nil, err
}
logger.Info("Found relevant records for error", "count", len(records))
return records, nil
}
// DocumentDecisionActivity documents workflow decision in knowledge base
func (a *Activities) DocumentDecisionActivity(ctx context.Context, decisionType string, decision string, reasoning string) (string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Documenting decision", "type", decisionType)
content := fmt.Sprintf("Decision: %s\n\nReasoning: %s", decision, reasoning)
id, err := a.service.CreateKnowledge(ctx, &KnowledgeRecord{
Level: "L2",
Title: fmt.Sprintf("Decision: %s", decisionType),
Content: content,
Source: fmt.Sprintf("workflow://decision/%s", decisionType),
Metadata: map[string]interface{}{
"decision_type": decisionType,
"type": "workflow_decision",
},
})
if err != nil {
logger.Error("Failed to document decision", "error", err)
return "", err
}
logger.Info("Decision documented", "id", id)
return id, nil
}
// SearchAndApplyActivity searches knowledge and applies it
func (a *Activities) SearchAndApplyActivity(ctx context.Context, query string, selector func(record *KnowledgeRecord) bool) ([]string, error) {
logger := activity.GetLogger(ctx)
logger.Info("Searching and applying", "query", query)
records, err := a.service.RetrieveKnowledge(ctx, query, &RetrievalOptions{
Limit: 10,
Floor: 0.7,
})
if err != nil {
logger.Error("Search and apply failed", "error", err)
return nil, err
}
applied := make([]string, 0)
for _, record := range records {
if selector == nil || selector(&record) {
applied = append(applied, record.Content)
logger.Info("Applied knowledge", "id", record.ID)
}
}
logger.Info("Applied knowledge records", "count", len(applied))
return applied, nil
}
// RefreshMemoryActivity refreshes memory context (periodic activity)
func (a *Activities) RefreshMemoryActivity(ctx context.Context) (map[string]interface{}, error) {
logger := activity.GetLogger(ctx)
logger.Info("Refreshing memory context")
vault, err := a.service.GetVault(ctx)
if err != nil {
logger.Error("Memory refresh failed", "error", err)
return nil, err
}
healthy := a.service.IsHealthy(ctx)
result := map[string]interface{}{
"vault_files": len(vault),
"healthy": healthy,
}
logger.Info("Memory refreshed", "vault_files", len(vault), "healthy", healthy)
return result, nil
}