"""Layer 3:LangGraphMind——用 LangGraph 认知图驱动的 agent 大脑。

继承 CognitiveMind,只覆写"推断"这一步(_run_action/_run_dialogue):
- prompt 组装、目标自举、反思、关系/信任、言行一致等全部沿用父类(同一份经久打磨的提示词);
- 决策推断改走 LangGraph 图 + ChatLiteLLM 结构化输出(原生函数调用约束,免脆弱正则);
- 结构化失败 → 回落父类的"litellm 直答 + 文本解析",世界绝不因模型抽风停摆。
因为是 CognitiveMind 的子类,引擎里所有 `isinstance(mind, CognitiveMind)` 分支照常命中,引擎零改动。
"""

from __future__ import annotations

import time

from genesis.llm.lc_adapter import build_chat_model
from genesis.obs.logging_setup import get_logger
from genesis.runtime.cognition import (
    _LATENCY, SLOW_CALL_S, CognitiveMind, Decision, ThinkInput,
)
from genesis.runtime.graph_brain import build_cognition_graph

logger = get_logger("runtime.langgraph_mind")


class LangGraphMind(CognitiveMind):
    """与引擎契约不变(decide(snap)→Decision),内部用 LangGraph 图做结构化决策。"""

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)
        self._model_cache: dict = {}                 # (max_tokens, thinking) → ChatLiteLLM,免每拍重建
        self._graph = build_cognition_graph(self._make_model)

    def _make_model(self, *, max_tokens: int = 520, thinking: bool = False):
        key = (max_tokens, thinking)
        if key not in self._model_cache:
            self._model_cache[key] = build_chat_model(
                temperature=0.85, max_tokens=max_tokens, thinking=thinking, request_timeout=25.0)
        return self._model_cache[key]

    # —— 推断走图;失败回落父类文本解析 ——
    def _run_action(self, system: str, user: str, deep: bool, max_tokens: int,
                    snap: ThinkInput) -> Decision:
        dec = self._invoke_graph("action", system, user, max_tokens, tag="L2", key="decision",
                                 extra={"intent": self.intent})
        if dec is None:                              # 结构化失败 → 经久考验的文本兜底
            return super()._run_action(system, user, deep, max_tokens, snap)
        return dec

    def _run_dialogue(self, system: str, user: str, snap: ThinkInput) -> Decision:
        dec = self._invoke_graph("dialogue", system, user, 220, tag="L1", key="decision",
                                 extra={"dialogue_with": snap.dialogue_with, "intent": self.intent})
        if dec is None:
            return super()._run_dialogue(system, user, snap)
        return dec

    def _run_bootstrap(self, system: str, user: str) -> list[str]:
        out = self._invoke_graph("bootstrap", system, user, 320, tag="L2", key="result", extra={})
        if out is None:
            return super()._run_bootstrap(system, user)
        return [str(g) for g in out.goals][:4]

    def _run_reflect(self, system: str, user: str) -> tuple[list[str], list[dict]]:
        out = self._invoke_graph("reflect", system, user, 320, tag="reflect", key="result", extra={})
        if out is None:
            return super()._run_reflect(system, user)
        insights = [str(i) for i in out.insights][:3]
        relations = [r.model_dump() for r in out.relations if r.name]
        return insights, relations

    def _invoke_graph(self, mode: str, system: str, user: str, max_tokens: int,
                      *, tag: str, key: str, extra: dict):
        """调用认知图取 state[key];复用 cognition 的耗时埋点(/api/latency 继续可用)。"""
        state = {"mode": mode, "system": system, "user": user,
                 "max_tokens": max_tokens, "deep": False, **extra}
        t0 = time.monotonic()
        out = None
        try:
            out = self._graph.invoke(state).get(key)
        except Exception as e:                       # 图层兜底以外的异常(网络/构图)→ 交回上层文本兜底
            logger.warning("[%s] LangGraph 调用异常:%s", self.aid, str(e)[:160])
        dt = time.monotonic() - t0
        out_chars = len(out.raw) if isinstance(out, Decision) else 0
        _LATENCY.append((dt, self.aid, tag, False, len(system) + len(user), out_chars))
        if dt > SLOW_CALL_S:
            logger.warning("[慢LG %.1fs] %s tag=%s in=%d out=%d", dt, self.aid, tag,
                           len(system) + len(user), out_chars)
        return out
