File size: 1,537 Bytes
68025ee
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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()