289 lines
7.9 KiB
Go
289 lines
7.9 KiB
Go
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
|
||
|
|
}
|