# router.py from fastapi import APIRouter, HTTPException from pydantic import BaseModel from reasoning.reasoning import reason_check from reasoning.reflection import reflect_notes from reasoning.refine import refine_answer from persona.speak import speak from persona.identity import load_identity from ingest.intake_client import IntakeClient from context import collect_context, update_last_assistant_message # ----------------------------- # Router (NOT FastAPI app) # ----------------------------- cortex_router = APIRouter() # Initialize Intake client once intake_client = IntakeClient() # ----------------------------- # Pydantic models # ----------------------------- class ReasonRequest(BaseModel): session_id: str user_prompt: str temperature: float | None = None # ----------------------------- # /reason endpoint # ----------------------------- @cortex_router.post("/reason") async def run_reason(req: ReasonRequest): # 0. Collect unified context from all sources context_state = await collect_context(req.session_id, req.user_prompt) # 0.5. Load identity block identity_block = load_identity(req.session_id) # 1. Extract Intake summary for reflection # Use L20 (Session Overview) as primary summary for reflection intake_summary = "(no context available)" if context_state.get("intake"): l20_summary = context_state["intake"].get("L20") if l20_summary and isinstance(l20_summary, dict): intake_summary = l20_summary.get("summary", "(no context available)") elif isinstance(l20_summary, str): intake_summary = l20_summary # 2. Reflection try: reflection = await reflect_notes(intake_summary, identity_block=identity_block) reflection_notes = reflection.get("notes", []) except Exception: reflection_notes = [] # 3. First-pass reasoning draft draft = await reason_check( req.user_prompt, identity_block=identity_block, rag_block=context_state.get("rag", []), reflection_notes=reflection_notes, context=context_state ) # 4. Refinement result = await refine_answer( draft_output=draft, reflection_notes=reflection_notes, identity_block=identity_block, rag_block=context_state.get("rag", []), ) final_neutral = result["final_output"] # 5. Persona layer persona_answer = await speak(final_neutral) # 6. Update session state with assistant's response update_last_assistant_message(req.session_id, persona_answer) # 7. Return full bundle return { "draft": draft, "neutral": final_neutral, "persona": persona_answer, "reflection": reflection_notes, "session_id": req.session_id, "context_summary": { "rag_results": len(context_state.get("rag", [])), "minutes_since_last": context_state.get("minutes_since_last_msg"), "message_count": context_state.get("message_count"), "mode": context_state.get("mode"), } } # ----------------------------- # Intake ingest passthrough # ----------------------------- @cortex_router.post("/ingest") async def ingest_stub(): return {"status": "ok"}