# api_server.py import requests from fastapi import FastAPI, Request from pydantic import BaseModel from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.responses import FileResponse from fastapi import BackgroundTasks from core.conversation_manager import ConversationState from core.conversation_engine import handle_turn from core.agent_orchestrator import run_agents from core.input_classifier import is_meaningful_input from core.memory_store import save_state, load_state # session persistence handling from core.conversation_manager import from_dict # to have the retrieved conversation in DICT/JSON converted to ConversationState Object from core.command_parser import parse_command from agents.epistemic_agent import epistemic_response from core.epistemic.profile_manager import update_global_profile from core.epistemic.epistemic_cross import cross_session_analysis # CREATE APP FIRST app = FastAPI() # THEN MOUNT STATIC app.mount("/static", StaticFiles(directory="web"), name="static") # sessions = {} # ---- CORS FIX ---- app.add_middleware( CORSMiddleware, allow_origins=["*"], # allow any origin allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) class ChatRequest(BaseModel): session_id: str message: str @app.get("/", response_class=HTMLResponse) def serve_ui(): return FileResponse("web/index.html") #@app.get("/") #def root(): # return {"status": "CGJI01 API running"} #@app.post("/analyze") #def analyze(req: Request): # results = run_agents(req.text) # return results @app.post("/analyze") def analyze(req: Request): if not is_meaningful_input(req.text): return { "status": "ok", "message": "Input does not appear to require deep psychological analysis.", "input": req.text } results = run_agents(req.text) return {"result": results} """ sessions = {} @app.post("/chat") async def chat(req: Request): data = await req.json() session_id = data["session_id"] message = data["message"] if session_id not in sessions: sessions[session_id] = ConversationState() state = sessions[session_id] output = handle_turn(state, message) if output["type"] == "end": del sessions[session_id] # Clean up session when finished return output """ # HANDLE AGENT SWITCH - FUNCTION def handle_agent_switch(state, agent_name): output = run_specific_agent(agent_name, state.initial_query) # Ensure output is a dict if not isinstance(output, dict): output = { "type": "error", "content": "Agent returned invalid response" } print("๐Ÿง  AGENT SWITCH OUTPUT:", output) # Inject agent identity output["agent"] = agent_name return output #def handle_agent_switch(state, agent_name): # ๐Ÿ”ฅ TEMP: route through existing system # print(f"โš ๏ธ Switching to {agent_name}, but using handle_turn for now") # return handle_turn(state, state.initial_query) def run_epistemic_witness(state, session_id): try: # Extract full histories client_history = [ msg["text"] for msg in state.agent_histories.get("jung", []) if msg["role"] == "client" ] jung_history = [ msg["text"] for msg in state.agent_histories.get("jung", []) if msg["role"] == "agent" ] # Run epistemic analysis analysis = epistemic_response(client_history, jung_history) # Store inside state if not hasattr(state, "witness_logs"): state.witness_logs = {} state.witness_logs["epistemic"] = analysis print("๐Ÿง  EPISTEMIC UPDATED") except Exception as e: print("โŒ Epistemic error:", e) sessions = {} @app.post("/chat") def chat(req: ChatRequest, background_tasks: BackgroundTasks): print("๐Ÿ”ฅ CHAT ENDPOINT HIT") session_id = req.session_id message = req.message print("\n๐Ÿงพ RAW REQUEST BODY:", req) #if session_id not in sessions: # sessions[session_id] = ConversationState() if session_id not in sessions: saved_data = load_state(session_id) # when user sends a message after loading, backend restores full conversation automatically if saved_data: print("โ™ป๏ธ Restored session from disk") #sessions[session_id] = loaded print("๐Ÿ” SAVED DATA:", saved_data) try: state = from_dict(saved_data) except Exception as e: print("โŒ RESTORE FAILED:", e) state = ConversationState() else: #sessions[session_id] = ConversationState() state = ConversationState() sessions[session_id] = state state = sessions[session_id] # โœ… Ensure flag exists if not hasattr(state, "conversation_started"): state.conversation_started = False if not state.conversation_started: state.conversation_started = True # โœ… Store initial query if not state.initial_query and not message.startswith("__agent__:"): #state.initial_query = message if state.history: state.initial_query = next( (m["content"] for m in data.get("history", []) if m["role"] == "client"), "" ) # improve initial query restore else: state.initial_query = message print("\n๐Ÿ“ฅ USER MESSAGE:", message) cmd, cleaned_message = parse_command(message) if cmd: if cmd["command"] == "general": state.mode = "general" state.role = "client" elif cmd["command"] == "analysis": state.mode = "analysis" if len(cmd["args"]) > 0: if cmd["args"][0] == "analyst": state.role = "analyst" elif cmd["args"][0] == "client": state.role = "client" message = cleaned_message # ----------------------------- # AGENT SWITCH # ----------------------------- if message.startswith("__agent__:"): selected_agent = message.split(":")[1] print("๐Ÿงญ Switching to agent:", selected_agent) state.current_agent = selected_agent output = handle_agent_switch(state, selected_agent) print("\n๐Ÿ“ค RESPONSE TO UI:", output) return output # ----------------------------- # NORMAL FLOW (JUNG etc.) # ----------------------------- output = handle_turn(state, message) # ---- (EXPERIMENTAL) LOW-LEVEL CROSS-SESSION EPISTEMIC INTELLIGENCE/ANALYSIS ---- # ------------------------------- # UPDATE GLOBAL PROFILE # ------------------------------- state.session_id = session_id # ensure attached session_record = update_global_profile(state) # ------------------------------- # EPISTEMIC CROSS-SESSION # ------------------------------- epistemic_text = cross_session_analysis(session_record) # attach to response output["witness_updates"] = { "epistemic": epistemic_text } # ---- (EXPERIMENTAL) LOW-LEVEL CROSS-SESSION EPISTEMIC INTELLIGENCE/ANALYSIS - End ---- # ----------------------------------------- # SAVE STATE AFTER EVERY TURN (PERSISTENCE) # ----------------------------------------- save_state(session_id, state) # ----------------------------- # ๐Ÿง  EPISTEMIC (BACKGROUND) # ----------------------------- jung_history_len = len(state.agent_histories.get("jung", [])) # โœ… Throttle โ†’ every 2 turns if jung_history_len % 2 == 0: background_tasks.add_task(run_epistemic_witness, state, session_id) # ----------------------------- # ๐Ÿ“ฆ ATTACH EPISTEMIC TO RESPONSE # ----------------------------- if hasattr(state, "witness_logs"): output["witness_updates"] = state.witness_logs print("\n๐Ÿ“ค RESPONSE TO UI:", output) # ----------------------------- # CLEANUP # ----------------------------- if output["type"] == "end": del sessions[session_id] return output # ---- GET SESSION DATA, PAST CONVERSATION ---- @app.get("/session/{session_id}") def get_session(session_id: str): data = load_state(session_id) if not data: return {"error": "Session not found"} return data @app.get("/health") def health_check(): #import requests ollama_status = False try: requests.get("http://127.0.0.1:11434") ollama_status = True except: pass return { "api": "running", "ollama": "connected" if ollama_status else "not running", "agents": "ready" }