Angshuman28's picture
Upload folder using huggingface_hub
5c4e77e verified
Raw
History Blame Contribute Delete
7 kB
"""Brain class - composes Perception + Lens + 3 Subagents + Brain Executive.
Per cortex/CLAUDE.md: each brain has a deterministic Python Perception
+ Lens, three LLM subagents (router-callable), and a deterministic
Python Brain Executive. The Brain class wires these together.
Multi-model deployment: each Brain holds a SINGLE LLMClient instance
passed at construction. Different brains can use different models by
constructing each Brain with a different LLMClient (e.g., Qwen for epi,
Llama for logistics). NO module-level state, NO shared singletons.
"""
from __future__ import annotations
from typing import List, Literal
from cortex.lenses import lens_for
from cortex.schemas import (
BeliefState,
BrainLensedObservation,
BrainRecommendation,
CandidatePlan,
CriticReport,
PerceptionReport,
SubagentInput,
)
from cortex.subagents import (
CriticSubagent,
PlannerSubagent,
WorldModelerSubagent,
perception_for,
)
from cortex.subagents._base import _LLMClientLike
from CrisisWorldCortex.models import CrisisworldcortexObservation
from ._executive import aggregate_brain_outputs
_BrainId = Literal["epidemiology", "logistics", "governance"]
class Brain:
"""Per-brain pipeline holder.
Each Brain instance owns its own LLMClient. The orchestration layer
(Session 12 Council, Workstream B trainers) instantiates one Brain
per brain id, optionally with different LLMClients pointing to
different models. The Brain class itself has NO module-level state
and NO forced singleton.
Args:
brain_id: One of "epidemiology", "logistics", "governance".
llm_client: This brain's LLM client. Subagents are constructed
with the SAME client so token billing aggregates correctly.
wm: WorldModeler subagent.
planner: Planner subagent.
critic: Critic subagent.
"""
def __init__(
self,
brain_id: _BrainId,
llm_client: _LLMClientLike,
wm: WorldModelerSubagent,
planner: PlannerSubagent,
critic: CriticSubagent,
) -> None:
self.brain_id = brain_id
self.llm_client = llm_client
self.wm = wm
self.planner = planner
self.critic = critic
# ------------------------------------------------------------------
# Deterministic Python pieces (no LLM)
# ------------------------------------------------------------------
def compute_perception(self, obs: CrisisworldcortexObservation) -> PerceptionReport:
"""Run this brain's Perception. Pure Python; no LLM."""
return perception_for(self.brain_id, obs)
def compute_lens(
self, obs: CrisisworldcortexObservation, last_reward: float
) -> BrainLensedObservation:
"""Run this brain's Lens. Pure Python; no LLM."""
return lens_for(self.brain_id, obs, last_reward)
def aggregate(
self,
perception: PerceptionReport,
beliefs: List[BeliefState],
plans: List[CandidatePlan],
critics: List[CriticReport],
tokens_used: int = 0,
) -> BrainRecommendation:
"""Run this brain's Brain Executive. Pure Python; no LLM."""
return aggregate_brain_outputs(
brain_id=self.brain_id,
perception=perception,
beliefs=beliefs,
plans=plans,
critics=critics,
tokens_used=tokens_used,
)
# ------------------------------------------------------------------
# High-level convenience: round-1 single tick
# ------------------------------------------------------------------
def run_tick(
self,
obs: CrisisworldcortexObservation,
last_reward: float,
tick: int,
round_: int = 1,
) -> BrainRecommendation:
"""Round-1 single-tick pipeline (Session 11 smoke).
Round 2 is orchestrated by the Council Executive (Session 12)
via the fine-grained methods (compute_perception, compute_lens,
wm.run / planner.run / critic.run, aggregate). Calling this
convenience method with ``round_!=1`` raises NotImplementedError
to prevent accidental misuse before the Council exists.
"""
if round_ != 1:
raise NotImplementedError(
f"Round {round_} orchestration is the Council Executive's "
f"responsibility (Session 12). Use Brain.compute_perception/"
f"compute_lens + WorldModelerSubagent.run/PlannerSubagent.run/"
f"CriticSubagent.run + Brain.aggregate directly."
)
perception = self.compute_perception(obs)
# Lens is computed for completeness; Session 11 doesn't yet plumb
# it into SubagentInput (M-FR-4 step indices fixed). Session 12
# Council will extend the SubagentInput contract to carry lens
# output if subagents need it.
_ = self.compute_lens(obs, last_reward)
# WorldModeler (step_idx=0)
wm_input = SubagentInput(
brain=self.brain_id,
role="world_modeler",
tick=tick,
round=round_,
perception=perception,
prior_belief=None,
prior_plans=[],
target_plan_id=None,
last_reward=last_reward,
recent_action_log_excerpt=list(obs.recent_action_log),
)
belief = self.wm.run(wm_input, step_idx=0)
# Planner (step_idx=1)
planner_input = SubagentInput(
brain=self.brain_id,
role="planner",
tick=tick,
round=round_,
perception=perception,
prior_belief=belief,
prior_plans=[],
target_plan_id=None,
last_reward=last_reward,
recent_action_log_excerpt=list(obs.recent_action_log),
)
plan = self.planner.run(planner_input, step_idx=1)
# Critic (step_idx=2)
critic_input = SubagentInput(
brain=self.brain_id,
role="critic",
tick=tick,
round=round_,
perception=perception,
prior_belief=belief,
prior_plans=[plan],
target_plan_id="plan-0",
last_reward=last_reward,
recent_action_log_excerpt=list(obs.recent_action_log),
)
critic = self.critic.run(critic_input, step_idx=2)
# Tally tokens billed to this brain's caller_ids.
caller_id_base = f"cortex:{self.brain_id}"
tokens_used = sum(
self.llm_client.tokens_used_for(f"{caller_id_base}:{role}:t{tick}:r{round_}:s{idx}")
for role, idx in (
("world_modeler", 0),
("planner", 1),
("critic", 2),
)
)
return self.aggregate(
perception=perception,
beliefs=[belief],
plans=[plan],
critics=[critic],
tokens_used=tokens_used,
)