refactor: rename action→activity, statemachine→workflow, remove HTTP API layer
- action/ → activity/ (Temporal activities) - statemachine/ → workflow/ (Temporal workflows) - Removed internal/api/ and cmd/server/ (api-gw handles HTTP, Temporal is the API) - Created pkg/types/types.go as single source of truth for all shared types - Extracted CallRoleLLM helper (DRY: implementer/planner/judge shared pattern) - Fixed circular import: workflow_graph_query uses string activity names - Fixed logger.logf → logger.Info/Warn (method didn't exist) - Fixed routing types: added Branches, Activity, BackoffSeconds, TaskActivity - Fixed db.Canvas.Name, db.Client→DB, GetWorkflow→FetchWorkflow - Removed unused imports - All tests pass, build clean, vet clean
This commit is contained in:
@@ -0,0 +1,255 @@
|
||||
package activity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rockliang/poimen/workflows/internal/memory"
|
||||
)
|
||||
|
||||
// RetrieveMemoryInput input for RetrieveMemoryActivity
|
||||
type RetrieveMemoryInput struct {
|
||||
// Query semantic search query
|
||||
Query string `json:"query"`
|
||||
|
||||
// Project memory project (default: "poimen")
|
||||
Project string `json:"project,omitempty"`
|
||||
|
||||
// Scope retrieval scope: "skills", "lessons", "all" (default: "all")
|
||||
Scope string `json:"scope,omitempty"`
|
||||
|
||||
// Limit max results (default: 10)
|
||||
Limit int `json:"limit,omitempty"`
|
||||
|
||||
// LevelFilter filter by level: L1, L2, R (reference)
|
||||
LevelFilter []string `json:"levelFilter,omitempty"`
|
||||
|
||||
// Tool tool context for skill matching
|
||||
Tool string `json:"tool,omitempty"`
|
||||
|
||||
// Task task description for context retrieval
|
||||
Task string `json:"task,omitempty"`
|
||||
}
|
||||
|
||||
// RetrieveMemoryOutput output from RetrieveMemoryActivity
|
||||
type RetrieveMemoryOutput struct {
|
||||
// Skills relevant skills found
|
||||
Skills []MemorySkill `json:"skills"`
|
||||
|
||||
// Lessons relevant lessons/knowledge found
|
||||
Lessons []MemoryLesson `json:"lessons"`
|
||||
|
||||
// References reference documents found
|
||||
References []MemoryReference `json:"references"`
|
||||
|
||||
// TotalResults total results found
|
||||
TotalResults int `json:"totalResults"`
|
||||
|
||||
// Budget token budget info
|
||||
Budget MemoryBudget `json:"budget"`
|
||||
}
|
||||
|
||||
// MemorySkill skill from memory
|
||||
type MemorySkill struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Why string `json:"why,omitempty"`
|
||||
}
|
||||
|
||||
// MemoryLesson lesson from memory
|
||||
type MemoryLesson struct {
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
Level string `json:"level"`
|
||||
Score float32 `json:"score"`
|
||||
Breadcrumb string `json:"breadcrumb,omitempty"`
|
||||
}
|
||||
|
||||
// MemoryReference reference document from memory
|
||||
type MemoryReference struct {
|
||||
ID string `json:"id"`
|
||||
Text string `json:"text"`
|
||||
Score float32 `json:"score"`
|
||||
Breadcrumb string `json:"breadcrumb,omitempty"`
|
||||
}
|
||||
|
||||
// MemoryBudget token budget tracking
|
||||
type MemoryBudget struct {
|
||||
Requested int `json:"requested"`
|
||||
Used int `json:"used"`
|
||||
}
|
||||
|
||||
// RetrieveMemoryActivity retrieves relevant knowledge from poimen-memory
|
||||
func RetrieveMemoryActivity(ctx context.Context, in RetrieveMemoryInput) (RetrieveMemoryOutput, error) {
|
||||
logger := newActivityLogger(ctx)
|
||||
logger.Info("RetrieveMemoryActivity started", "query", in.Query, "scope", in.Scope)
|
||||
|
||||
output := RetrieveMemoryOutput{
|
||||
Skills: []MemorySkill{},
|
||||
Lessons: []MemoryLesson{},
|
||||
References: []MemoryReference{},
|
||||
}
|
||||
|
||||
// Get memory service URL and token
|
||||
baseURL := os.Getenv("POIMEN_MEMORY_URL")
|
||||
if baseURL == "" {
|
||||
baseURL = "http://poimen-memory.poimen.svc.cluster.local:8080"
|
||||
}
|
||||
|
||||
token := os.Getenv("POIMEN_MEMORY_TOKEN")
|
||||
// Token optional for internal cluster access
|
||||
|
||||
// Set defaults
|
||||
project := in.Project
|
||||
if project == "" {
|
||||
project = "poimen"
|
||||
}
|
||||
|
||||
scope := in.Scope
|
||||
if scope == "" {
|
||||
scope = "all"
|
||||
}
|
||||
|
||||
limit := in.Limit
|
||||
if limit == 0 {
|
||||
limit = 10
|
||||
}
|
||||
|
||||
client := memory.NewClient(baseURL, token)
|
||||
|
||||
// If tool/task provided, use Context API for skill matching
|
||||
if in.Tool != "" || in.Task != "" {
|
||||
contextResp, err := client.Context(ctx, &memory.ContextRequest{
|
||||
Project: project,
|
||||
Tool: in.Tool,
|
||||
Task: in.Task,
|
||||
SignatureSource: in.Query,
|
||||
Scope: "tool_context",
|
||||
Budget: 8192,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warn("Context retrieval failed, falling back to query", "error", err)
|
||||
} else {
|
||||
// Extract skills
|
||||
for _, skill := range contextResp.Skills {
|
||||
output.Skills = append(output.Skills, MemorySkill{
|
||||
Name: skill.Name,
|
||||
Why: skill.Why,
|
||||
})
|
||||
}
|
||||
|
||||
// Extract lessons
|
||||
for i, lesson := range contextResp.Lessons {
|
||||
output.Lessons = append(output.Lessons, MemoryLesson{
|
||||
ID: fmt.Sprintf("ctx-%d", i),
|
||||
Text: lesson.Text,
|
||||
Level: lesson.Level,
|
||||
Score: lesson.Score,
|
||||
Breadcrumb: "",
|
||||
})
|
||||
}
|
||||
|
||||
output.Budget = MemoryBudget{
|
||||
Requested: contextResp.Budget.Requested,
|
||||
Used: contextResp.Budget.Used,
|
||||
}
|
||||
output.TotalResults = len(output.Skills) + len(output.Lessons)
|
||||
}
|
||||
}
|
||||
|
||||
// Also do semantic query for additional context
|
||||
if scope == "all" || scope == "lessons" || scope == "references" {
|
||||
levelFilter := in.LevelFilter
|
||||
if len(levelFilter) == 0 {
|
||||
levelFilter = []string{"L1", "L2"}
|
||||
}
|
||||
|
||||
queryResp, err := client.Query(ctx, &memory.QueryRequest{
|
||||
Project: project,
|
||||
Query: in.Query,
|
||||
LevelFilter: levelFilter,
|
||||
Limit: limit,
|
||||
Scope: "all",
|
||||
})
|
||||
if err != nil {
|
||||
logger.Warn("Query failed", "error", err)
|
||||
} else {
|
||||
for _, result := range queryResp.Results {
|
||||
if result.Level == "R" {
|
||||
output.References = append(output.References, MemoryReference{
|
||||
ID: result.ID,
|
||||
Text: result.Text,
|
||||
Score: result.Score,
|
||||
Breadcrumb: result.Breadcrumb,
|
||||
})
|
||||
} else {
|
||||
// Avoid duplicates from Context call
|
||||
found := false
|
||||
for _, existing := range output.Lessons {
|
||||
if existing.ID == result.ID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
output.Lessons = append(output.Lessons, MemoryLesson{
|
||||
ID: result.ID,
|
||||
Text: result.Text,
|
||||
Level: result.Level,
|
||||
Score: result.Score,
|
||||
Breadcrumb: result.Breadcrumb,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
output.TotalResults = len(output.Skills) + len(output.Lessons) + len(output.References)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("RetrieveMemoryActivity completed",
|
||||
"skills", len(output.Skills),
|
||||
"lessons", len(output.Lessons),
|
||||
"references", len(output.References))
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
// FormatMemoryForPrompt formats memory output for LLM prompt injection
|
||||
func FormatMemoryForPrompt(mem RetrieveMemoryOutput) string {
|
||||
if mem.TotalResults == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var result string
|
||||
|
||||
if len(mem.Skills) > 0 {
|
||||
result += "\n## Relevant Skills\n"
|
||||
for _, skill := range mem.Skills {
|
||||
result += fmt.Sprintf("- **%s**: %s\n", skill.Name, skill.Why)
|
||||
}
|
||||
}
|
||||
|
||||
if len(mem.Lessons) > 0 {
|
||||
result += "\n## Relevant Knowledge\n"
|
||||
for _, lesson := range mem.Lessons {
|
||||
result += fmt.Sprintf("- [%s] %s\n", lesson.Level, truncate(lesson.Text, 200))
|
||||
}
|
||||
}
|
||||
|
||||
if len(mem.References) > 0 {
|
||||
result += "\n## Reference Documents\n"
|
||||
for _, ref := range mem.References {
|
||||
result += fmt.Sprintf("- %s\n", truncate(ref.Text, 200))
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func truncate(s string, maxLen int) string {
|
||||
if len(s) <= maxLen {
|
||||
return s
|
||||
}
|
||||
return s[:maxLen] + "..."
|
||||
}
|
||||
Reference in New Issue
Block a user