1199 lines
32 KiB
Markdown
1199 lines
32 KiB
Markdown
# Poimen Routing Workflow Specification
|
|||
|
|
|
||
|
|
**Status**: Complete Standard
|
||
|
|
**Version**: 1.0
|
||
|
|
**Architecture**: User Message → LLM-Router → JSON WorkflowSpec → RoutingWorkflow
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## OVERVIEW
|
||
|
|
|
||
|
|
### The Flow
|
||
|
|
|
||
|
|
```
|
||
|
|
User Message: "Analyze this GitHub repo for security and quality"
|
||
|
|
↓
|
||
|
|
[llm-router Activity] ← LLM-powered intelligent agent
|
||
|
|
Reads: ActivityKnowledgeBase.json
|
||
|
|
↓
|
||
|
|
Uses LLM to understand intent:
|
||
|
|
- What is user asking for?
|
||
|
|
- Which activities are needed?
|
||
|
|
- What order?
|
||
|
|
- What timeout for each?
|
||
|
|
- What retry strategy?
|
||
|
|
↓
|
||
|
|
Generates JSON WorkflowSpec
|
||
|
|
{
|
||
|
|
"name": "repo-analysis",
|
||
|
|
"input": {"repo": "https://...", "branch": "main"},
|
||
|
|
"states": [
|
||
|
|
{
|
||
|
|
"name": "Clone",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CloneRepoActivity",
|
||
|
|
"parameters": {"repo": "${input.repo}"},
|
||
|
|
"timeout": "5m", ← LLM decided
|
||
|
|
"retry": {...}, ← LLM decided
|
||
|
|
"next": "Analyze"
|
||
|
|
},
|
||
|
|
{...more states...}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
↓
|
||
|
|
[RoutingWorkflow] ← Generic executor
|
||
|
|
Takes the JSON spec from llm-router
|
||
|
|
Executes states in order
|
||
|
|
Respects timeout/retry
|
||
|
|
↓
|
||
|
|
Final Results
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Insight
|
||
|
|
|
||
|
|
**Intelligence is distributed**:
|
||
|
|
- **ActivityKnowledgeBase.json**: What activities exist, their constraints, capabilities
|
||
|
|
- **llm-router Activity**: Uses LLM to reason about user intent + pick activities + decide settings
|
||
|
|
- **RoutingWorkflow**: Dumb executor that just runs what the LLM decided
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 1: ACTIVITY KNOWLEDGE BASE
|
||
|
|
|
||
|
|
### What is it?
|
||
|
|
|
||
|
|
Metadata about all available activities. The llm-router reads this to make intelligent decisions.
|
||
|
|
|
||
|
|
### File Location
|
||
|
|
|
||
|
|
```
|
||
|
|
internal/routing/activity_knowledge_base.json
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Format
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"activities": [
|
||
|
|
{
|
||
|
|
"name": "CloneRepoActivity",
|
||
|
|
"description": "Clone a Git repository to local filesystem",
|
||
|
|
"category": "source-control",
|
||
|
|
"inputs": {
|
||
|
|
"repo": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Repository URL (https://...)",
|
||
|
|
"required": true
|
||
|
|
},
|
||
|
|
"branch": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Branch name to clone",
|
||
|
|
"required": false,
|
||
|
|
"default": "main"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"path": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Local filesystem path to cloned repo"
|
||
|
|
},
|
||
|
|
"commit": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Current commit hash"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "5m",
|
||
|
|
"isFlaky": false,
|
||
|
|
"recommendedRetries": 2,
|
||
|
|
"retryBackoff": 1.5,
|
||
|
|
"dependencies": [],
|
||
|
|
"notes": "Fast operation, rarely fails"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "AnalyzeCodeActivity",
|
||
|
|
"description": "Analyze code for quality issues, complexity, patterns",
|
||
|
|
"category": "analysis",
|
||
|
|
"inputs": {
|
||
|
|
"path": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Path to code directory",
|
||
|
|
"required": true
|
||
|
|
},
|
||
|
|
"language": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Programming language (go, python, js, etc)",
|
||
|
|
"required": false,
|
||
|
|
"default": "auto-detect"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"quality": {
|
||
|
|
"type": "number",
|
||
|
|
"description": "Quality score 0-1"
|
||
|
|
},
|
||
|
|
"issues": {
|
||
|
|
"type": "array",
|
||
|
|
"description": "List of issues found"
|
||
|
|
},
|
||
|
|
"complexity": {
|
||
|
|
"type": "number",
|
||
|
|
"description": "Cyclomatic complexity"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "10m",
|
||
|
|
"isFlaky": true,
|
||
|
|
"recommendedRetries": 3,
|
||
|
|
"retryBackoff": 2.0,
|
||
|
|
"dependencies": ["CloneRepoActivity"],
|
||
|
|
"notes": "Can timeout on large codebases, retry recommended"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SecurityScanActivity",
|
||
|
|
"description": "Scan code for vulnerabilities and security issues",
|
||
|
|
"category": "security",
|
||
|
|
"inputs": {
|
||
|
|
"path": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Path to scan",
|
||
|
|
"required": true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"vulnerabilities": {
|
||
|
|
"type": "array",
|
||
|
|
"description": "List of vulnerabilities found"
|
||
|
|
},
|
||
|
|
"score": {
|
||
|
|
"type": "number",
|
||
|
|
"description": "Security score 0-1"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "15m",
|
||
|
|
"isFlaky": false,
|
||
|
|
"recommendedRetries": 2,
|
||
|
|
"retryBackoff": 1.5,
|
||
|
|
"dependencies": ["CloneRepoActivity"],
|
||
|
|
"notes": "Longer runtime, can be parallelized with other scans"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "PerformanceAnalysisActivity",
|
||
|
|
"description": "Analyze runtime performance, bottlenecks, optimization opportunities",
|
||
|
|
"category": "analysis",
|
||
|
|
"inputs": {
|
||
|
|
"path": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Path to code",
|
||
|
|
"required": true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"performance": {
|
||
|
|
"type": "number",
|
||
|
|
"description": "Performance score 0-1"
|
||
|
|
},
|
||
|
|
"bottlenecks": {
|
||
|
|
"type": "array",
|
||
|
|
"description": "Performance bottlenecks"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "10m",
|
||
|
|
"isFlaky": true,
|
||
|
|
"recommendedRetries": 2,
|
||
|
|
"retryBackoff": 2.0,
|
||
|
|
"dependencies": ["CloneRepoActivity"],
|
||
|
|
"notes": "Can timeout on complex analysis"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "JudgeActivity",
|
||
|
|
"description": "Make final judgment/recommendation based on analysis results",
|
||
|
|
"category": "judgment",
|
||
|
|
"inputs": {
|
||
|
|
"quality": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Code quality analysis result",
|
||
|
|
"required": true
|
||
|
|
},
|
||
|
|
"security": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Security scan result",
|
||
|
|
"required": false
|
||
|
|
},
|
||
|
|
"performance": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Performance analysis result",
|
||
|
|
"required": false
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"verdict": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "approved, needs-changes, rejected"
|
||
|
|
},
|
||
|
|
"score": {
|
||
|
|
"type": "number",
|
||
|
|
"description": "Overall score"
|
||
|
|
},
|
||
|
|
"recommendations": {
|
||
|
|
"type": "array",
|
||
|
|
"description": "Recommendations for improvement"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "5m",
|
||
|
|
"isFlaky": false,
|
||
|
|
"recommendedRetries": 1,
|
||
|
|
"retryBackoff": 1.0,
|
||
|
|
"dependencies": ["AnalyzeCodeActivity"],
|
||
|
|
"notes": "Fast operation, only runs after analysis"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "CombineResultsActivity",
|
||
|
|
"description": "Combine multiple analysis results into final report",
|
||
|
|
"category": "aggregation",
|
||
|
|
"inputs": {
|
||
|
|
"results": {
|
||
|
|
"type": "array",
|
||
|
|
"description": "Array of previous step results",
|
||
|
|
"required": true
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"report": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Final combined report"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "2m",
|
||
|
|
"isFlaky": false,
|
||
|
|
"recommendedRetries": 1,
|
||
|
|
"retryBackoff": 1.0,
|
||
|
|
"dependencies": [],
|
||
|
|
"notes": "Fast, runs at the end"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SendNotificationActivity",
|
||
|
|
"description": "Send notification with results to user",
|
||
|
|
"category": "notification",
|
||
|
|
"inputs": {
|
||
|
|
"result": {
|
||
|
|
"type": "object",
|
||
|
|
"description": "Results to send",
|
||
|
|
"required": true
|
||
|
|
},
|
||
|
|
"channel": {
|
||
|
|
"type": "string",
|
||
|
|
"description": "Where to send (email, slack, etc)",
|
||
|
|
"required": false,
|
||
|
|
"default": "email"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"outputs": {
|
||
|
|
"sent": {
|
||
|
|
"type": "boolean",
|
||
|
|
"description": "Was notification sent?"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"constraints": {
|
||
|
|
"defaultTimeout": "2m",
|
||
|
|
"isFlaky": true,
|
||
|
|
"recommendedRetries": 3,
|
||
|
|
"retryBackoff": 2.0,
|
||
|
|
"dependencies": [],
|
||
|
|
"notes": "External dependency, retry recommended"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Knowledge Base Fields Explained
|
||
|
|
|
||
|
|
| Field | Purpose | Used By |
|
||
|
|
|-------|---------|---------|
|
||
|
|
| **name** | Unique activity identifier | llm-router for routing |
|
||
|
|
| **description** | What the activity does | llm-router for reasoning |
|
||
|
|
| **category** | Type of activity (analysis, security, etc) | llm-router for grouping |
|
||
|
|
| **inputs** | What the activity accepts | llm-router to chain activities |
|
||
|
|
| **outputs** | What the activity produces | llm-router to connect outputs to next activity inputs |
|
||
|
|
| **constraints.defaultTimeout** | Recommended timeout | llm-router to decide timeout |
|
||
|
|
| **constraints.isFlaky** | Does it fail often? | llm-router to decide retry count |
|
||
|
|
| **constraints.recommendedRetries** | How many retries? | llm-router to configure retry |
|
||
|
|
| **constraints.retryBackoff** | Backoff multiplier | llm-router to configure exponential backoff |
|
||
|
|
| **constraints.dependencies** | Must run after X | llm-router to order activities |
|
||
|
|
| **constraints.notes** | Special handling notes | llm-router for reasoning |
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 2: LLM-ROUTER ACTIVITY
|
||
|
|
|
||
|
|
### What is it?
|
||
|
|
|
||
|
|
An Activity that uses an LLM to intelligently decide:
|
||
|
|
1. Which activities are needed
|
||
|
|
2. What order to execute them
|
||
|
|
3. What timeout for each activity
|
||
|
|
4. What retry policy for each activity
|
||
|
|
5. How to chain parameters (JSONPath)
|
||
|
|
|
||
|
|
### Input to llm-router
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"message": "Analyze this GitHub repo for security and code quality",
|
||
|
|
"repo": "https://github.com/rockliang/poimen",
|
||
|
|
"branch": "main"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Output from llm-router
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "repo-analysis-workflow",
|
||
|
|
"input": {
|
||
|
|
"repo": "https://github.com/rockliang/poimen",
|
||
|
|
"branch": "main"
|
||
|
|
},
|
||
|
|
"states": [
|
||
|
|
{
|
||
|
|
"name": "Clone",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CloneRepoActivity",
|
||
|
|
"parameters": {
|
||
|
|
"repo": "${input.repo}",
|
||
|
|
"branch": "${input.branch}"
|
||
|
|
},
|
||
|
|
"timeout": "5m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 2,
|
||
|
|
"backoffRate": 1.5,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"next": "Analyze"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "Analyze",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "AnalyzeCodeActivity",
|
||
|
|
"parameters": {
|
||
|
|
"path": "${Clone.output.path}"
|
||
|
|
},
|
||
|
|
"timeout": "10m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 3,
|
||
|
|
"backoffRate": 2.0,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"catch": [
|
||
|
|
{
|
||
|
|
"errorEquals": ["Timeout"],
|
||
|
|
"next": "HandleTimeout"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"next": "SecurityScan"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SecurityScan",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "SecurityScanActivity",
|
||
|
|
"parameters": {
|
||
|
|
"path": "${Clone.output.path}"
|
||
|
|
},
|
||
|
|
"timeout": "15m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 2,
|
||
|
|
"backoffRate": 1.5,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"next": "Combine"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "Combine",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CombineResultsActivity",
|
||
|
|
"parameters": {
|
||
|
|
"results": [
|
||
|
|
"${Analyze.output}",
|
||
|
|
"${SecurityScan.output}"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"timeout": "2m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 1,
|
||
|
|
"backoffRate": 1.0,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"next": "SendResults"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SendResults",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "SendNotificationActivity",
|
||
|
|
"parameters": {
|
||
|
|
"result": "${Combine.output.report}",
|
||
|
|
"channel": "email"
|
||
|
|
},
|
||
|
|
"timeout": "2m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 3,
|
||
|
|
"backoffRate": 2.0,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"end": true
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "HandleTimeout",
|
||
|
|
"type": "Fail",
|
||
|
|
"error": "AnalysisTimeout",
|
||
|
|
"cause": "Code analysis timed out after 10 minutes"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### LLM-Router Logic (Pseudo-code)
|
||
|
|
|
||
|
|
```python
|
||
|
|
def llm_router_activity(message: str, repo: str, branch: str) -> WorkflowSpec:
|
||
|
|
# 1. Load knowledge base
|
||
|
|
knowledge_base = load_activity_knowledge_base()
|
||
|
|
|
||
|
|
# 2. Use LLM to understand intent
|
||
|
|
intent = llm.analyze_intent(message)
|
||
|
|
# intent = {
|
||
|
|
# "needs_security_scan": true,
|
||
|
|
# "needs_code_analysis": true,
|
||
|
|
# "needs_performance_analysis": false,
|
||
|
|
# "needs_final_judgment": true,
|
||
|
|
# }
|
||
|
|
|
||
|
|
# 3. Select activities based on intent
|
||
|
|
selected_activities = []
|
||
|
|
if intent.needs_security_scan:
|
||
|
|
selected_activities.append(knowledge_base["SecurityScanActivity"])
|
||
|
|
if intent.needs_code_analysis:
|
||
|
|
selected_activities.append(knowledge_base["AnalyzeCodeActivity"])
|
||
|
|
if intent.needs_performance_analysis:
|
||
|
|
selected_activities.append(knowledge_base["PerformanceAnalysisActivity"])
|
||
|
|
|
||
|
|
# 4. Order activities by dependencies
|
||
|
|
ordered = topological_sort_by_dependencies(selected_activities)
|
||
|
|
|
||
|
|
# 5. Build workflow states with intelligent settings
|
||
|
|
states = []
|
||
|
|
|
||
|
|
# Always start with Clone
|
||
|
|
clone_state = {
|
||
|
|
"name": "Clone",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CloneRepoActivity",
|
||
|
|
"parameters": {
|
||
|
|
"repo": "${input.repo}",
|
||
|
|
"branch": "${input.branch}"
|
||
|
|
},
|
||
|
|
"timeout": "5m", # From knowledge base
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 2,
|
||
|
|
"backoffRate": 1.5,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"next": ordered[0]["name"]
|
||
|
|
}
|
||
|
|
states.append(clone_state)
|
||
|
|
|
||
|
|
# Add selected activities in order
|
||
|
|
for i, activity in enumerate(ordered):
|
||
|
|
state = {
|
||
|
|
"name": activity["name"].replace("Activity", ""),
|
||
|
|
"type": "Task",
|
||
|
|
"resource": activity["name"],
|
||
|
|
"parameters": {
|
||
|
|
# Use LLM to map inputs
|
||
|
|
# For each input, decide: use output from previous step? use input param? use constant?
|
||
|
|
},
|
||
|
|
"timeout": activity["constraints"]["defaultTimeout"],
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": activity["constraints"]["recommendedRetries"],
|
||
|
|
"backoffRate": activity["constraints"]["retryBackoff"],
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"next": ordered[i+1]["name"] if i+1 < len(ordered) else "SendResults"
|
||
|
|
}
|
||
|
|
|
||
|
|
# Add error handling if flaky
|
||
|
|
if activity["constraints"]["isFlaky"]:
|
||
|
|
state["catch"] = [
|
||
|
|
{
|
||
|
|
"errorEquals": ["Timeout"],
|
||
|
|
"next": "HandleTimeout"
|
||
|
|
}
|
||
|
|
]
|
||
|
|
|
||
|
|
states.append(state)
|
||
|
|
|
||
|
|
# Add final notification
|
||
|
|
states.append({
|
||
|
|
"name": "SendResults",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "SendNotificationActivity",
|
||
|
|
"parameters": {
|
||
|
|
"result": "${" + ordered[-1]["name"].replace("Activity", "") + ".output}",
|
||
|
|
"channel": "email"
|
||
|
|
},
|
||
|
|
"timeout": "2m",
|
||
|
|
"retry": {"maxAttempts": 3, "backoffRate": 2.0, "initialInterval": "1s"},
|
||
|
|
"end": true
|
||
|
|
})
|
||
|
|
|
||
|
|
# Add error handler
|
||
|
|
states.append({
|
||
|
|
"name": "HandleTimeout",
|
||
|
|
"type": "Fail",
|
||
|
|
"error": "AnalysisTimeout",
|
||
|
|
"cause": "Activity timed out"
|
||
|
|
})
|
||
|
|
|
||
|
|
# 6. Return complete workflow spec
|
||
|
|
return WorkflowSpec(
|
||
|
|
name="generated-workflow",
|
||
|
|
input={"repo": repo, "branch": branch},
|
||
|
|
states=states
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Go Implementation Skeleton
|
||
|
|
|
||
|
|
```go
|
||
|
|
// cmd/worker/activities/llm_router.go
|
||
|
|
|
||
|
|
type LLMRouterInput struct {
|
||
|
|
Message string
|
||
|
|
Repo string
|
||
|
|
Branch string
|
||
|
|
}
|
||
|
|
|
||
|
|
func LLMRouterActivity(ctx context.Context, input LLMRouterInput) (routing.WorkflowSpec, error) {
|
||
|
|
// 1. Load activity knowledge base
|
||
|
|
kb, err := LoadActivityKnowledgeBase()
|
||
|
|
if err != nil {
|
||
|
|
return routing.WorkflowSpec{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// 2. Use LLM to understand intent
|
||
|
|
// Call memory service to get LLM analysis
|
||
|
|
intent, err := llm.AnalyzeIntent(ctx, input.Message, kb)
|
||
|
|
if err != nil {
|
||
|
|
return routing.WorkflowSpec{}, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// 3. Build workflow spec intelligently
|
||
|
|
spec := buildWorkflowSpec(intent, input, kb)
|
||
|
|
|
||
|
|
return spec, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func buildWorkflowSpec(intent Intent, input LLMRouterInput, kb ActivityKnowledgeBase) routing.WorkflowSpec {
|
||
|
|
// Create workflow with intelligent settings from knowledge base
|
||
|
|
// ...
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 3: ROUTING WORKFLOW (The Executor)
|
||
|
|
|
||
|
|
### What is it?
|
||
|
|
|
||
|
|
A generic Temporal workflow that executes ANY WorkflowSpec generated by llm-router.
|
||
|
|
|
||
|
|
### Input
|
||
|
|
|
||
|
|
```go
|
||
|
|
type RoutingWorkflowInput struct {
|
||
|
|
WorkflowSpec routing.WorkflowSpec // Generated by llm-router
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Implementation
|
||
|
|
|
||
|
|
```go
|
||
|
|
func RoutingWorkflow(ctx workflow.Context, spec routing.WorkflowSpec) (Result, error) {
|
||
|
|
logger := workflow.GetLogger(ctx)
|
||
|
|
|
||
|
|
// Initialize context
|
||
|
|
ec := &ExecutionContext{
|
||
|
|
Input: spec.Input,
|
||
|
|
StepResults: make(map[string]interface{}),
|
||
|
|
}
|
||
|
|
|
||
|
|
// Find first state
|
||
|
|
currentStateName := FindFirstState(spec.States)
|
||
|
|
|
||
|
|
// State machine loop
|
||
|
|
for {
|
||
|
|
logger.Info("Executing state", "state", currentStateName)
|
||
|
|
|
||
|
|
state := FindStateByName(spec.States, currentStateName)
|
||
|
|
if state == nil {
|
||
|
|
return Result{}, fmt.Errorf("state not found: %s", currentStateName)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Execute state (Task, Pass, Fail, etc)
|
||
|
|
result, err := ExecuteState(ctx, state, ec)
|
||
|
|
|
||
|
|
// Error handling
|
||
|
|
if err != nil {
|
||
|
|
for _, catchClause := range state.Catch {
|
||
|
|
if MatchesError(err, catchClause.ErrorEquals) {
|
||
|
|
logger.Info("Error caught", "handler", catchClause.Next)
|
||
|
|
currentStateName = catchClause.Next
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return Result{}, fmt.Errorf("state %s failed: %w", currentStateName, err)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store result
|
||
|
|
ec.StepResults[state.Name] = result
|
||
|
|
|
||
|
|
// Check if done
|
||
|
|
if state.End {
|
||
|
|
logger.Info("Workflow completed")
|
||
|
|
return Result{
|
||
|
|
FinalOutput: result,
|
||
|
|
Status: "COMPLETED",
|
||
|
|
}, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// Move to next
|
||
|
|
currentStateName = state.Next
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 4: WORKFLOW SPEC FORMAT
|
||
|
|
|
||
|
|
### State Types
|
||
|
|
|
||
|
|
The llm-router generates states with these types:
|
||
|
|
|
||
|
|
#### Task (Execute Activity)
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "Clone",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CloneRepoActivity",
|
||
|
|
"parameters": {
|
||
|
|
"repo": "${input.repo}",
|
||
|
|
"branch": "${input.branch:main}"
|
||
|
|
},
|
||
|
|
"timeout": "5m",
|
||
|
|
"retry": {
|
||
|
|
"maxAttempts": 3,
|
||
|
|
"backoffRate": 2.0,
|
||
|
|
"initialInterval": "1s"
|
||
|
|
},
|
||
|
|
"catch": [
|
||
|
|
{
|
||
|
|
"errorEquals": ["Timeout", "NetworkError"],
|
||
|
|
"next": "HandleError"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"next": "Analyze"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**LLM decides**:
|
||
|
|
- Which activity to use
|
||
|
|
- How to chain parameters (${Clone.output.path})
|
||
|
|
- Timeout (from knowledge base + LLM reasoning)
|
||
|
|
- Retry policy (from knowledge base + isFlaky flag)
|
||
|
|
- Error handling (catch blocks for flaky activities)
|
||
|
|
|
||
|
|
#### Pass (Static Output)
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "CombineResults",
|
||
|
|
"type": "Pass",
|
||
|
|
"result": {
|
||
|
|
"status": "success",
|
||
|
|
"report": "Analysis complete"
|
||
|
|
},
|
||
|
|
"next": "SendResults"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Fail (Terminate)
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "HandleError",
|
||
|
|
"type": "Fail",
|
||
|
|
"error": "AnalysisFailed",
|
||
|
|
"cause": "Security scan timed out"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 5: EXECUTION FLOW EXAMPLE
|
||
|
|
|
||
|
|
### User Request
|
||
|
|
|
||
|
|
```
|
||
|
|
"Analyze the GitHub repo rockliang/poimen for security vulnerabilities and code quality"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 1: llm-router Activity
|
||
|
|
|
||
|
|
```
|
||
|
|
Input: {message: "...", repo: "https://github.com/rockliang/poimen", branch: "main"}
|
||
|
|
|
||
|
|
LLM Analysis:
|
||
|
|
- User wants: security analysis + code quality
|
||
|
|
- Sequence: Clone → Analyze (quality) → SecurityScan → CombineResults → Notify
|
||
|
|
- Timeouts: Use defaults from knowledge base
|
||
|
|
- Clone: 5m (not flaky)
|
||
|
|
- Analyze: 10m (flaky, so 3 retries)
|
||
|
|
- SecurityScan: 15m (not flaky, so 2 retries)
|
||
|
|
- Combine: 2m (not flaky, 1 retry)
|
||
|
|
- Error handling: Add catch blocks for flaky activities (Analyze)
|
||
|
|
|
||
|
|
Output: WorkflowSpec (as JSON above)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Step 2: RoutingWorkflow Executes
|
||
|
|
|
||
|
|
```
|
||
|
|
State 1: Clone
|
||
|
|
Execute: CloneRepoActivity({repo: "https://...", branch: "main"})
|
||
|
|
Timeout: 5m (from llm decision)
|
||
|
|
Result: {path: "/tmp/cloned-repo"}
|
||
|
|
↓
|
||
|
|
State 2: Analyze
|
||
|
|
Execute: AnalyzeCodeActivity({path: "/tmp/cloned-repo"})
|
||
|
|
Timeout: 10m
|
||
|
|
Retry: 3 attempts (from llm decision, isFlaky=true)
|
||
|
|
Attempt 1: TIMEOUT → Wait 1s
|
||
|
|
Attempt 2: TIMEOUT → Wait 2s
|
||
|
|
Attempt 3: SUCCESS → {quality: 0.85, issues: [...]}
|
||
|
|
↓
|
||
|
|
State 3: SecurityScan
|
||
|
|
Execute: SecurityScanActivity({path: "/tmp/cloned-repo"})
|
||
|
|
Timeout: 15m
|
||
|
|
Retry: 2 attempts (from llm decision, isFlaky=false)
|
||
|
|
Result: {vulnerabilities: [], score: 0.95}
|
||
|
|
↓
|
||
|
|
State 4: CombineResults
|
||
|
|
Execute: CombineResultsActivity({results: [...]})
|
||
|
|
Result: {report: {...}}
|
||
|
|
↓
|
||
|
|
State 5: SendResults
|
||
|
|
Execute: SendNotificationActivity({result: {...}, channel: "email"})
|
||
|
|
Result: {sent: true}
|
||
|
|
↓
|
||
|
|
✅ WORKFLOW COMPLETED
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 6: GO TYPE DEFINITIONS
|
||
|
|
|
||
|
|
```go
|
||
|
|
package routing
|
||
|
|
|
||
|
|
import "time"
|
||
|
|
|
||
|
|
// WorkflowSpec is generated by llm-router (one-time execution)
|
||
|
|
type WorkflowSpec struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Input map[string]interface{} `json:"input"`
|
||
|
|
States []State `json:"states"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// CronWorkflowSpec is generated by llm-router (scheduled execution)
|
||
|
|
type CronWorkflowSpec struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Type string `json:"type"` // "CronWorkflow"
|
||
|
|
Schedule string `json:"schedule"` // Cron expression (e.g., "0 2 * * *")
|
||
|
|
Timezone string `json:"timezone"` // "UTC", "America/New_York", etc
|
||
|
|
Input map[string]interface{} `json:"input"` // Fixed input for each run
|
||
|
|
States []State `json:"states"` // Workflow states
|
||
|
|
MaxConcurrent int `json:"maxConcurrent,omitempty"` // Max parallel runs (default 1)
|
||
|
|
Timeout string `json:"timeout,omitempty"` // Overall timeout per run
|
||
|
|
EnableHistory bool `json:"enableHistory,omitempty"` // Keep execution history
|
||
|
|
}
|
||
|
|
|
||
|
|
// State is a step in the workflow
|
||
|
|
type State struct {
|
||
|
|
Name string
|
||
|
|
Type StateType // "Task", "Pass", "Fail"
|
||
|
|
|
||
|
|
// Task fields
|
||
|
|
Resource string
|
||
|
|
Parameters map[string]interface{}
|
||
|
|
Timeout string
|
||
|
|
Retry *RetryPolicy
|
||
|
|
Catch []CatchClause
|
||
|
|
|
||
|
|
// Pass fields
|
||
|
|
Result interface{}
|
||
|
|
|
||
|
|
// Fail fields
|
||
|
|
Error string
|
||
|
|
Cause string
|
||
|
|
|
||
|
|
// Transition
|
||
|
|
Next string
|
||
|
|
End bool
|
||
|
|
}
|
||
|
|
|
||
|
|
type StateType string
|
||
|
|
|
||
|
|
const (
|
||
|
|
StateTypeTask StateType = "Task"
|
||
|
|
StateTypePass StateType = "Pass"
|
||
|
|
StateTypeFail StateType = "Fail"
|
||
|
|
)
|
||
|
|
|
||
|
|
// RetryPolicy (LLM decides this from knowledge base)
|
||
|
|
type RetryPolicy struct {
|
||
|
|
MaxAttempts int32 `json:"maxAttempts"`
|
||
|
|
BackoffRate float64 `json:"backoffRate"`
|
||
|
|
InitialInterval string `json:"initialInterval"`
|
||
|
|
MaxInterval string `json:"maxInterval,omitempty"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// CatchClause for error handling
|
||
|
|
type CatchClause struct {
|
||
|
|
ErrorEquals []string `json:"errorEquals"`
|
||
|
|
Next string `json:"next"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// ExecutionContext tracks state
|
||
|
|
type ExecutionContext struct {
|
||
|
|
Input map[string]interface{}
|
||
|
|
StepResults map[string]interface{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Result is final output
|
||
|
|
type Result struct {
|
||
|
|
FinalOutput interface{}
|
||
|
|
Status string
|
||
|
|
Error error
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 7: CRON JOB SCHEDULING
|
||
|
|
|
||
|
|
### What is it?
|
||
|
|
|
||
|
|
llm-router can generate workflows that execute **on a schedule** (cron jobs), not just one-time.
|
||
|
|
|
||
|
|
### Example: Daily Security Scan
|
||
|
|
|
||
|
|
User: "Scan all repositories for security vulnerabilities every day at 2 AM"
|
||
|
|
|
||
|
|
llm-router generates:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"name": "daily-security-scan",
|
||
|
|
"type": "CronWorkflow",
|
||
|
|
"schedule": "0 2 * * *",
|
||
|
|
"timezone": "UTC",
|
||
|
|
"input": {
|
||
|
|
"repos": [
|
||
|
|
"https://github.com/rockliang/poimen",
|
||
|
|
"https://github.com/rockliang/workflow-engine"
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"states": [
|
||
|
|
{
|
||
|
|
"name": "Clone",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "CloneRepoActivity",
|
||
|
|
"parameters": {
|
||
|
|
"repo": "${input.repos[0]}"
|
||
|
|
},
|
||
|
|
"timeout": "5m",
|
||
|
|
"retry": {"maxAttempts": 2, "backoffRate": 1.5, "initialInterval": "1s"},
|
||
|
|
"next": "SecurityScan"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SecurityScan",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "SecurityScanActivity",
|
||
|
|
"parameters": {"path": "${Clone.output.path}"},
|
||
|
|
"timeout": "15m",
|
||
|
|
"retry": {"maxAttempts": 2, "backoffRate": 1.5, "initialInterval": "1s"},
|
||
|
|
"next": "SendAlert"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"name": "SendAlert",
|
||
|
|
"type": "Task",
|
||
|
|
"resource": "SendNotificationActivity",
|
||
|
|
"parameters": {
|
||
|
|
"result": "${SecurityScan.output}",
|
||
|
|
"channel": "slack"
|
||
|
|
},
|
||
|
|
"timeout": "2m",
|
||
|
|
"retry": {"maxAttempts": 3, "backoffRate": 2.0, "initialInterval": "1s"},
|
||
|
|
"end": true
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Cron Format
|
||
|
|
|
||
|
|
Standard Unix cron syntax:
|
||
|
|
|
||
|
|
```
|
||
|
|
┌───────────── minute (0 - 59)
|
||
|
|
│ ┌───────────── hour (0 - 23)
|
||
|
|
│ │ ┌───────────── day of month (1 - 31)
|
||
|
|
│ │ │ ┌───────────── month (1 - 12)
|
||
|
|
│ │ │ │ ┌───────────── day of week (0 - 6, 0 = Sunday)
|
||
|
|
│ │ │ │ │
|
||
|
|
│ │ │ │ │
|
||
|
|
* * * * *
|
||
|
|
|
||
|
|
Examples:
|
||
|
|
0 2 * * * = Every day at 2:00 AM
|
||
|
|
0 */6 * * * = Every 6 hours
|
||
|
|
0 9 * * 1-5 = Weekdays at 9 AM
|
||
|
|
0 0 1 * * = First day of month at midnight
|
||
|
|
30 2 * * 0 = Every Sunday at 2:30 AM
|
||
|
|
*/15 * * * * = Every 15 minutes
|
||
|
|
0 12 * * * = Every day at noon
|
||
|
|
```
|
||
|
|
|
||
|
|
### Cron Workflow Fields
|
||
|
|
|
||
|
|
```go
|
||
|
|
type CronWorkflowSpec struct {
|
||
|
|
Name string `json:"name"`
|
||
|
|
Type string `json:"type"` // "CronWorkflow"
|
||
|
|
Schedule string `json:"schedule"` // Cron expression
|
||
|
|
Timezone string `json:"timezone"` // "UTC", "America/New_York", etc
|
||
|
|
Input map[string]interface{} `json:"input"` // Fixed input for each run
|
||
|
|
States []State `json:"states"` // Same as regular workflow
|
||
|
|
|
||
|
|
// Optional
|
||
|
|
MaxConcurrent int `json:"maxConcurrent,omitempty"` // Max parallel executions (default 1)
|
||
|
|
Timeout string `json:"timeout,omitempty"` // Overall timeout per run (default 1h)
|
||
|
|
EnableHistory bool `json:"enableHistory,omitempty"` // Keep execution history?
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Temporal Cron Job Implementation
|
||
|
|
|
||
|
|
```go
|
||
|
|
func SubmitCronWorkflow(cronSpec CronWorkflowSpec) error {
|
||
|
|
client, _ := client.Dial(client.ClientOptions{
|
||
|
|
HostPort: "temporal-frontend.temporal:7233",
|
||
|
|
})
|
||
|
|
|
||
|
|
// For cron, we still use RoutingWorkflow
|
||
|
|
// But Temporal handles the scheduling
|
||
|
|
|
||
|
|
_, err := client.ExecuteWorkflow(
|
||
|
|
context.Background(),
|
||
|
|
client.StartWorkflowOptions{
|
||
|
|
ID: cronSpec.Name,
|
||
|
|
TaskQueue: "poimen-taskqueue",
|
||
|
|
CronSchedule: cronSpec.Schedule, // ← Temporal cron support
|
||
|
|
},
|
||
|
|
RoutingWorkflow,
|
||
|
|
cronSpec.States,
|
||
|
|
)
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Example Use Cases
|
||
|
|
|
||
|
|
```
|
||
|
|
1. Daily Security Scan (0 2 * * *)
|
||
|
|
Input: {repos: [...]}
|
||
|
|
Runs: Every day at 2 AM
|
||
|
|
↓
|
||
|
|
Clone all repos → Security scan → Send alert
|
||
|
|
|
||
|
|
2. Hourly Code Quality Check (0 * * * *)
|
||
|
|
Input: {repo: "https://github.com/rockliang/poimen"}
|
||
|
|
Runs: Every hour
|
||
|
|
↓
|
||
|
|
Clone → Analyze → Judge → Log results
|
||
|
|
|
||
|
|
3. Weekly Performance Benchmark (0 3 * * 0)
|
||
|
|
Input: {repos: [...], benchmark: true}
|
||
|
|
Runs: Every Sunday at 3 AM
|
||
|
|
↓
|
||
|
|
Clone → Performance test → Compare with baseline → Report
|
||
|
|
|
||
|
|
4. Every 15 minutes Health Check (*/15 * * * *)
|
||
|
|
Input: {service: "api"}
|
||
|
|
Runs: Every 15 minutes
|
||
|
|
↓
|
||
|
|
Check service health → Send metrics → Alert if down
|
||
|
|
```
|
||
|
|
|
||
|
|
### llm-router for Cron Jobs
|
||
|
|
|
||
|
|
User: "Run daily security scan at 2 AM UTC on all our repositories"
|
||
|
|
|
||
|
|
llm-router decision:
|
||
|
|
|
||
|
|
```python
|
||
|
|
def llm_router_activity(message: str) -> WorkflowSpec:
|
||
|
|
intent = llm.analyze_intent(message)
|
||
|
|
# intent = {
|
||
|
|
# "is_scheduled": True,
|
||
|
|
# "schedule": "0 2 * * *", # LLM extracts from "2 AM"
|
||
|
|
# "timezone": "UTC",
|
||
|
|
# "needs_security_scan": True,
|
||
|
|
# "target": "all repositories"
|
||
|
|
# }
|
||
|
|
|
||
|
|
if intent.is_scheduled:
|
||
|
|
return CronWorkflowSpec(
|
||
|
|
name="daily-security-scan",
|
||
|
|
type="CronWorkflow",
|
||
|
|
schedule=intent.schedule, # "0 2 * * *"
|
||
|
|
timezone=intent.timezone, # "UTC"
|
||
|
|
input={"repos": get_all_repos()},
|
||
|
|
states=[
|
||
|
|
# Same states as one-time workflow
|
||
|
|
]
|
||
|
|
)
|
||
|
|
else:
|
||
|
|
return OneTimeWorkflowSpec(...)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Execution History
|
||
|
|
|
||
|
|
When `enableHistory=true`, Temporal tracks all cron executions:
|
||
|
|
|
||
|
|
```
|
||
|
|
Cron Workflow ID: daily-security-scan
|
||
|
|
|
||
|
|
Execution 1: 2025-02-01 02:00:00 UTC → COMPLETED
|
||
|
|
Execution 2: 2025-02-02 02:00:00 UTC → COMPLETED
|
||
|
|
Execution 3: 2025-02-03 02:00:00 UTC → FAILED (timeout)
|
||
|
|
Execution 4: 2025-02-04 02:00:00 UTC → COMPLETED
|
||
|
|
Execution 5: 2025-02-05 02:00:00 UTC → COMPLETED (in progress...)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## PART 8: VALIDATION
|
||
|
|
|
||
|
|
Before RoutingWorkflow executes the spec, validate:
|
||
|
|
|
||
|
|
```
|
||
|
|
✅ All state names are unique
|
||
|
|
✅ All state.next references exist
|
||
|
|
✅ All catch.next references exist
|
||
|
|
✅ All Task states have resource defined
|
||
|
|
✅ No circular loops (A→B→C→A)
|
||
|
|
✅ At least one path leads to end
|
||
|
|
✅ No dead ends
|
||
|
|
✅ Timeout format is valid (e.g., "5m")
|
||
|
|
✅ Retry settings are valid
|
||
|
|
✅ JSONPath expressions are syntactically valid
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## SUMMARY
|
||
|
|
|
||
|
|
### The Intelligence is in llm-router
|
||
|
|
|
||
|
|
**llm-router activity**:
|
||
|
|
- ✅ Reads ActivityKnowledgeBase.json
|
||
|
|
- ✅ Uses LLM to understand user intent
|
||
|
|
- ✅ Intelligently selects activities to run
|
||
|
|
- ✅ Orders them by dependencies
|
||
|
|
- ✅ Decides timeout for EACH activity (from knowledge base + LLM reasoning)
|
||
|
|
- ✅ Decides retry policy for EACH activity (flaky? retry 3x : 1x)
|
||
|
|
- ✅ Chains parameters using JSONPath (${Clone.output.path})
|
||
|
|
- ✅ Adds error handling (catch blocks)
|
||
|
|
- ✅ **Detects scheduled workflows** (cron jobs) and generates CronWorkflowSpec
|
||
|
|
- ✅ Returns complete JSON WorkflowSpec (one-time or recurring)
|
||
|
|
|
||
|
|
### The Executor is Generic
|
||
|
|
|
||
|
|
**RoutingWorkflow**:
|
||
|
|
- ✅ Takes JSON spec from llm-router
|
||
|
|
- ✅ Executes states in order
|
||
|
|
- ✅ Respects timeout/retry
|
||
|
|
- ✅ Handles errors with catch blocks
|
||
|
|
- ✅ Works for both one-time and cron workflows
|
||
|
|
- ✅ Returns results
|
||
|
|
|
||
|
|
### Execution Modes
|
||
|
|
|
||
|
|
| Mode | Triggered By | Execution | Example |
|
||
|
|
|------|---|---|---|
|
||
|
|
| **One-Time** | API call / CLI / Direct | Runs once, returns immediately | Analyze repo, generate report |
|
||
|
|
| **Cron Job** | Schedule | Runs repeatedly on schedule | Daily security scan at 2 AM |
|
||
|
|
| **Blocking** | User waits | Synchronous, returns results | Execute template, get output |
|
||
|
|
| **Async** | User polls | Asynchronous, check status later | Submit workflow, poll status |
|
||
|
|
|
||
|
|
### Files Needed
|
||
|
|
|
||
|
|
```
|
||
|
|
1. internal/routing/activity_knowledge_base.json
|
||
|
|
↑ Static metadata about activities (timeouts, retry, flaky, etc)
|
||
|
|
|
||
|
|
2. cmd/worker/activities/llm_router.go
|
||
|
|
↑ LLM-powered activity that generates WorkflowSpec or CronWorkflowSpec
|
||
|
|
|
||
|
|
3. statemachine/routing_workflow.go
|
||
|
|
↑ Generic executor for any spec (one-time or cron)
|
||
|
|
|
||
|
|
4. internal/routing/validator.go
|
||
|
|
↑ Validate specs before execution
|
||
|
|
↑ Validate cron expressions
|
||
|
|
|
||
|
|
5. cmd/api-server/cron_handlers.go (optional)
|
||
|
|
↑ Endpoints to manage cron workflows
|
||
|
|
↑ GET /api/v1/cron/{id}/status
|
||
|
|
↑ DELETE /api/v1/cron/{id} (cancel)
|
||
|
|
```
|
||
|
|
|
||
|
|
This is the **correct, complete architecture**! 🎯
|
||
|
|
|
||
|
|
### Quick Feature Checklist
|
||
|
|
|
||
|
|
- ✅ **One-time workflows** (API/CLI submit)
|
||
|
|
- ✅ **Scheduled workflows** (Cron jobs with schedule)
|
||
|
|
- ✅ **Intelligent routing** (LLM decides what to run)
|
||
|
|
- ✅ **Smart timeouts** (From activity knowledge base)
|
||
|
|
- ✅ **Smart retries** (Based on isFlaky flag)
|
||
|
|
- ✅ **Error handling** (Catch blocks for flaky activities)
|
||
|
|
- ✅ **Parameter chaining** (JSONPath resolution)
|
||
|
|
- ✅ **Execution history** (For cron jobs)
|
||
|
|
- ✅ **Temporal durability** (Automatic replay)
|
||
|
|
|