feat(aggregation): Implement sub-agent summarization pass

This commit is contained in:
2026-05-04 04:52:40 -04:00
parent d85514eb4f
commit 76c4ce3677
4 changed files with 147 additions and 4 deletions
+49
View File
@@ -2450,3 +2450,52 @@ def get_history_bleed_stats(md_content: Optional[str] = None) -> dict[str, Any]:
"percentage": 0, "percentage": 0,
}) })
def run_subagent_summarization(file_path: str, content: str, is_code: bool, outline: str) -> str:
"""Performs a stateless summarization request using a sub-agent prompt."""
prompt_tmpl = mma_prompts.TIER4_SUMMARIZE_CODE_PROMPT if is_code else mma_prompts.TIER4_SUMMARIZE_TEXT_PROMPT
prompt = prompt_tmpl.format(file_path=file_path, outline=outline, content=content)
if _provider == "gemini":
_ensure_gemini_client()
if _gemini_client:
resp = _gemini_client.models.generate_content(
model=_model,
contents=prompt,
config=types.GenerateContentConfig(
temperature=0.0,
max_output_tokens=1024,
)
)
return resp.text or ""
elif _provider == "anthropic":
_ensure_anthropic_client()
if _anthropic_client:
resp = _anthropic_client.messages.create(
model=_model,
max_tokens=1024,
messages=[{"role": "user", "content": prompt}]
)
return "".join([b.text for b in resp.content if hasattr(b, "text") and b.text])
elif _provider == "deepseek":
creds = _load_credentials()
api_key = creds.get("deepseek", {}).get("api_key")
if not api_key: return "ERROR: DeepSeek API key missing"
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {
"model": _model,
"messages": [{"role": "user", "content": prompt}],
"temperature": 0.0,
}
try:
r = requests.post("https://api.deepseek.com/chat/completions", headers=headers, json=payload, timeout=60)
r.raise_for_status()
return r.json()["choices"][0]["message"]["content"]
except Exception as e:
return f"ERROR: DeepSeek summarization failed: {e}"
elif _provider == "gemini_cli":
# Using the adapter for a one-off call
from src.gemini_cli_adapter import GeminiCliAdapter
adapter = GeminiCliAdapter(binary_path="gemini")
resp_data = adapter.send(prompt, model=_model)
return resp_data.get("text", "")
return "ERROR: Unsupported provider for sub-agent summarization"
+29
View File
@@ -179,3 +179,32 @@ RULES:
Analyze this error and generate the patch: Analyze this error and generate the patch:
""" """
TIER4_SUMMARIZE_CODE_PROMPT: str = """You are a Tier 4 QA Agent specializing in code summarization.
Your goal is to provide a concise, high-signal summary of the provided code file.
Focus on the primary responsibility of the module and its key architectural components.
INPUT:
- File Path: {file_path}
- Heuristic Outline: {outline}
- Raw Content:
{content}
OUTPUT REQUIREMENT:
Provide a 1-2 sentence high-level summary followed by a brief bulleted list of key features or responsibilities.
Keep it extremely concise. Do NOT repeat the outline.
"""
TIER4_SUMMARIZE_TEXT_PROMPT: str = """You are a Tier 4 QA Agent specializing in document summarization.
Your goal is to provide a concise, high-signal summary of the provided text/markdown file.
INPUT:
- File Path: {file_path}
- Heuristic Outline: {outline}
- Raw Content:
{content}
OUTPUT REQUIREMENT:
Provide a 1-2 sentence high-level summary of the document's purpose and key takeaways.
Keep it extremely concise.
"""
+19 -1
View File
@@ -164,7 +164,25 @@ def summarise_file(path: Path, content: str) -> str:
suffix = path.suffix.lower() if hasattr(path, "suffix") else "" suffix = path.suffix.lower() if hasattr(path, "suffix") else ""
fn = _SUMMARISERS.get(suffix, _summarise_generic) fn = _SUMMARISERS.get(suffix, _summarise_generic)
try: try:
summary = fn(path, content) heuristic_outline = fn(path, content)
# Smart AI Summarization
is_code = suffix in [".py", ".ps1", ".js", ".ts", ".cpp", ".c", ".h", ".cs", ".go", ".rs", ".lua"]
try:
from src import ai_client
smart_summary = ai_client.run_subagent_summarization(
file_path=str(path),
content=content[:10000], # Cap content to 10k chars for summarization
is_code=is_code,
outline=heuristic_outline
)
if smart_summary and not smart_summary.startswith("ERROR:"):
summary = f"{smart_summary}\n\n**Outline:**\n{heuristic_outline}"
else:
summary = heuristic_outline
except Exception:
summary = heuristic_outline # Fallback
_summary_cache.set_summary(str(path), content_hash, summary) _summary_cache.set_summary(str(path), content_hash, summary)
return summary return summary
except Exception as e: except Exception as e:
+47
View File
@@ -0,0 +1,47 @@
import pytest
from unittest.mock import MagicMock, patch
from src import ai_client
from src import summarize
from pathlib import Path
def test_run_subagent_summarization_gemini():
with patch("src.ai_client._provider", "gemini"), \
patch("src.ai_client._gemini_client") as mock_client, \
patch("src.ai_client._ensure_gemini_client"):
mock_resp = MagicMock()
mock_resp.text = "Smart Summary"
mock_client.models.generate_content.return_value = mock_resp
res = ai_client.run_subagent_summarization("test.py", "print('hello')", True, "Outline")
assert res == "Smart Summary"
mock_client.models.generate_content.assert_called_once()
def test_run_subagent_summarization_anthropic():
with patch("src.ai_client._provider", "anthropic"), \
patch("src.ai_client._anthropic_client") as mock_client, \
patch("src.ai_client._ensure_anthropic_client"):
mock_resp = MagicMock()
mock_block = MagicMock()
mock_block.text = "Anthropic Summary"
mock_resp.content = [mock_block]
mock_client.messages.create.return_value = mock_resp
res = ai_client.run_subagent_summarization("test.py", "print('hello')", True, "Outline")
assert res == "Anthropic Summary"
mock_client.messages.create.assert_called_once()
def test_summarise_file_integration():
with patch("src.ai_client.run_subagent_summarization") as mock_run:
mock_run.return_value = "Smart AI Summary"
# Ensure we don't hit the real cache for this test
with patch("src.summarize._summary_cache") as mock_cache:
mock_cache.get_summary.return_value = None
p = Path("test_file.py")
content = "def hello():\n pass"
summary = summarize.summarise_file(p, content)
assert "Smart AI Summary" in summary
assert "**Outline:**" in summary
assert "functions: hello" in summary