feat(memory): add Temporal activities integration for memory service
- Implement 12 Temporal activities for memory operations - Activities: create, update, search, context, diagnose, analyze, document - Add activity registration and worker setup - Full retry/timeout configuration with observability - Include workflow patterns and examples - All tests passing (23/23) Documentation: - MEMORY_INTEGRATION.md: High-level integration guide - MEMORY_ACTIVITIES.md: Complete activities reference - REGISTERED_ACTIVITIES.md: Registry and calling conventions
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.temporal.io/sdk/workflow"
|
||||
)
|
||||
|
||||
// LearningWorkflow learns from task execution
|
||||
// Pattern: Execute → Learn → Document
|
||||
func LearningWorkflow(ctx workflow.Context, taskID string, executor string) (string, error) {
|
||||
// Execute task (placeholder - replace with actual activity)
|
||||
taskResult := fmt.Sprintf("Task %s executed by %s", taskID, executor)
|
||||
|
||||
// Learn from execution
|
||||
knowledgeID, err := ExecuteLearnFromExecution(
|
||||
ctx,
|
||||
taskID,
|
||||
taskResult,
|
||||
[]string{"execution", "learning"},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("learn from execution: %w", err)
|
||||
}
|
||||
|
||||
return knowledgeID, nil
|
||||
}
|
||||
|
||||
// DiagnosticWorkflow diagnoses issue using memory service
|
||||
// Pattern: Get Context → Extract Recommendations → Apply
|
||||
func DiagnosticWorkflow(ctx workflow.Context, tool, issue string) ([]string, error) {
|
||||
// Get recommendations
|
||||
recommendations, err := ExecuteDiagnoseIssue(
|
||||
ctx,
|
||||
tool,
|
||||
issue,
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("diagnose: %w", err)
|
||||
}
|
||||
|
||||
return recommendations, nil
|
||||
}
|
||||
|
||||
// SearchAndApplyWorkflow searches knowledge and applies it
|
||||
// Pattern: Search → Filter → Apply
|
||||
func SearchAndApplyWorkflow(ctx workflow.Context, query string) ([]KnowledgeRecord, error) {
|
||||
// Search knowledge
|
||||
records, err := ExecuteSearchKnowledge(
|
||||
ctx,
|
||||
query,
|
||||
&RetrievalOptions{
|
||||
Limit: 10,
|
||||
LevelFilter: []string{"L1", "L2"},
|
||||
Floor: 0.7,
|
||||
},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search: %w", err)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// ContextualDecisionWorkflow makes decisions with memory context
|
||||
// Pattern: Get Context → Make Decision → Document Decision
|
||||
func ContextualDecisionWorkflow(ctx workflow.Context, tool, task string, decision string) (string, error) {
|
||||
// Get context
|
||||
svcCtx, err := ExecuteGetContext(
|
||||
ctx,
|
||||
tool,
|
||||
task,
|
||||
8192,
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get context: %w", err)
|
||||
}
|
||||
|
||||
// Build reasoning from lessons
|
||||
reasoning := fmt.Sprintf("Based on %d lessons from memory service (tier %d)", len(svcCtx.Lessons), svcCtx.Tier)
|
||||
|
||||
// Document decision
|
||||
docID, err := ExecuteDocumentDecision(
|
||||
ctx,
|
||||
tool,
|
||||
decision,
|
||||
reasoning,
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("document decision: %w", err)
|
||||
}
|
||||
|
||||
return docID, nil
|
||||
}
|
||||
|
||||
// ErrorRecoveryWorkflow analyzes error and searches for recovery
|
||||
// Pattern: Error → Analyze → Search → Recover
|
||||
func ErrorRecoveryWorkflow(ctx workflow.Context, errorMsg string) ([]string, error) {
|
||||
// Analyze error
|
||||
records, err := ExecuteAnalyzeError(
|
||||
ctx,
|
||||
errorMsg,
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("analyze error: %w", err)
|
||||
}
|
||||
|
||||
// Extract recovery recommendations
|
||||
recommendations := make([]string, 0)
|
||||
for _, record := range records {
|
||||
if record.Level == "L1" { // High confidence
|
||||
recommendations = append(recommendations, record.Content)
|
||||
}
|
||||
}
|
||||
|
||||
return recommendations, nil
|
||||
}
|
||||
|
||||
// HealthAwareWorkflow checks health before proceeding
|
||||
// Pattern: HealthCheck → Conditional Proceed
|
||||
func HealthAwareWorkflow(ctx workflow.Context, taskID string) (bool, error) {
|
||||
// Check health
|
||||
healthy, err := ExecuteHealthCheck(ctx, DefaultActivityOptions())
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("health check: %w", err)
|
||||
}
|
||||
|
||||
if !healthy {
|
||||
return false, fmt.Errorf("memory service unhealthy, skipping task %s", taskID)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// IterativeLearnWorkflow learns iteratively
|
||||
// Pattern: Execute → Learn → Refine → Learn Again
|
||||
func IterativeLearnWorkflow(ctx workflow.Context, topic string, iterations int) ([]string, error) {
|
||||
knowledgeIDs := make([]string, 0)
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
// Learn current iteration
|
||||
id, err := ExecuteLearnFromExecution(
|
||||
ctx,
|
||||
fmt.Sprintf("%s-iteration-%d", topic, i+1),
|
||||
fmt.Sprintf("Iteration %d: %s", i+1, topic),
|
||||
[]string{"iteration", fmt.Sprintf("iteration-%d", i+1)},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("learn iteration %d: %w", i+1, err)
|
||||
}
|
||||
|
||||
knowledgeIDs = append(knowledgeIDs, id)
|
||||
|
||||
// Search for related knowledge
|
||||
records, err := ExecuteSearchKnowledge(
|
||||
ctx,
|
||||
topic,
|
||||
&RetrievalOptions{Limit: 5, Floor: 0.6},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search iteration %d: %w", i+1, err)
|
||||
}
|
||||
|
||||
// Log found records
|
||||
if len(records) > 0 {
|
||||
workflow.GetLogger(ctx).Info("Iteration found related records", "iteration", i+1, "records", len(records))
|
||||
}
|
||||
}
|
||||
|
||||
return knowledgeIDs, nil
|
||||
}
|
||||
|
||||
// ConditionalLearningWorkflow learns only on success
|
||||
// Pattern: Execute → If Success → Learn
|
||||
func ConditionalLearningWorkflow(ctx workflow.Context, taskID string, shouldSucceed bool) (string, error) {
|
||||
if !shouldSucceed {
|
||||
return "", fmt.Errorf("task failed, skipping learning")
|
||||
}
|
||||
|
||||
// Only learn on success
|
||||
result := fmt.Sprintf("Task %s succeeded", taskID)
|
||||
|
||||
id, err := ExecuteLearnFromExecution(
|
||||
ctx,
|
||||
taskID,
|
||||
result,
|
||||
[]string{"success"},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("learn from success: %w", err)
|
||||
}
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// MultiStepWorkflow performs multiple memory operations
|
||||
// Pattern: Create → Search → Context → Document
|
||||
func MultiStepWorkflow(ctx workflow.Context, topic string) (map[string]interface{}, error) {
|
||||
results := make(map[string]interface{})
|
||||
|
||||
// Step 1: Create knowledge
|
||||
createID, err := ExecuteCreateKnowledge(
|
||||
ctx,
|
||||
&KnowledgeRecord{
|
||||
Level: "L1",
|
||||
Title: fmt.Sprintf("Initial: %s", topic),
|
||||
Content: fmt.Sprintf("Starting workflow for %s", topic),
|
||||
Source: "workflow://multi-step",
|
||||
},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create knowledge: %w", err)
|
||||
}
|
||||
results["created"] = createID
|
||||
|
||||
// Step 2: Search knowledge
|
||||
searchRecords, err := ExecuteSearchKnowledge(
|
||||
ctx,
|
||||
topic,
|
||||
&RetrievalOptions{Limit: 5},
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("search knowledge: %w", err)
|
||||
}
|
||||
results["found"] = len(searchRecords)
|
||||
|
||||
// Step 3: Get context
|
||||
svcCtx, err := ExecuteGetContext(
|
||||
ctx,
|
||||
"workflow",
|
||||
topic,
|
||||
8192,
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get context: %w", err)
|
||||
}
|
||||
results["context_tier"] = svcCtx.Tier
|
||||
results["lessons"] = len(svcCtx.Lessons)
|
||||
results["skills"] = len(svcCtx.Skills)
|
||||
|
||||
// Step 4: Document completion
|
||||
docID, err := ExecuteDocumentDecision(
|
||||
ctx,
|
||||
"workflow_completion",
|
||||
fmt.Sprintf("Completed multi-step workflow for %s", topic),
|
||||
fmt.Sprintf("Found %d records, tier %d context", len(searchRecords), svcCtx.Tier),
|
||||
DefaultActivityOptions(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("document completion: %w", err)
|
||||
}
|
||||
results["documented"] = docID
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// ParallelLearnWorkflow learns from multiple sources in parallel
|
||||
// Pattern: Execute Multiple Tasks in Parallel → Learn from Each
|
||||
func ParallelLearnWorkflow(ctx workflow.Context, taskIDs []string) ([]string, error) {
|
||||
// Create parallel activities
|
||||
futures := make([]workflow.Future, len(taskIDs))
|
||||
|
||||
for i, taskID := range taskIDs {
|
||||
// Execute each task in parallel
|
||||
future := workflow.ExecuteActivity(
|
||||
workflow.WithActivityOptions(
|
||||
ctx,
|
||||
workflow.ActivityOptions{
|
||||
ScheduleToCloseTimeout: DefaultActivityOptions().RetryBackoff * 60,
|
||||
StartToCloseTimeout: DefaultActivityOptions().RetryBackoff * 30,
|
||||
},
|
||||
),
|
||||
"LearnFromExecutionActivity",
|
||||
taskID,
|
||||
fmt.Sprintf("Result from %s", taskID),
|
||||
[]string{"parallel", taskID},
|
||||
)
|
||||
futures[i] = future
|
||||
}
|
||||
|
||||
// Collect results
|
||||
results := make([]string, len(futures))
|
||||
for i, future := range futures {
|
||||
err := future.Get(ctx, &results[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parallel learn task %d: %w", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
Reference in New Issue
Block a user