Spaces:
Running
Running
| import json | |
| import re | |
| import logging | |
| from llm.openrouter import OpenRouterClient | |
| logger = logging.getLogger(__name__) | |
| FALLBACK_DECISION = {"action": "HOLD", "size": 0.0, "confidence": 0.0, "reason": "Parse error — defaulting to HOLD"} | |
| class Agent: | |
| def __init__(self, role: str, system_prompt: str, llm_client: OpenRouterClient): | |
| self.role = role | |
| self.system_prompt = system_prompt | |
| self.llm = llm_client | |
| def run(self, context: dict) -> dict: | |
| prompt = self.build_prompt(context) | |
| raw = self.llm.call(self.system_prompt, prompt) | |
| return self.parse(raw) | |
| def build_prompt(self, context: dict) -> str: | |
| raise NotImplementedError | |
| def parse(self, raw: str) -> dict: | |
| """Robust JSON parsing: strip markdown fences, regex fallback.""" | |
| if not raw: | |
| return FALLBACK_DECISION.copy() | |
| # Strip markdown code fences | |
| cleaned = re.sub(r"```(?:json)?\s*", "", raw).strip() | |
| cleaned = re.sub(r"```\s*$", "", cleaned).strip() | |
| # Try direct parse | |
| try: | |
| return json.loads(cleaned) | |
| except json.JSONDecodeError: | |
| pass | |
| # Try to extract first JSON object | |
| match = re.search(r"\{[^{}]*\}", cleaned, re.DOTALL) | |
| if match: | |
| try: | |
| return json.loads(match.group()) | |
| except json.JSONDecodeError: | |
| pass | |
| logger.warning(f"[{self.role}] Failed to parse response: {raw[:200]}") | |
| return FALLBACK_DECISION.copy() | |