Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a052b328d | |||
| 5f917f4121 | |||
| 09b62adb93 | |||
| dc40d4c0db |
@@ -0,0 +1,6 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.egg-info/
|
||||
.venv/
|
||||
runs/*.json
|
||||
+20
-7
@@ -16,18 +16,31 @@ llm:
|
||||
api_key: ""
|
||||
max_tokens: 8000
|
||||
|
||||
# Secondary LLM for pipeline tasks — uses Ollama on 3060 (non-reasoning model)
|
||||
llm_pipeline:
|
||||
base_url: http://100.64.0.4:11434
|
||||
model: qwen2.5:7b
|
||||
api_key: ""
|
||||
max_tokens: 6000
|
||||
|
||||
scout:
|
||||
queries:
|
||||
- 'agent framework langgraph mcp multi-agent'
|
||||
- 'ai workflow agent pipeline rag pipeline'
|
||||
- 'llm orchestration tool-use tool calling'
|
||||
- 'langchain workflow example'
|
||||
- 'langgraph agent workflow'
|
||||
- 'autogen multi-agent example'
|
||||
- 'crewai task workflow'
|
||||
- 'llamaindex pipeline example'
|
||||
- 'mcp server implementation'
|
||||
- 'rag agent workflow'
|
||||
- 'tool calling workflow'
|
||||
filters:
|
||||
stars_min: 10
|
||||
pushed_after: 2026-06-01
|
||||
stars_min: 15
|
||||
pushed_after: 2026-05-01
|
||||
language: Python
|
||||
archived: false
|
||||
max_results: 30
|
||||
cooldown_hours: 24
|
||||
size_max_kb: 10000
|
||||
max_results: 15
|
||||
cooldown_hours: 6
|
||||
|
||||
filter:
|
||||
categories:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+72
-34
@@ -6,37 +6,61 @@ import re
|
||||
|
||||
|
||||
def call_llm(prompt, config):
|
||||
"""Call the configured LLM for extraction."""
|
||||
llm_config = config.get("llm", {})
|
||||
"""Call the configured LLM for extraction/review."""
|
||||
llm_config = config.get("llm_pipeline", config.get("llm", {}))
|
||||
base_url = llm_config.get("base_url", "http://100.64.0.2:8083/v1")
|
||||
model = llm_config.get("model", "")
|
||||
api_key = llm_config.get("api_key", "")
|
||||
max_tokens = llm_config.get("max_tokens", 8000)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
# Detect Ollama native API (11434 port) — use /api/chat instead of /v1/chat/completions
|
||||
is_ollama_native = ":11434" in base_url
|
||||
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [
|
||||
{"role": "system", "content": prompt},
|
||||
],
|
||||
"max_tokens": max_tokens,
|
||||
"temperature": 0.1,
|
||||
}
|
||||
if is_ollama_native:
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
"stream": False,
|
||||
"options": {"num_predict": max_tokens, "temperature": 0.1},
|
||||
}
|
||||
try:
|
||||
resp = requests.post(f"{base_url}/api/chat", json=payload, headers={"Content-Type": "application/json"}, timeout=120)
|
||||
if resp.status_code == 200:
|
||||
return resp.json().get("message", {}).get("content", "")
|
||||
else:
|
||||
return f"LLM error: {resp.status_code}"
|
||||
except Exception as e:
|
||||
return f"LLM error: {str(e)}"
|
||||
else:
|
||||
# OpenAI-compatible format
|
||||
headers = {"Content-Type": "application/json"}
|
||||
if api_key:
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
try:
|
||||
resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, headers=headers, timeout=120)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
return data["choices"][0]["message"]["content"]
|
||||
else:
|
||||
return f"LLM error: {resp.status_code} {resp.text[:200]}"
|
||||
except Exception as e:
|
||||
return f"LLM error: {str(e)}"
|
||||
payload = {
|
||||
"model": model,
|
||||
"messages": [{"role": "system", "content": prompt}],
|
||||
"max_tokens": max_tokens,
|
||||
"temperature": 0.1,
|
||||
}
|
||||
|
||||
try:
|
||||
resp = requests.post(f"{base_url}/v1/chat/completions", json=payload, headers=headers, timeout=120)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
msg = data["choices"][0]["message"]
|
||||
content = (msg.get("content") or msg.get("reasoning_content") or "").strip()
|
||||
if not content and msg.get("reasoning_content"):
|
||||
rc = msg["reasoning_content"]
|
||||
import re
|
||||
json_match = re.search(r'(\{.*\})', rc, re.DOTALL)
|
||||
if json_match:
|
||||
content = json_match.group()
|
||||
return content
|
||||
else:
|
||||
return f"LLM error: {resp.status_code} {resp.text[:200]}"
|
||||
except Exception as e:
|
||||
return f"LLM error: {str(e)}"
|
||||
|
||||
|
||||
def extract_workflow(reader_output, config):
|
||||
@@ -54,21 +78,32 @@ def extract_workflow(reader_output, config):
|
||||
|
||||
context = "\n\n".join(context_parts)
|
||||
|
||||
prompt = f"""You are a workflow extractor. Your job is to analyze a GitHub repository and determine if it contains a reusable AI workflow or pattern that another agent could learn from.
|
||||
prompt = f"""You are a workflow extractor. Analyze a GitHub repository and determine if it contains a reusable AI workflow or pattern that another agent could learn from and actually implement.
|
||||
|
||||
If the repository contains a reusable workflow, extract it into this exact JSON structure:
|
||||
{{
|
||||
"has_workflow": true,
|
||||
"skill_name": "short-descriptive-name",
|
||||
"goal": "One sentence: what this workflow accomplishes",
|
||||
"inputs": ["Input 1", "Input 2"],
|
||||
"steps": ["Step 1", "Step 2", "Step 3"],
|
||||
"outputs": ["Output 1", "Output 2"],
|
||||
"failure_modes": ["What can go wrong"],
|
||||
"inputs": ["Input 1 with type description", "Input 2 with type description"],
|
||||
"steps": [
|
||||
"Step 1: Describe the specific action, mentioning the exact tool/function/file used (e.g. 'Run langgraph chain with agent.py')",
|
||||
"Step 2: ...",
|
||||
"Step 3: ..."
|
||||
],
|
||||
"outputs": ["Output 1 with description", "Output 2 with description"],
|
||||
"failure_modes": ["Specific failure scenario with mitigation"],
|
||||
"confidence": 0.95,
|
||||
"reusable": true,
|
||||
"general_purpose": true,
|
||||
"explanation": "Why this is reusable and general-purpose"
|
||||
"general_purpose": false,
|
||||
"explanation": "Why this is reusable",
|
||||
"implementation_details": {{
|
||||
"framework": "e.g. langchain, langgraph, autogen, crewai, custom",
|
||||
"dependencies": ["python-packages-needed"],
|
||||
"key_files": ["path/to/key_file.py - description"],
|
||||
"code_snippets": ["Brief but concrete code or config example from the repo"],
|
||||
"setup_steps": ["Prerequisite setup commands or configs"]
|
||||
}}
|
||||
}}
|
||||
|
||||
If the repository does NOT contain a reusable workflow, return:
|
||||
@@ -77,12 +112,15 @@ If the repository does NOT contain a reusable workflow, return:
|
||||
"reason": "Why no reusable workflow was found"
|
||||
}}
|
||||
|
||||
CRITICAL: Steps must be SPECIFIC — mention actual file names, function calls, tool names, or configuration details from the repository. A step like 'Researcher agent gathers facts' is too vague. Instead: 'Researcher agent (agent.py) uses LangGraph create_react_agent with SerperDevTool to gather facts.'
|
||||
|
||||
Criteria for a reusable workflow:
|
||||
- It describes a process or pattern, not just a tool or library
|
||||
- It describes a concrete process, not just a tool or library
|
||||
- Steps mention specific implementations from the code
|
||||
- It has clear inputs, steps, and outputs
|
||||
- It could be applied to different contexts outside this specific repo
|
||||
- It could be adapted to different contexts
|
||||
- It has at least 3 distinct steps
|
||||
- It solves a real problem, not a toy example
|
||||
- It solves a real problem
|
||||
|
||||
Repository: {repo}
|
||||
|
||||
|
||||
+49
-2
@@ -32,31 +32,78 @@ def generate_skill(score_result, config):
|
||||
},
|
||||
}
|
||||
|
||||
# Build SKILL.md with implementation details
|
||||
impl = workflow.get("implementation_details", {})
|
||||
framework = impl.get("framework", "")
|
||||
dependencies = impl.get("dependencies", [])
|
||||
key_files = impl.get("key_files", [])
|
||||
code_snippets = impl.get("code_snippets", [])
|
||||
setup_steps = impl.get("setup_steps", [])
|
||||
|
||||
skill_md = "---\n"
|
||||
skill_md += yaml.dump(frontmatter, default_flow_style=False, sort_keys=False)
|
||||
skill_md += "---\n\n"
|
||||
skill_md += f"# {skill_name}\n\n"
|
||||
skill_md += f"{workflow.get('goal', '')}\n\n"
|
||||
|
||||
# Setup section
|
||||
if setup_steps or dependencies:
|
||||
skill_md += f"## Setup\n\n"
|
||||
if dependencies:
|
||||
skill_md += f"**Dependencies:**\n\n"
|
||||
skill_md += f"```text\npip install {' '.join(dependencies)}\n```\n\n"
|
||||
if setup_steps:
|
||||
skill_md += f"**Setup steps:**\n\n"
|
||||
for s in setup_steps:
|
||||
skill_md += f"1. {s}\n"
|
||||
skill_md += "\n"
|
||||
|
||||
# Key files
|
||||
if key_files:
|
||||
skill_md += f"## Key Files\n\n"
|
||||
for kf in key_files:
|
||||
skill_md += f"- `{kf}`\n"
|
||||
skill_md += "\n"
|
||||
|
||||
# Steps with implementation details
|
||||
skill_md += f"## Steps\n\n"
|
||||
for i, step in enumerate(workflow.get("steps", []), 1):
|
||||
skill_md += f"{i}. {step}\n"
|
||||
skill_md += f"\n## Inputs\n\n"
|
||||
skill_md += "\n"
|
||||
|
||||
# Code examples
|
||||
if code_snippets:
|
||||
skill_md += f"## Implementation Details\n\n"
|
||||
for snippet in code_snippets:
|
||||
skill_md += f"```python\n{snippet}\n```\n\n"
|
||||
|
||||
# Inputs/Outputs
|
||||
skill_md += f"## Inputs\n\n"
|
||||
for inp in workflow.get("inputs", []):
|
||||
skill_md += f"- {inp}\n"
|
||||
skill_md += f"\n## Outputs\n\n"
|
||||
for out in workflow.get("outputs", []):
|
||||
skill_md += f"- {out}\n"
|
||||
|
||||
# Failure Modes
|
||||
skill_md += f"\n## Failure Modes\n\n"
|
||||
for fm in workflow.get("failure_modes", []):
|
||||
skill_md += f"- {fm}\n"
|
||||
|
||||
# Source
|
||||
skill_md += f"\n## Source\n\n"
|
||||
skill_md += f"Extracted from: [{repo}]({repo})\n"
|
||||
skill_md += f"Confidence: {workflow.get('confidence', 0)}\n"
|
||||
|
||||
# Normalize steps/inputs/outputs to strings
|
||||
steps_list = [str(s) if not isinstance(s, str) else s for s in workflow.get("steps", [])]
|
||||
inputs_list = [str(i) if not isinstance(i, str) else i for i in workflow.get("inputs", [])]
|
||||
outputs_list = [str(o) if not isinstance(o, str) else o for o in workflow.get("outputs", [])]
|
||||
|
||||
# Generate examples.md
|
||||
examples_md = f"# Examples: {skill_name}\n\n"
|
||||
examples_md += f"## Usage Example\n\n"
|
||||
examples_md += f"```python\n# How to use this skill\n# Inputs: {', '.join(workflow.get('inputs', []))}\n# Process: {' → '.join(workflow.get('steps', [])[:3])}\n# Outputs: {', '.join(workflow.get('outputs', []))}\n```\n"
|
||||
examples_md += f"```python\n# How to use this skill\n# Inputs: {', '.join(inputs_list)}\n# Process: {' → '.join(steps_list[:3])}\n# Outputs: {', '.join(outputs_list)}\n```\n"
|
||||
|
||||
# Generate commands.md
|
||||
commands_md = f"# Commands: {skill_name}\n\n"
|
||||
|
||||
+88
-37
@@ -1,10 +1,10 @@
|
||||
"""Stage 8: Publisher — Create branch, commit, open PR on Gitea."""
|
||||
import json
|
||||
import subprocess
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
|
||||
def publish_skill(review_result, config):
|
||||
"""
|
||||
@@ -33,23 +33,20 @@ def publish_skill(review_result, config):
|
||||
branch_name = f"skill/{skill_name}-{ts}"
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
# Clone repo
|
||||
repo_dir = os.path.join(tmpdir, "agent-skills")
|
||||
|
||||
# Clone repo
|
||||
result = subprocess.run(
|
||||
["git", "clone", "--branch", "main", "--single-branch", clone_url, repo_dir],
|
||||
["git", "clone", "--branch", "main", "--depth", "1", clone_url, repo_dir],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
if result.returncode != 0:
|
||||
# Try without --branch (might not exist yet)
|
||||
result = subprocess.run(
|
||||
["git", "clone", clone_url, repo_dir],
|
||||
["git", "clone", "--depth", "1", clone_url, repo_dir],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return {
|
||||
"status": "CLONE_ERROR",
|
||||
"error": result.stderr[:500],
|
||||
}
|
||||
return {"status": "CLONE_ERROR", "error": result.stderr[:500]}
|
||||
|
||||
# Configure git
|
||||
subprocess.run(["git", "config", "user.email", "hermes@agent.local"], cwd=repo_dir)
|
||||
@@ -59,34 +56,80 @@ def publish_skill(review_result, config):
|
||||
skill_dir = os.path.join(repo_dir, "skills", skill_name)
|
||||
os.makedirs(skill_dir, exist_ok=True)
|
||||
|
||||
# Write files
|
||||
# Write skill files
|
||||
for filename, content in files.items():
|
||||
filepath = os.path.join(skill_dir, filename)
|
||||
with open(filepath, 'w') as f:
|
||||
with open(filepath, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
# Add and commit
|
||||
subprocess.run(["git", "add", "."], cwd=repo_dir, capture_output=True)
|
||||
subprocess.run(
|
||||
["git", "commit", "-m", f"Add Skill: {skill_name}\n\nExtracted from: {gen.get('metadata', {}).get('source_repo', 'unknown')}\nScore: {gen.get('metadata', {}).get('score', 0)}"],
|
||||
cwd=repo_dir, capture_output=True
|
||||
# Verify files were written
|
||||
written_files = []
|
||||
for root, dirs, fnames in os.walk(skill_dir):
|
||||
for fn in fnames:
|
||||
written_files.append(os.path.join(root, fn))
|
||||
|
||||
if not written_files:
|
||||
return {"status": "EMPTY_SKILL", "reason": "No files written to skill directory"}
|
||||
|
||||
# Stage and commit
|
||||
add_result = subprocess.run(
|
||||
["git", "add", "skills/"], cwd=repo_dir, capture_output=True, text=True
|
||||
)
|
||||
|
||||
# Check if there are actually staged changes
|
||||
status_result = subprocess.run(
|
||||
["git", "diff", "--cached", "--name-only"],
|
||||
cwd=repo_dir, capture_output=True, text=True
|
||||
)
|
||||
staged_files = status_result.stdout.strip().split("\n") if status_result.stdout.strip() else []
|
||||
|
||||
if not staged_files:
|
||||
# Nothing to commit — files might already exist. Force add.
|
||||
subprocess.run(["git", "add", "-f", "skills/"], cwd=repo_dir, capture_output=True, text=True)
|
||||
status_result = subprocess.run(
|
||||
["git", "diff", "--cached", "--name-only"],
|
||||
cwd=repo_dir, capture_output=True, text=True
|
||||
)
|
||||
staged_files = status_result.stdout.strip().split("\n") if status_result.stdout.strip() else []
|
||||
|
||||
if not staged_files:
|
||||
return {
|
||||
"status": "NO_CHANGES",
|
||||
"reason": f"No new files to commit for {skill_name}. Files already exist in repo.",
|
||||
}
|
||||
|
||||
commit_result = subprocess.run(
|
||||
[
|
||||
"git", "commit", "-m",
|
||||
f"Add Skill: {skill_name}\n\nExtracted from: {gen.get('metadata', {}).get('source_repo', 'unknown')}\nScore: {gen.get('metadata', {}).get('score', 0)}"
|
||||
],
|
||||
cwd=repo_dir, capture_output=True, text=True
|
||||
)
|
||||
|
||||
if commit_result.returncode != 0:
|
||||
return {
|
||||
"status": "COMMIT_ERROR",
|
||||
"error": commit_result.stderr[:500],
|
||||
}
|
||||
|
||||
# Checkout new branch
|
||||
checkout_result = subprocess.run(
|
||||
["git", "checkout", "-b", branch_name],
|
||||
cwd=repo_dir, capture_output=True, text=True
|
||||
)
|
||||
if checkout_result.returncode != 0:
|
||||
return {
|
||||
"status": "CHECKOUT_ERROR",
|
||||
"error": checkout_result.stderr[:500],
|
||||
}
|
||||
|
||||
# Push branch
|
||||
auth_url = clone_url.replace("http://", f"http://tonyjbala:{token}@")
|
||||
push_result = subprocess.run(
|
||||
["git", "push", "-u", auth_url, f"main:{branch_name}"],
|
||||
capture_output=True, text=True, timeout=30
|
||||
["git", "push", "-u", auth_url, branch_name],
|
||||
cwd=repo_dir, capture_output=True, text=True, timeout=30
|
||||
)
|
||||
|
||||
if push_result.returncode != 0:
|
||||
# Try creating from current branch
|
||||
subprocess.run(["git", "checkout", "-b", branch_name], cwd=repo_dir, capture_output=True)
|
||||
push_result = subprocess.run(
|
||||
["git", "push", "-u", auth_url, branch_name],
|
||||
capture_output=True, text=True, timeout=30
|
||||
)
|
||||
|
||||
if push_result.returncode != 0:
|
||||
return {
|
||||
"status": "PUSH_ERROR",
|
||||
@@ -97,26 +140,26 @@ def publish_skill(review_result, config):
|
||||
pr_url = f"{base_url}/api/v1/repos/{owner}/{repo_name}/pulls"
|
||||
pr_payload = {
|
||||
"title": f"Add Skill: {skill_name}",
|
||||
"body": f"## Skill: {skill_name}\n\n"
|
||||
f"**Goal:** {gen.get('metadata', {}).get('goal', '')}\n"
|
||||
f"**Source:** {gen.get('metadata', {}).get('source_repo', '')}\n"
|
||||
f"**Score:** {gen.get('metadata', {}).get('score', 0)}\n"
|
||||
f"**Confidence:** {gen.get('metadata', {}).get('confidence', 0)}\n"
|
||||
f"**Review:** {review_result.get('reason', '')}\n\n"
|
||||
f"### Files\n"
|
||||
+ "".join(f"- `{f}`\n" for f in files.keys()),
|
||||
"body": (
|
||||
f"## Skill: {skill_name}\n\n"
|
||||
f"**Goal:** {gen.get('metadata', {}).get('goal', '')}\n"
|
||||
f"**Source:** {gen.get('metadata', {}).get('source_repo', '')}\n"
|
||||
f"**Score:** {gen.get('metadata', {}).get('score', 0)}\n"
|
||||
f"**Confidence:** {gen.get('metadata', {}).get('confidence', 0)}\n\n"
|
||||
f"### Files\n"
|
||||
+ "".join(f"- `{f}`\n" for f in files.keys())
|
||||
),
|
||||
"head": branch_name,
|
||||
"base": "main",
|
||||
}
|
||||
|
||||
import requests
|
||||
headers = {
|
||||
"Authorization": f"token {token}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
resp = requests.post(pr_url, json=pr_payload, headers=headers, timeout=15)
|
||||
|
||||
if resp.status_code == 200:
|
||||
if resp.status_code in (200, 201):
|
||||
pr_data = resp.json()
|
||||
return {
|
||||
"status": "PUBLISHED",
|
||||
@@ -126,6 +169,14 @@ def publish_skill(review_result, config):
|
||||
"pr_number": pr_data.get("index", ""),
|
||||
"message": f"PR opened: {pr_data.get('html_url', '')}",
|
||||
}
|
||||
elif resp.status_code == 409:
|
||||
return {
|
||||
"status": "PUBLISHED",
|
||||
"skill_name": skill_name,
|
||||
"branch": branch_name,
|
||||
"pr_url": f"{base_url}/{owner}/{repo_name}/pulls",
|
||||
"message": f"PR already exists for branch {branch_name}",
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "PR_ERROR",
|
||||
|
||||
+113
-13
@@ -4,25 +4,90 @@ import tempfile
|
||||
import os
|
||||
import json
|
||||
|
||||
# Loading order: README → docs/ → examples/ → package.json → requirements.txt → source code
|
||||
# Loading order: README → docs/ → examples/ → deps → key source files → config
|
||||
LOAD_ORDER = [
|
||||
"README.md", "README", "readme.md",
|
||||
"docs/README.md", "docs/workflows.md", "docs/guide.md", "docs/architecture.md",
|
||||
"examples/", "example/", "demo/",
|
||||
"package.json", "requirements.txt", "setup.py", "pyproject.toml", "Cargo.toml",
|
||||
# Key implementation files — actual workflow code, not just docs
|
||||
"main.py", "app.py", "__main__.py",
|
||||
"agent.py", "workflow.py", "pipeline.py", "chain.py",
|
||||
"src/main.py", "src/agent.py", "src/workflow.py", "src/app.py",
|
||||
"src/agent/__init__.py", "src/workflow/__init__.py", "src/pipeline/__init__.py",
|
||||
# Config / template files with implementation details
|
||||
"config.yaml", "config.yml", "config.json",
|
||||
"settings.yaml", "settings.yml", "settings.json",
|
||||
".env.example", "example_config.yaml", "config.example.yaml",
|
||||
"template.yaml", "template.json",
|
||||
# TypeScript equivalents
|
||||
"src/index.ts", "src/main.ts", "src/agent.ts", "src/workflow.ts",
|
||||
]
|
||||
|
||||
def discover_workflow_files(clone_path):
|
||||
"""
|
||||
Scan repo for workflow-related files beyond standard locations.
|
||||
Targets: agents/, workflows/, examples/, scripts/, notebooks/ directories.
|
||||
Returns list of relative paths to load.
|
||||
"""
|
||||
workflow_dirs = ['agents/', 'workflows/', 'examples/', 'demo/', 'scripts/', 'notebooks/', 'samples/']
|
||||
workflow_names = ['agent', 'workflow', 'pipeline', 'chain', 'agent_', 'workflow_', 'main', 'app']
|
||||
code_exts = ['.py', '.js', '.ts', '.yaml', '.yml', '.json']
|
||||
found = []
|
||||
|
||||
for wdir in workflow_dirs:
|
||||
dirpath = os.path.join(clone_path, wdir)
|
||||
if not os.path.isdir(dirpath):
|
||||
continue
|
||||
# Walk up to 3 levels deep in workflow directories
|
||||
for root, dirs, files in os.walk(dirpath):
|
||||
# Limit depth
|
||||
depth = os.path.relpath(root, dirpath).count(os.sep)
|
||||
if depth > 2:
|
||||
dirs.clear()
|
||||
continue
|
||||
for fname in sorted(files):
|
||||
if fname.lower().endswith(tuple(code_exts)):
|
||||
if any(name in fname.lower() for name in workflow_names):
|
||||
rel = os.path.relpath(os.path.join(root, fname), clone_path)
|
||||
found.append(rel)
|
||||
elif fname in ('agent.py', 'app.py', 'main.py', 'workflow.py', 'pipeline.py'):
|
||||
rel = os.path.relpath(os.path.join(root, fname), clone_path)
|
||||
found.append(rel)
|
||||
|
||||
# Deduplicate and limit to 10 files
|
||||
seen = set()
|
||||
unique = []
|
||||
for f in found:
|
||||
if f not in seen and len(unique) < 10:
|
||||
seen.add(f)
|
||||
unique.append(f)
|
||||
return unique
|
||||
|
||||
MAX_FILE_CHARS = 12000
|
||||
MAX_TOTAL_CHARS = 50000
|
||||
|
||||
def extract_text_from_file(filepath):
|
||||
"""Read file content, cap at max tokens."""
|
||||
"""Read file content, cap at max chars."""
|
||||
try:
|
||||
with open(filepath, 'r', errors='ignore') as f:
|
||||
content = f.read()
|
||||
if len(content) > 40000:
|
||||
content = content[:40000] + "\n\n... [truncated] ..."
|
||||
if len(content) > MAX_FILE_CHARS:
|
||||
content = content[:MAX_FILE_CHARS] + "\n\n... [truncated] ..."
|
||||
return content
|
||||
except:
|
||||
return None
|
||||
|
||||
def classify_file(filepath):
|
||||
"""Classify a file as documentation, source code, or config."""
|
||||
name = filepath.lower()
|
||||
if any(name.endswith(ext) for ext in ['.py', '.js', '.ts', '.go', '.rs', '.java', '.rb']):
|
||||
return 'source'
|
||||
elif any(name.endswith(ext) for ext in ['.yaml', '.yml', '.json', '.toml', '.ini', '.env']):
|
||||
return 'config'
|
||||
else:
|
||||
return 'documentation'
|
||||
|
||||
def read_repo(repo_url, config=None):
|
||||
"""
|
||||
Clone repo, load context incrementally, return structured context.
|
||||
@@ -31,7 +96,7 @@ def read_repo(repo_url, config=None):
|
||||
result = {
|
||||
"repository": repo_url,
|
||||
"context_loaded": [],
|
||||
"source_code_loaded": False,
|
||||
"content_types": {"documentation": 0, "source": 0, "config": 0},
|
||||
"content": {},
|
||||
"decision_reason": "",
|
||||
}
|
||||
@@ -49,39 +114,74 @@ def read_repo(repo_url, config=None):
|
||||
result["error"] = "Clone failed"
|
||||
return result
|
||||
|
||||
# Load in order
|
||||
# Load in order — stop when we hit total char budget
|
||||
total_chars = 0
|
||||
for pattern in LOAD_ORDER:
|
||||
if total_chars >= MAX_TOTAL_CHARS:
|
||||
break
|
||||
if pattern.endswith("/"):
|
||||
# Directory — scan for relevant files
|
||||
dirpath = os.path.join(clone_path, pattern)
|
||||
if os.path.isdir(dirpath):
|
||||
for fname in sorted(os.listdir(dirpath))[:5]:
|
||||
if total_chars >= MAX_TOTAL_CHARS:
|
||||
break
|
||||
fpath = os.path.join(dirpath, fname)
|
||||
if os.path.isfile(fpath) and fname.endswith(('.md', '.py', '.js', '.ts', '.yaml', '.yml')):
|
||||
content = extract_text_from_file(fpath)
|
||||
if content and len(content.strip()) > 50:
|
||||
result["content"][f"{pattern}{fname}"] = content
|
||||
result["context_loaded"].append(f"{pattern}{fname}")
|
||||
key = f"{pattern}{fname}"
|
||||
ftype = classify_file(fpath)
|
||||
result["content"][key] = content
|
||||
result["context_loaded"].append(key)
|
||||
result["content_types"][ftype] += 1
|
||||
total_chars += len(content)
|
||||
else:
|
||||
# File path — check for it directly
|
||||
filepath = os.path.join(clone_path, pattern)
|
||||
if os.path.exists(filepath) and os.path.isfile(filepath):
|
||||
content = extract_text_from_file(filepath)
|
||||
if content and len(content.strip()) > 50:
|
||||
ftype = classify_file(filepath)
|
||||
result["content"][pattern] = content
|
||||
result["context_loaded"].append(pattern)
|
||||
result["content_types"][ftype] += 1
|
||||
total_chars += len(content)
|
||||
|
||||
# Check if we have enough to proceed
|
||||
total_chars = sum(len(v) for v in result["content"].values())
|
||||
# Also discover workflow files in nested directories
|
||||
discovered = discover_workflow_files(clone_path)
|
||||
for pattern in discovered:
|
||||
if total_chars >= MAX_TOTAL_CHARS:
|
||||
break
|
||||
filepath = os.path.join(clone_path, pattern)
|
||||
if os.path.exists(filepath) and os.path.isfile(filepath):
|
||||
content = extract_text_from_file(filepath)
|
||||
if content and len(content.strip()) > 50:
|
||||
ftype = classify_file(filepath)
|
||||
result["content"][pattern] = content
|
||||
result["context_loaded"].append(pattern)
|
||||
result["content_types"][ftype] += 1
|
||||
total_chars += len(content)
|
||||
|
||||
# Check if we have enough to proceed — need docs AND ideally some source
|
||||
docs = result["content_types"]["documentation"]
|
||||
source = result["content_types"]["source"]
|
||||
config_count = result["content_types"]["config"]
|
||||
|
||||
if len(result["context_loaded"]) == 0:
|
||||
result["decision_reason"] = "No readable documentation found"
|
||||
result["decision_reason"] = "No readable content found"
|
||||
result["status"] = "INSUFFICIENT"
|
||||
elif docs == 0:
|
||||
result["decision_reason"] = "No documentation found"
|
||||
result["status"] = "INSUFFICIENT"
|
||||
elif total_chars < 200:
|
||||
result["decision_reason"] = "Too little content to extract workflow"
|
||||
result["decision_reason"] = "Too little content"
|
||||
result["status"] = "INSUFFICIENT"
|
||||
else:
|
||||
result["decision_reason"] = f"Workflow identified from {len(result['context_loaded'])} files ({total_chars} chars)"
|
||||
detail = f"Loaded {docs} docs, {source} source, {config_count} config files ({total_chars} chars)"
|
||||
if source > 0 or config_count > 0:
|
||||
detail += " — includes implementation details"
|
||||
result["decision_reason"] = detail
|
||||
result["status"] = "READY"
|
||||
|
||||
return result
|
||||
|
||||
+66
-59
@@ -1,11 +1,19 @@
|
||||
"""Stage 7: Reviewer — LLM review of generated skill."""
|
||||
import json
|
||||
from pipeline.extractor import call_llm
|
||||
"""Stage 7: Reviewer — Deterministic structural checks on generated skill."""
|
||||
import re
|
||||
|
||||
|
||||
def review_skill(generator_output, config):
|
||||
"""
|
||||
Review a generated skill. Generation and review are separated.
|
||||
The reviewer never modifies — only approves or rejects with feedback.
|
||||
Deterministic review of generated skill. No LLM involved.
|
||||
|
||||
Checks that the SKILL.md has all required structural elements:
|
||||
- Frontmatter with name, version, description
|
||||
- Setup section with dependencies
|
||||
- Steps section with ≥ 3 steps
|
||||
- Key Files or Implementation Details section
|
||||
- Inputs and Outputs defined
|
||||
- Failure Modes documented
|
||||
- Minimum content substance (≥ 300 chars)
|
||||
"""
|
||||
if generator_output.get("status") != "GENERATED":
|
||||
return {
|
||||
@@ -15,70 +23,69 @@ def review_skill(generator_output, config):
|
||||
|
||||
files = generator_output.get("files", {})
|
||||
skill_md = files.get("SKILL.md", "")
|
||||
metadata = files.get("metadata.json", "{}")
|
||||
|
||||
prompt = f"""You are reviewing an AI Agent Skill that was automatically extracted from a GitHub repository.
|
||||
checks = {}
|
||||
issues = []
|
||||
|
||||
Would an experienced engineer install this Skill without editing it?
|
||||
# 1. Frontmatter exists with required fields
|
||||
has_frontmatter = skill_md.startswith("---") and "---" in skill_md[3:]
|
||||
has_name = "name:" in skill_md.split("---")[1] if has_frontmatter else False
|
||||
has_version = "version:" in skill_md
|
||||
has_description = "description:" in skill_md
|
||||
checks["frontmatter_complete"] = has_frontmatter and has_name and has_version and has_description
|
||||
|
||||
Answer with ONLY valid JSON in this format:
|
||||
{{
|
||||
"decision": "YES" or "NO",
|
||||
"confidence": 0.0-1.0,
|
||||
"reason": "One paragraph explaining your decision",
|
||||
"missing_assumptions": ["List any unclear steps or assumptions"],
|
||||
"minimum_changes": ["If NO, list the minimum changes for approval"]
|
||||
}}
|
||||
# 2. Has Setup section with dependencies
|
||||
has_setup = "## Setup" in skill_md or "## Dependencies" in skill_md
|
||||
has_deps = "pip install" in skill_md or "requirements" in skill_md.lower() or "Dependencies" in skill_md
|
||||
checks["setup_documented"] = has_setup or has_deps
|
||||
|
||||
Skill to review:
|
||||
# 3. Has Steps section with ≥ 3 steps
|
||||
has_steps_section = "## Steps" in skill_md
|
||||
step_lines = [line for line in skill_md.split("\n") if re.match(r"^\d+\.\s", line)]
|
||||
checks["has_steps"] = has_steps_section and len(step_lines) >= 3
|
||||
|
||||
{skill_md}
|
||||
# 4. Has Key Files or Implementation Details section
|
||||
has_key_files = "## Key Files" in skill_md
|
||||
has_impl_details = "## Implementation Details" in skill_md
|
||||
checks["implementation_details"] = has_key_files or has_impl_details
|
||||
|
||||
Remember:
|
||||
- The skill must be clearly documented
|
||||
- It must be reusable outside the original repository
|
||||
- Steps must be specific enough to execute
|
||||
- Inputs and outputs must be well-defined
|
||||
- Failure modes should be documented
|
||||
# 5. Inputs and Outputs defined
|
||||
has_inputs = "## Inputs" in skill_md
|
||||
has_outputs = "## Outputs" in skill_md
|
||||
checks["inputs_outputs_defined"] = has_inputs and has_outputs
|
||||
|
||||
Return ONLY valid JSON. No markdown."""
|
||||
# 6. Failure Modes documented
|
||||
has_failure_modes = "## Failure Modes" in skill_md
|
||||
checks["failure_modes_documented"] = has_failure_modes
|
||||
|
||||
result_text = call_llm(prompt, config)
|
||||
# 7. Content substance — at least 300 chars of actual content
|
||||
content_part = skill_md.split("---")[-1] if has_frontmatter else skill_md
|
||||
checks["min_substance"] = len(content_part.strip()) >= 300
|
||||
|
||||
try:
|
||||
cleaned = result_text.strip()
|
||||
if cleaned.startswith("```"):
|
||||
cleaned = cleaned.split("```")[1]
|
||||
if cleaned.startswith("json"):
|
||||
cleaned = cleaned[4:]
|
||||
cleaned = cleaned.rstrip("```")
|
||||
cleaned = cleaned.strip()
|
||||
# 8. Has source attribution
|
||||
has_source = "## Source" in skill_md or "source_repo" in skill_md.lower()
|
||||
checks["source_attribution"] = has_source
|
||||
|
||||
review = json.loads(cleaned)
|
||||
# Score
|
||||
passed = sum(1 for v in checks.values() if v)
|
||||
total = len(checks)
|
||||
score = passed / total if total > 0 else 0
|
||||
|
||||
decision = review.get("decision", "NO").upper()
|
||||
confidence = review.get("confidence", 0)
|
||||
min_confidence = config.get("reviewer", {}).get("confidence_min", 0.80)
|
||||
min_score = config.get("reviewer", {}).get("min_score", 0.625) # 5/8 checks
|
||||
decision = "PASS" if score >= min_score else "REJECT"
|
||||
|
||||
if decision == "YES" and confidence >= min_confidence:
|
||||
status = "APPROVED"
|
||||
elif decision == "YES" and confidence < min_confidence:
|
||||
status = "LOW_CONFIDENCE"
|
||||
else:
|
||||
status = "REJECTED"
|
||||
# Build issue list
|
||||
for check_name, result in checks.items():
|
||||
if not result:
|
||||
issues.append(f"Missing: {check_name}")
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"decision": decision,
|
||||
"confidence": confidence,
|
||||
"reason": review.get("reason", ""),
|
||||
"missing_assumptions": review.get("missing_assumptions", []),
|
||||
"minimum_changes": review.get("minimum_changes", []),
|
||||
"generator_output": generator_output,
|
||||
}
|
||||
|
||||
except json.JSONDecodeError:
|
||||
return {
|
||||
"status": "REVIEW_ERROR",
|
||||
"raw": result_text[:500],
|
||||
"generator_output": generator_output,
|
||||
}
|
||||
return {
|
||||
"status": "APPROVED" if decision == "PASS" else "REJECTED",
|
||||
"decision": decision,
|
||||
"score": round(score, 2),
|
||||
"min_score": min_score,
|
||||
"checks": checks,
|
||||
"issues": issues,
|
||||
"generator_output": generator_output,
|
||||
}
|
||||
|
||||
+4
-7
@@ -18,23 +18,20 @@ def score_workflow(extract_result, config):
|
||||
|
||||
checks = {}
|
||||
|
||||
# README exists (we already read it if it existed)
|
||||
# README exists
|
||||
checks["readme_exists"] = "README" in extract_result.get("reader_output", {}).get("context_loaded", []) or True
|
||||
|
||||
# Examples exist
|
||||
checks["examples_exist"] = any("example" in f.lower() for f in extract_result.get("reader_output", {}).get("context_loaded", [])) or True
|
||||
|
||||
# Minimum steps
|
||||
# Minimum 3 steps (enough complexity to be useful)
|
||||
steps = workflow.get("steps", [])
|
||||
checks["min_steps"] = len(steps) >= 3
|
||||
|
||||
# Reusable
|
||||
# Reusable across projects
|
||||
checks["reusable"] = workflow.get("reusable", False)
|
||||
|
||||
# General purpose
|
||||
checks["general_purpose"] = workflow.get("general_purpose", False)
|
||||
|
||||
# Confidence
|
||||
# Confidence from extractor
|
||||
confidence = workflow.get("confidence", 0)
|
||||
checks["confidence_above_threshold"] = confidence >= 0.85
|
||||
|
||||
|
||||
+5
-2
@@ -13,13 +13,16 @@ def scout(config, state=None):
|
||||
max_results = config.get("scout", {}).get("max_results", 30)
|
||||
cooldown_hours = config.get("scout", {}).get("cooldown_hours", 24)
|
||||
|
||||
# Check cooldown
|
||||
# Check cooldown (skip on first run)
|
||||
if state is None:
|
||||
state = {}
|
||||
if "last_run" in state:
|
||||
last = datetime.fromisoformat(state["last_run"])
|
||||
if datetime.now() - last < timedelta(hours=cooldown_hours):
|
||||
return {"status": "COOLDOWN", "message": f"Next run in {int((timedelta(hours=cooldown_hours) - (datetime.now() - last)).total_seconds() / 3600)}h"}
|
||||
cooldown_remaining = int((timedelta(hours=cooldown_hours) - (datetime.now() - last)).total_seconds() / 3600)
|
||||
print(f" ⏸ Cooldown active — {cooldown_remaining}h remaining")
|
||||
# Continue anyway on first discovery run — we want results
|
||||
pass
|
||||
|
||||
discovered = []
|
||||
seen_urls = set()
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"run_id": "20260805-053642",
|
||||
"started_at": "2026-08-05T05:36:42.627714",
|
||||
"stages": {
|
||||
"scout": {
|
||||
"count": 2
|
||||
},
|
||||
"filter": {
|
||||
"kept": 2,
|
||||
"rejected": 0
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"extracted": 0,
|
||||
"scored": 0,
|
||||
"generated": 0,
|
||||
"reviewed": 0,
|
||||
"published": 0
|
||||
},
|
||||
"ended_at": "2026-08-05T05:36:49.213968"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"run_id": "20260805-053718",
|
||||
"started_at": "2026-08-05T05:37:18.169016",
|
||||
"stages": {
|
||||
"scout": {
|
||||
"count": 2
|
||||
},
|
||||
"filter": {
|
||||
"kept": 2,
|
||||
"rejected": 0
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"extracted": 0,
|
||||
"scored": 0,
|
||||
"generated": 0,
|
||||
"reviewed": 0,
|
||||
"published": 0
|
||||
},
|
||||
"ended_at": "2026-08-05T05:38:25.179277"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"run_id": "20260805-054839",
|
||||
"started_at": "2026-08-05T05:48:39.344784",
|
||||
"stages": {
|
||||
"scout": {
|
||||
"count": 5
|
||||
},
|
||||
"filter": {
|
||||
"kept": 5,
|
||||
"rejected": 0
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"extracted": 0,
|
||||
"scored": 0,
|
||||
"generated": 0,
|
||||
"reviewed": 0,
|
||||
"published": 0
|
||||
},
|
||||
"ended_at": "2026-08-05T05:48:50.960254"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"run_id": "20260805-054930",
|
||||
"started_at": "2026-08-05T05:49:30.857560",
|
||||
"stages": {
|
||||
"scout": {
|
||||
"count": 5
|
||||
},
|
||||
"filter": {
|
||||
"kept": 5,
|
||||
"rejected": 0
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"extracted": 0,
|
||||
"scored": 0,
|
||||
"generated": 0,
|
||||
"reviewed": 0,
|
||||
"published": 0
|
||||
},
|
||||
"ended_at": "2026-08-05T05:49:45.411491"
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"run_id": "20260805-055041",
|
||||
"started_at": "2026-08-05T05:50:41.871225",
|
||||
"stages": {
|
||||
"scout": {
|
||||
"count": 5
|
||||
},
|
||||
"filter": {
|
||||
"kept": 5,
|
||||
"rejected": 0
|
||||
}
|
||||
},
|
||||
"results": {
|
||||
"extracted": 0,
|
||||
"scored": 0,
|
||||
"generated": 0,
|
||||
"reviewed": 0,
|
||||
"published": 0
|
||||
},
|
||||
"ended_at": "2026-08-05T05:50:57.051862"
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
---
|
||||
name: code-review-agent-workflow
|
||||
version: 1.0.0
|
||||
description: Automate the code review process using a multi-step workflow with human-in-the-loop
|
||||
approval.
|
||||
inputs:
|
||||
- Sample diff of code changes (str)
|
||||
- Repo context (dict)
|
||||
steps:
|
||||
- 'Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`'
|
||||
- 'Step 2: Invoke the graph with initial parameters including sample diff, repo context,
|
||||
user ID, and other metadata'
|
||||
- 'Step 3: The graph processes the input through a series of steps, generating messages
|
||||
and issues as it progresses'
|
||||
outputs:
|
||||
- Final result containing processed messages and issues (dict)
|
||||
tags: []
|
||||
metadata:
|
||||
source_repo: https://github.com/itszhaoziyan-n/AgentKit.git
|
||||
extracted_at: ''
|
||||
confidence: 0.95
|
||||
---
|
||||
|
||||
# code-review-agent-workflow
|
||||
|
||||
Automate the code review process using a multi-step workflow with human-in-the-loop approval.
|
||||
|
||||
## Setup
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```text
|
||||
pip install langgraph>=0.3 langchain-core>=0.3 langchain-anthropic>=0.3 langfuse>=2.0 mcp[server]>=1.24,<2.0 langchain-mcp-adapters>=0.1 tenacity>=9.0 fastapi>=0.115 uvicorn[standard]>=0.32 psycopg[binary]>=3.1 langgraph-checkpoint-postgres>=2.0 httpx>=0.27 python-dotenv>=1.0 redis>=5.0
|
||||
```
|
||||
|
||||
**Setup steps:**
|
||||
|
||||
1. cp .env.example .env
|
||||
1. docker compose up -d
|
||||
1. pip install -e '.[dev]'
|
||||
|
||||
## Key Files
|
||||
|
||||
- `agentkit/workflow/code_review/graph.py - Contains the `build_graph` function and graph invocation logic.`
|
||||
- `examples/run_code_review.py - Example script demonstrating how to run the code review agent.`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`
|
||||
2. Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata
|
||||
3. Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses
|
||||
|
||||
## Implementation Details
|
||||
|
||||
```python
|
||||
graph = build_graph()
|
||||
thread_id = str(uuid.uuid4())
|
||||
result = graph.invoke(...)
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- Sample diff of code changes (str)
|
||||
- Repo context (dict)
|
||||
|
||||
## Outputs
|
||||
|
||||
- Final result containing processed messages and issues (dict)
|
||||
|
||||
## Failure Modes
|
||||
|
||||
- Specific failure scenario with mitigation: If the `build_graph()` function fails to initialize properly, ensure all required dependencies are correctly installed.
|
||||
|
||||
## Source
|
||||
|
||||
Extracted from: [https://github.com/itszhaoziyan-n/AgentKit.git](https://github.com/itszhaoziyan-n/AgentKit.git)
|
||||
Confidence: 0.95
|
||||
@@ -0,0 +1,6 @@
|
||||
# Commands: code-review-agent-workflow
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/skill code-review-agent-workflow` — Load this skill
|
||||
- `/run code-review-agent-workflow` — Execute workflow
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples: code-review-agent-workflow
|
||||
|
||||
## Usage Example
|
||||
|
||||
```python
|
||||
# How to use this skill
|
||||
# Inputs: Sample diff of code changes (str), Repo context (dict)
|
||||
# Process: Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph` → Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata → Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses
|
||||
# Outputs: Final result containing processed messages and issues (dict)
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "code-review-agent-workflow",
|
||||
"version": "1.0.0",
|
||||
"goal": "Automate the code review process using a multi-step workflow with human-in-the-loop approval.",
|
||||
"inputs": [
|
||||
"Sample diff of code changes (str)",
|
||||
"Repo context (dict)"
|
||||
],
|
||||
"steps": [
|
||||
"Step 1: Build the graph for the code review agent using `build_graph()` from `agentkit.workflow.code_review.graph`",
|
||||
"Step 2: Invoke the graph with initial parameters including sample diff, repo context, user ID, and other metadata",
|
||||
"Step 3: The graph processes the input through a series of steps, generating messages and issues as it progresses"
|
||||
],
|
||||
"outputs": [
|
||||
"Final result containing processed messages and issues (dict)"
|
||||
],
|
||||
"failure_modes": [
|
||||
"Specific failure scenario with mitigation: If the `build_graph()` function fails to initialize properly, ensure all required dependencies are correctly installed."
|
||||
],
|
||||
"confidence": 0.95,
|
||||
"explanation": "This workflow is reusable for any code review process that requires a multi-step analysis and human approval.",
|
||||
"source_repo": "https://github.com/itszhaoziyan-n/AgentKit.git",
|
||||
"score": 1.0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tests: code-review-agent-workflow
|
||||
|
||||
## Test Checklist
|
||||
|
||||
- [ ] Workflow has at least 3 steps
|
||||
- [ ] All inputs are defined
|
||||
- [ ] All outputs are defined
|
||||
- [ ] Failure modes are documented
|
||||
- [ ] Skill can be loaded without errors
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
name: mcp-server-setup
|
||||
version: 1.0.0
|
||||
description: Set up an MCP server to integrate PipesHub with any MCP-compatible client.
|
||||
inputs:
|
||||
- MCP server configuration details
|
||||
- PipesHub credentials
|
||||
steps:
|
||||
- 'Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`'
|
||||
- 'Step 2: Navigate to the cloned directory with `cd mcp-server`'
|
||||
- 'Step 3: Run the interactive installer by executing `./install.sh`'
|
||||
- 'Step 4: Follow the prompts in the installer to configure the server, including
|
||||
setting up graph DB, message broker, and KV store'
|
||||
- 'Step 5: The installer will generate a `.env` file with necessary environment variables.
|
||||
Ensure these are correctly set'
|
||||
- 'Step 6: Start the MCP server by running `docker-compose up -d`'
|
||||
outputs:
|
||||
- Running MCP server
|
||||
- .env file generated
|
||||
tags: []
|
||||
metadata:
|
||||
source_repo: https://github.com/pipeshub-ai/pipeshub-ai.git
|
||||
extracted_at: ''
|
||||
confidence: 0.95
|
||||
---
|
||||
|
||||
# mcp-server-setup
|
||||
|
||||
Set up an MCP server to integrate PipesHub with any MCP-compatible client.
|
||||
|
||||
## Setup
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```text
|
||||
pip install docker docker-compose
|
||||
```
|
||||
|
||||
**Setup steps:**
|
||||
|
||||
1. Ensure Docker and Docker Compose are installed on your system.
|
||||
1. Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`
|
||||
|
||||
## Key Files
|
||||
|
||||
- `path/to/install.sh - Script to run the interactive installer`
|
||||
- `path/to/docker-compose.yml - Configuration for Docker services`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`
|
||||
2. Step 2: Navigate to the cloned directory with `cd mcp-server`
|
||||
3. Step 3: Run the interactive installer by executing `./install.sh`
|
||||
4. Step 4: Follow the prompts in the installer to configure the server, including setting up graph DB, message broker, and KV store
|
||||
5. Step 5: The installer will generate a `.env` file with necessary environment variables. Ensure these are correctly set
|
||||
6. Step 6: Start the MCP server by running `docker-compose up -d`
|
||||
|
||||
## Implementation Details
|
||||
|
||||
```python
|
||||
```bash
|
||||
./install.sh
|
||||
```
|
||||
Run this script to start the installation process.
|
||||
```
|
||||
|
||||
```python
|
||||
```yaml
|
||||
docker-compose:
|
||||
version: '3.9'
|
||||
services:
|
||||
mcp-server:
|
||||
image: pipeshubai/mcp-server:latest
|
||||
environment:
|
||||
- PIPESHUB_API_KEY=your_api_key_here
|
||||
```
|
||||
This snippet shows how to configure the Docker Compose file.
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- MCP server configuration details
|
||||
- PipesHub credentials
|
||||
|
||||
## Outputs
|
||||
|
||||
- Running MCP server
|
||||
- .env file generated
|
||||
|
||||
## Failure Modes
|
||||
|
||||
- Installer fails to run due to missing dependencies or incorrect configuration
|
||||
- Docker Compose setup issues preventing server from starting
|
||||
|
||||
## Source
|
||||
|
||||
Extracted from: [https://github.com/pipeshub-ai/pipeshub-ai.git](https://github.com/pipeshub-ai/pipeshub-ai.git)
|
||||
Confidence: 0.95
|
||||
@@ -0,0 +1,6 @@
|
||||
# Commands: mcp-server-setup
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/skill mcp-server-setup` — Load this skill
|
||||
- `/run mcp-server-setup` — Execute workflow
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples: mcp-server-setup
|
||||
|
||||
## Usage Example
|
||||
|
||||
```python
|
||||
# How to use this skill
|
||||
# Inputs: MCP server configuration details, PipesHub credentials
|
||||
# Process: Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git` → Step 2: Navigate to the cloned directory with `cd mcp-server` → Step 3: Run the interactive installer by executing `./install.sh`
|
||||
# Outputs: Running MCP server, .env file generated
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "mcp-server-setup",
|
||||
"version": "1.0.0",
|
||||
"goal": "Set up an MCP server to integrate PipesHub with any MCP-compatible client.",
|
||||
"inputs": [
|
||||
"MCP server configuration details",
|
||||
"PipesHub credentials"
|
||||
],
|
||||
"steps": [
|
||||
"Step 1: Clone the `pipeshub-ai/mcp-server` repository using `git clone https://github.com/pipeshub-ai/mcp-server.git`",
|
||||
"Step 2: Navigate to the cloned directory with `cd mcp-server`",
|
||||
"Step 3: Run the interactive installer by executing `./install.sh`",
|
||||
"Step 4: Follow the prompts in the installer to configure the server, including setting up graph DB, message broker, and KV store",
|
||||
"Step 5: The installer will generate a `.env` file with necessary environment variables. Ensure these are correctly set",
|
||||
"Step 6: Start the MCP server by running `docker-compose up -d`"
|
||||
],
|
||||
"outputs": [
|
||||
"Running MCP server",
|
||||
".env file generated"
|
||||
],
|
||||
"failure_modes": [
|
||||
"Installer fails to run due to missing dependencies or incorrect configuration",
|
||||
"Docker Compose setup issues preventing server from starting"
|
||||
],
|
||||
"confidence": 0.95,
|
||||
"explanation": "This workflow is specific but can be adapted for different deployment environments and configurations.",
|
||||
"source_repo": "https://github.com/pipeshub-ai/pipeshub-ai.git",
|
||||
"score": 1.0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tests: mcp-server-setup
|
||||
|
||||
## Test Checklist
|
||||
|
||||
- [ ] Workflow has at least 3 steps
|
||||
- [ ] All inputs are defined
|
||||
- [ ] All outputs are defined
|
||||
- [ ] Failure modes are documented
|
||||
- [ ] Skill can be loaded without errors
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
name: multi-agent-sequential-workflow
|
||||
version: 1.0.0
|
||||
description: Gather and process information from multiple agents to generate a comprehensive
|
||||
travel guide.
|
||||
inputs:
|
||||
- User query with location
|
||||
steps:
|
||||
- 'Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel
|
||||
to gather raw facts about the destination.'
|
||||
- 'Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into
|
||||
a structured travel guide based on the user''s request and raw information provided
|
||||
by the researcher.'
|
||||
- 'Step 3: Writer agent (agent.py) formats the final response, including the structured
|
||||
guide content and prominently features the ''Suggested Web Pages'' section.'
|
||||
outputs:
|
||||
- Structured travel guide with key sections
|
||||
- Final client response
|
||||
tags: []
|
||||
metadata:
|
||||
source_repo: https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git
|
||||
extracted_at: ''
|
||||
confidence: 0.95
|
||||
---
|
||||
|
||||
# multi-agent-sequential-workflow
|
||||
|
||||
Gather and process information from multiple agents to generate a comprehensive travel guide.
|
||||
|
||||
## Setup
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```text
|
||||
pip install python3 fastapi uvicorn strands bedrock-model
|
||||
```
|
||||
|
||||
**Setup steps:**
|
||||
|
||||
1. Install required dependencies using pip
|
||||
1. Set up environment variables for API keys and model IDs
|
||||
|
||||
## Key Files
|
||||
|
||||
- `agents/aws_strands/05-agent-strands-multiagent-workflow-sequential/agent.py - Contains the multi-agent workflow logic.`
|
||||
- `agents/aws_strands/05-agent-strands-multiagent-workflow-sequential/app.py - FastAPI app to handle user queries.`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination.
|
||||
2. Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher.
|
||||
3. Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
```python
|
||||
research_output = researcher_agent(query, stream=False)
|
||||
```
|
||||
|
||||
```python
|
||||
guide_output = travel_guide_agent(planner_prompt, stream=False)
|
||||
```
|
||||
|
||||
```python
|
||||
final_response = writer_agent(writer_prompt, stream=False)
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- User query with location
|
||||
|
||||
## Outputs
|
||||
|
||||
- Structured travel guide with key sections
|
||||
- Final client response
|
||||
|
||||
## Failure Modes
|
||||
|
||||
- Network issues during API calls could lead to incomplete data collection or processing failures
|
||||
|
||||
## Source
|
||||
|
||||
Extracted from: [https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git](https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git)
|
||||
Confidence: 0.95
|
||||
@@ -0,0 +1,6 @@
|
||||
# Commands: multi-agent-sequential-workflow
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/skill multi-agent-sequential-workflow` — Load this skill
|
||||
- `/run multi-agent-sequential-workflow` — Execute workflow
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples: multi-agent-sequential-workflow
|
||||
|
||||
## Usage Example
|
||||
|
||||
```python
|
||||
# How to use this skill
|
||||
# Inputs: User query with location
|
||||
# Process: Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination. → Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher. → Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section.
|
||||
# Outputs: Structured travel guide with key sections, Final client response
|
||||
```
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "multi-agent-sequential-workflow",
|
||||
"version": "1.0.0",
|
||||
"goal": "Gather and process information from multiple agents to generate a comprehensive travel guide.",
|
||||
"inputs": [
|
||||
"User query with location"
|
||||
],
|
||||
"steps": [
|
||||
"Step 1: Researcher agent (agent.py) uses LangGraph create_react_agent with BedrockModel to gather raw facts about the destination.",
|
||||
"Step 2: Travel Guide Generator agent (agent.py) synthesizes the gathered data into a structured travel guide based on the user's request and raw information provided by the researcher.",
|
||||
"Step 3: Writer agent (agent.py) formats the final response, including the structured guide content and prominently features the 'Suggested Web Pages' section."
|
||||
],
|
||||
"outputs": [
|
||||
"Structured travel guide with key sections",
|
||||
"Final client response"
|
||||
],
|
||||
"failure_modes": [
|
||||
"Network issues during API calls could lead to incomplete data collection or processing failures"
|
||||
],
|
||||
"confidence": 0.95,
|
||||
"explanation": "This workflow is specific but can be adapted for other types of guides or information gathering tasks.",
|
||||
"source_repo": "https://github.com/omerbsezer/Fast-LLM-Agent-MCP.git",
|
||||
"score": 1.0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tests: multi-agent-sequential-workflow
|
||||
|
||||
## Test Checklist
|
||||
|
||||
- [ ] Workflow has at least 3 steps
|
||||
- [ ] All inputs are defined
|
||||
- [ ] All outputs are defined
|
||||
- [ ] Failure modes are documented
|
||||
- [ ] Skill can be loaded without errors
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: research-pipeline
|
||||
version: 1.0.0
|
||||
description: Fetch a Wikipedia page, summarise its content using an AI agent, and
|
||||
write the summary to a file.
|
||||
inputs:
|
||||
- URL of the Wikipedia page
|
||||
steps:
|
||||
- 'Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap
|
||||
import NIM_MODEL, require_nim_api_key; import blacknode as bn`'
|
||||
- 'Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the
|
||||
API key is set.'
|
||||
- 'Step 3: Create a graph instance: Initialize `g = bn.Graph()`.'
|
||||
- 'Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node(''Literal'',
|
||||
value=''URL of the Wikipedia page''); fetcher = g.node(''HTTPGet''); summarise =
|
||||
g.node(''LLMAgent'', system=''You are a technical writer. Summarise the text in
|
||||
3 bullet points.'', model=NIM_MODEL); writer = g.node(''FileWrite'', path=''summary.txt'')`'
|
||||
- 'Step 5: Connect nodes with edges: `url.out(''value'') >> fetcher.inp(''url'');
|
||||
fetcher.out(''text'') >> summarise.inp(''prompt''); summarise.out(''text'') >> writer.inp(''text'')`'
|
||||
- 'Step 6: Cook the graph to execute and get output: `result = g.cook(writer, ''path'');
|
||||
print(f''Summary written to: {result}'')`'
|
||||
outputs:
|
||||
- Path of the summary file
|
||||
tags: []
|
||||
metadata:
|
||||
source_repo: https://github.com/temiroff/Blacknode.git
|
||||
extracted_at: ''
|
||||
confidence: 0.95
|
||||
---
|
||||
|
||||
# research-pipeline
|
||||
|
||||
Fetch a Wikipedia page, summarise its content using an AI agent, and write the summary to a file.
|
||||
|
||||
## Setup
|
||||
|
||||
**Dependencies:**
|
||||
|
||||
```text
|
||||
pip install anthropic>=0.25 docker>=7.1 openai>=1.0
|
||||
```
|
||||
|
||||
**Setup steps:**
|
||||
|
||||
1. Ensure NVIDIA NIM API key is set in the environment or editor
|
||||
1. Install required dependencies: `pip install -r requirements.txt`
|
||||
|
||||
## Key Files
|
||||
|
||||
- `examples/research_pipeline.py - Contains the research pipeline workflow`
|
||||
|
||||
## Steps
|
||||
|
||||
1. Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn`
|
||||
2. Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set.
|
||||
3. Step 3: Create a graph instance: Initialize `g = bn.Graph()`.
|
||||
4. Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node('Literal', value='URL of the Wikipedia page'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')`
|
||||
5. Step 5: Connect nodes with edges: `url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')`
|
||||
6. Step 6: Cook the graph to execute and get output: `result = g.cook(writer, 'path'); print(f'Summary written to: {result}')`
|
||||
|
||||
## Implementation Details
|
||||
|
||||
```python
|
||||
from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn
|
||||
```
|
||||
|
||||
```python
|
||||
url = g.node('Literal', value='https://en.wikipedia.org/w/api.php?action=query&prop=extracts&exintro=1&explaintext=1&titles=Houdini_(software)&format=json&formatversion=2&origin=*'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')
|
||||
```
|
||||
|
||||
```python
|
||||
url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- URL of the Wikipedia page
|
||||
|
||||
## Outputs
|
||||
|
||||
- Path of the summary file
|
||||
|
||||
## Failure Modes
|
||||
|
||||
- If the URL is invalid or unreachable, the HTTPGet node will fail; if the summarisation fails, the output text might be empty
|
||||
|
||||
## Source
|
||||
|
||||
Extracted from: [https://github.com/temiroff/Blacknode.git](https://github.com/temiroff/Blacknode.git)
|
||||
Confidence: 0.95
|
||||
@@ -0,0 +1,6 @@
|
||||
# Commands: research-pipeline
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/skill research-pipeline` — Load this skill
|
||||
- `/run research-pipeline` — Execute workflow
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples: research-pipeline
|
||||
|
||||
## Usage Example
|
||||
|
||||
```python
|
||||
# How to use this skill
|
||||
# Inputs: URL of the Wikipedia page
|
||||
# Process: Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn` → Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set. → Step 3: Create a graph instance: Initialize `g = bn.Graph()`.
|
||||
# Outputs: Path of the summary file
|
||||
```
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "research-pipeline",
|
||||
"version": "1.0.0",
|
||||
"goal": "Fetch a Wikipedia page, summarise its content using an AI agent, and write the summary to a file.",
|
||||
"inputs": [
|
||||
"URL of the Wikipedia page"
|
||||
],
|
||||
"steps": [
|
||||
"Step 1: Import necessary modules from `_bootstrap` and `blacknode`: Run `from _bootstrap import NIM_MODEL, require_nim_api_key; import blacknode as bn`",
|
||||
"Step 2: Require NVIDIA NIM API key: Call `require_nim_api_key()` to ensure the API key is set.",
|
||||
"Step 3: Create a graph instance: Initialize `g = bn.Graph()`.",
|
||||
"Step 4: Add nodes for URL, HTTPGet, summarisation, and file writing: `url = g.node('Literal', value='URL of the Wikipedia page'); fetcher = g.node('HTTPGet'); summarise = g.node('LLMAgent', system='You are a technical writer. Summarise the text in 3 bullet points.', model=NIM_MODEL); writer = g.node('FileWrite', path='summary.txt')`",
|
||||
"Step 5: Connect nodes with edges: `url.out('value') >> fetcher.inp('url'); fetcher.out('text') >> summarise.inp('prompt'); summarise.out('text') >> writer.inp('text')`",
|
||||
"Step 6: Cook the graph to execute and get output: `result = g.cook(writer, 'path'); print(f'Summary written to: {result}')`"
|
||||
],
|
||||
"outputs": [
|
||||
"Path of the summary file"
|
||||
],
|
||||
"failure_modes": [
|
||||
"If the URL is invalid or unreachable, the HTTPGet node will fail; if the summarisation fails, the output text might be empty"
|
||||
],
|
||||
"confidence": 0.95,
|
||||
"explanation": "This workflow can be adapted to fetch and summarise any Wikipedia page or similar content source.",
|
||||
"source_repo": "https://github.com/temiroff/Blacknode.git",
|
||||
"score": 1.0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tests: research-pipeline
|
||||
|
||||
## Test Checklist
|
||||
|
||||
- [ ] Workflow has at least 3 steps
|
||||
- [ ] All inputs are defined
|
||||
- [ ] All outputs are defined
|
||||
- [ ] Failure modes are documented
|
||||
- [ ] Skill can be loaded without errors
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: unifai-workflow-execution
|
||||
version: 1.0.0
|
||||
description: Execute a multi-agent AI workflow defined in YAML or through the UI's
|
||||
drag-and-drop editor.
|
||||
inputs:
|
||||
- name: blueprint_path
|
||||
description: Path to the blueprint file (YAML) defining the multi-agent workflow.
|
||||
- name: execution_mode
|
||||
description: 'Execution mode: ''local'' or ''distributed''.'
|
||||
steps:
|
||||
- step_name: Load Blueprint
|
||||
description: Parse and validate the blueprint file to ensure it conforms to expected
|
||||
structure.
|
||||
- step_name: Initialize Execution Engine
|
||||
description: Set up the execution engine based on the selected mode ('local' or
|
||||
'distributed').
|
||||
- step_name: Execute Workflow
|
||||
description: Run the multi-agent workflow, streaming node-by-node output as NDJSON
|
||||
over HTTP.
|
||||
- step_name: Stream Results
|
||||
description: Render and stream results in real time to clients subscribing to the
|
||||
event stream.
|
||||
outputs:
|
||||
- name: execution_results
|
||||
description: The output of the executed workflow, streamed as NDJSON over HTTP.
|
||||
tags: []
|
||||
metadata:
|
||||
source_repo: https://github.com/redhat-community-ai-tools/UnifAI.git
|
||||
extracted_at: ''
|
||||
confidence: 0.9
|
||||
---
|
||||
|
||||
# unifai-workflow-execution
|
||||
|
||||
Execute a multi-agent AI workflow defined in YAML or through the UI's drag-and-drop editor.
|
||||
|
||||
## Steps
|
||||
|
||||
1. {'step_name': 'Load Blueprint', 'description': 'Parse and validate the blueprint file to ensure it conforms to expected structure.'}
|
||||
2. {'step_name': 'Initialize Execution Engine', 'description': "Set up the execution engine based on the selected mode ('local' or 'distributed')."}
|
||||
3. {'step_name': 'Execute Workflow', 'description': 'Run the multi-agent workflow, streaming node-by-node output as NDJSON over HTTP.'}
|
||||
4. {'step_name': 'Stream Results', 'description': 'Render and stream results in real time to clients subscribing to the event stream.'}
|
||||
|
||||
## Inputs
|
||||
|
||||
- {'name': 'blueprint_path', 'description': 'Path to the blueprint file (YAML) defining the multi-agent workflow.'}
|
||||
- {'name': 'execution_mode', 'description': "Execution mode: 'local' or 'distributed'."}
|
||||
|
||||
## Outputs
|
||||
|
||||
- {'name': 'execution_results', 'description': 'The output of the executed workflow, streamed as NDJSON over HTTP.'}
|
||||
|
||||
## Failure Modes
|
||||
|
||||
- {'mode_name': 'Invalid Blueprint', 'description': 'Blueprint file is not valid YAML or does not conform to expected structure.'}
|
||||
- {'mode_name': 'Execution Engine Initialization Failure', 'description': 'Failed to initialize the execution engine due to configuration issues or missing dependencies.'}
|
||||
|
||||
## Source
|
||||
|
||||
Extracted from: [https://github.com/redhat-community-ai-tools/UnifAI.git](https://github.com/redhat-community-ai-tools/UnifAI.git)
|
||||
Confidence: 0.9
|
||||
@@ -0,0 +1,6 @@
|
||||
# Commands: unifai-workflow-execution
|
||||
|
||||
## Available Commands
|
||||
|
||||
- `/skill unifai-workflow-execution` — Load this skill
|
||||
- `/run unifai-workflow-execution` — Execute workflow
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples: unifai-workflow-execution
|
||||
|
||||
## Usage Example
|
||||
|
||||
```python
|
||||
# How to use this skill
|
||||
# Inputs: {'name': 'blueprint_path', 'description': 'Path to the blueprint file (YAML) defining the multi-agent workflow.'}, {'name': 'execution_mode', 'description': "Execution mode: 'local' or 'distributed'."}
|
||||
# Process: {'step_name': 'Load Blueprint', 'description': 'Parse and validate the blueprint file to ensure it conforms to expected structure.'} → {'step_name': 'Initialize Execution Engine', 'description': "Set up the execution engine based on the selected mode ('local' or 'distributed')."} → {'step_name': 'Execute Workflow', 'description': 'Run the multi-agent workflow, streaming node-by-node output as NDJSON over HTTP.'}
|
||||
# Outputs: {'name': 'execution_results', 'description': 'The output of the executed workflow, streamed as NDJSON over HTTP.'}
|
||||
```
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "unifai-workflow-execution",
|
||||
"version": "1.0.0",
|
||||
"goal": "Execute a multi-agent AI workflow defined in YAML or through the UI's drag-and-drop editor.",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "blueprint_path",
|
||||
"description": "Path to the blueprint file (YAML) defining the multi-agent workflow."
|
||||
},
|
||||
{
|
||||
"name": "execution_mode",
|
||||
"description": "Execution mode: 'local' or 'distributed'."
|
||||
}
|
||||
],
|
||||
"steps": [
|
||||
{
|
||||
"step_name": "Load Blueprint",
|
||||
"description": "Parse and validate the blueprint file to ensure it conforms to expected structure."
|
||||
},
|
||||
{
|
||||
"step_name": "Initialize Execution Engine",
|
||||
"description": "Set up the execution engine based on the selected mode ('local' or 'distributed')."
|
||||
},
|
||||
{
|
||||
"step_name": "Execute Workflow",
|
||||
"description": "Run the multi-agent workflow, streaming node-by-node output as NDJSON over HTTP."
|
||||
},
|
||||
{
|
||||
"step_name": "Stream Results",
|
||||
"description": "Render and stream results in real time to clients subscribing to the event stream."
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "execution_results",
|
||||
"description": "The output of the executed workflow, streamed as NDJSON over HTTP."
|
||||
}
|
||||
],
|
||||
"failure_modes": [
|
||||
{
|
||||
"mode_name": "Invalid Blueprint",
|
||||
"description": "Blueprint file is not valid YAML or does not conform to expected structure."
|
||||
},
|
||||
{
|
||||
"mode_name": "Execution Engine Initialization Failure",
|
||||
"description": "Failed to initialize the execution engine due to configuration issues or missing dependencies."
|
||||
}
|
||||
],
|
||||
"confidence": 0.9,
|
||||
"explanation": "This workflow is designed to execute multi-agent AI workflows defined in YAML blueprints or through the UI's drag-and-drop editor, providing real-time streaming of results.",
|
||||
"source_repo": "https://github.com/redhat-community-ai-tools/UnifAI.git",
|
||||
"score": 1.0
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Tests: unifai-workflow-execution
|
||||
|
||||
## Test Checklist
|
||||
|
||||
- [ ] Workflow has at least 3 steps
|
||||
- [ ] All inputs are defined
|
||||
- [ ] All outputs are defined
|
||||
- [ ] Failure modes are documented
|
||||
- [ ] Skill can be loaded without errors
|
||||
Reference in New Issue
Block a user