| import re |
| import logging |
| from typing import List, Dict, Any |
| from services.supabase_service import supabase |
|
|
| logger = logging.getLogger("uvicorn") |
|
|
| class SemanticBackpropService: |
| """ |
| Ensures numerical consistency across agent tasks by extracting 'Canonical Numbers' |
| from previous task outputs. |
| """ |
|
|
| @staticmethod |
| async def get_project_context(project_id: str, current_task_id: str) -> str: |
| """ |
| Fetches and extracts canonical figures from all completed sibling tasks. |
| """ |
| try: |
| resp = supabase.table("tasks") \ |
| .select("title, output_data") \ |
| .eq("project_id", project_id) \ |
| .eq("status", "done") \ |
| .neq("id", current_task_id) \ |
| .execute() |
| |
| if not resp.data: |
| return "" |
|
|
| canonical_blocks = [] |
| topic_blocks = [] |
|
|
| for task in resp.data: |
| output = task.get("output_data") or {} |
| |
| result_text = "" |
| if isinstance(output, dict): |
| result_text = output.get("result", "") or output.get("raw_output", "") |
| elif isinstance(output, str): |
| result_text = output |
|
|
| if not result_text: |
| continue |
|
|
| |
| lines = result_text.splitlines() |
| financial_lines = [] |
| |
| |
| keywords = [ |
| "$", "%", "USD", "MRR", "ARR", "ROI", "cost", "budget", |
| "revenue", "price", "fee", "estimate", "total", "quota" |
| ] |
|
|
| for line in lines: |
| if any(k.lower() in line.lower() for k in keywords): |
| if len(line.strip()) > 5: |
| financial_lines.append(line.strip()) |
|
|
| if financial_lines: |
| |
| seen = set() |
| unique_fin = [] |
| for fl in financial_lines: |
| key = fl[:50] |
| if key not in seen: |
| seen.add(key) |
| unique_fin.append(fl) |
|
|
| canonical_blocks.append( |
| f"Source Task: **{task['title']}**\n" + |
| "\n".join(f" • {fl}" for fl in unique_fin[:8]) |
| ) |
|
|
| |
| topic_blocks.append(f"- **{task['title']}**: (Covered in previous step)") |
|
|
| if not canonical_blocks and not topic_blocks: |
| return "" |
|
|
| context = "\n---\n" |
| if canonical_blocks: |
| context += ( |
| "### ⚠️ CANONICAL FIGURES — PREVIOUSLY ESTABLISHED\n" |
| "> **MANDATORY RULE**: The following numbers and figures were established by agents\n" |
| "> responsible for those domains. You MUST use these exact values if you reference them.\n" |
| "> DO NOT re-calculate or propose alternative values for these specific items.\n\n" |
| ) |
| context += "\n\n".join(canonical_blocks) + "\n\n" |
| |
| if topic_blocks: |
| context += ( |
| "### 📋 PREVIOUSLY COVERED TOPICS\n" |
| "> Do not repeat the analysis of these topics. Focus only on your specific task.\n" |
| ) |
| context += "\n".join(topic_blocks) + "\n" |
|
|
| return context |
|
|
| except Exception as e: |
| logger.error(f"Semantic Backprop failed: {e}") |
| return "" |
|
|
| semantic_backprop = SemanticBackpropService() |
|
|