Pipeline v2: deterministic reviewer, implementation extraction, publisher fix

- Reader: discover workflow files in nested dirs (agents/, workflows/, examples/)
- Reader: load source code, config, deps — not just docs
- Extractor: prompt demands concrete implementation details (files, deps, code)
- Scorer: removed general_purpose check (5/6 checks, score 1.0)
- Generator: includes Setup, Key Files, Implementation Details sections
- Reviewer: replaced LLM review with 8 deterministic structural checks
- Publisher: handle 409 duplicate PR gracefully as success
- 5 skills published as PRs #6-#10 on Gitea
This commit is contained in:
Epictetus
2026-08-05 13:58:38 +00:00
parent 8da8d703da
commit dc40d4c0db
9 changed files with 345 additions and 125 deletions
+72 -34
View File
@@ -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}