Download src/graph_rag_service/api/simulation.py from anky2002/graph-rag: direct link, hf CLI and curl.
- Browser
- Download file 4.96 kB
-
https://huggingface.co/spaces/anky2002/graph-rag/resolve/main/src/graph_rag_service/api/simulation.py
- Command line
-
hf download hf://spaces/anky2002/graph-rag/src/graph_rag_service/api/simulation.py
-
curl -L -o simulation.py https://huggingface.co/spaces/anky2002/graph-rag/resolve/main/src/graph_rag_service/api/simulation.py
4.96 kB
| from fastapi import APIRouter, Depends, HTTPException | |
| from typing import Dict, Any | |
| from pydantic import BaseModel | |
| from ..core.neo4j_store import Neo4jStore | |
| from ..core.llm_factory import UnifiedLLMProvider | |
| from .auth import get_current_user, User | |
| router = APIRouter(prefix="/api/v1/simulation", tags=["Simulation Interaction"]) | |
| def get_global_store(): | |
| # Lazy import to avoid circular dependency with server.py | |
| from .server import graph_store | |
| if graph_store is None: | |
| raise HTTPException(status_code=500, detail="Neo4j connection not initialized.") | |
| return graph_store | |
| def get_global_llm(): | |
| return UnifiedLLMProvider() | |
| def require_admin(user: User = Depends(get_current_user)) -> User: | |
| """Simulation jobs mutate shared state and consume LLM budget — admin only.""" | |
| if "admin" not in getattr(user, "scopes", []): | |
| raise HTTPException(status_code=403, detail="Admin scope required.") | |
| return user | |
| class InterviewRequest(BaseModel): | |
| agent_id: str | |
| user_query: str | |
| async def live_interview_agent( | |
| request: InterviewRequest, | |
| store: Neo4jStore = Depends(get_global_store), | |
| llm: UnifiedLLMProvider = Depends(get_global_llm), | |
| current_user: User = Depends(get_current_user), | |
| ): | |
| """ | |
| MiroFish Point 5: 'Live Interviews' with Simulated Personas. | |
| Allows users to chat with a graph entity in character, | |
| injecting their exact Neo4j memory into the system prompt. | |
| """ | |
| # 1. Fetch Agent Persona | |
| query_agent = """ | |
| MATCH (a:Entity {id: $agent_id}) | |
| RETURN a.name as name, a.persona as persona | |
| """ | |
| results = await store.execute_query(query_agent, {"agent_id": request.agent_id}) | |
| if not results: | |
| raise HTTPException(status_code=404, detail="Agent persona not found.") | |
| agent = results[0] | |
| # 2. Fetch Agent's 'Memory' (Recent relationships and properties) | |
| query_memory = """ | |
| MATCH (a:Entity {id: $agent_id})-[r]->(t:Entity) | |
| WITH t.name as target, type(r) as relation, coalesce(r.content, r.properties) as details, r.valid_from as timestamp | |
| ORDER BY timestamp DESC | |
| LIMIT 5 | |
| RETURN relation, target, details | |
| """ | |
| memories = await store.execute_query(query_memory, {"agent_id": request.agent_id}) | |
| memory_string = "Your recent memories/actions in the simulation sandbox:\n" | |
| for m in memories: | |
| memory_string += f"- You {m['relation']} ({m['target']}): {m['details']}\n" | |
| # 3. Construct System Persona Prompt | |
| system_prompt = ( | |
| f"You are engaging in a live roleplay interview.\n" | |
| f"Your Name: {agent['name']}\n" | |
| f"Your Psychological Profile & Background:\n{agent['persona']}\n\n" | |
| f"{memory_string if memories else 'You have no recent memories recorded.'}\n\n" | |
| f"Answer the user's question completely in character based ONLY on your profile and memories." | |
| ) | |
| # 4. Send to Local LLM | |
| try: | |
| response_text = await llm.complete( | |
| prompt=request.user_query, | |
| system_prompt=system_prompt, | |
| temperature=0.8 # slightly higher for creative roleplay | |
| ) | |
| return { | |
| "agent_id": request.agent_id, | |
| "agent_name": agent['name'], | |
| "response": response_text | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f"LLM Interaction Failed: {str(e)}") | |
| from ..retrieval.report_agent import ReportAgent | |
| async def get_sandbox_report( | |
| store: Neo4jStore = Depends(get_global_store), | |
| llm: UnifiedLLMProvider = Depends(get_global_llm), | |
| current_user: User = Depends(get_current_user), | |
| ): | |
| ''' | |
| MiroFish Point 3: Dedicated ReAct Reporting Agent & Tooling. | |
| Triggers the analytical engine to build a report off the recent sandbox events. | |
| ''' | |
| try: | |
| agent = ReportAgent(store, llm) | |
| report = await agent.generate_report( | |
| topic="Recent simulation sandbox activity, personas, and events", | |
| report_type="detailed", | |
| tenant_id=getattr(current_user, "tenant_id", None), | |
| ) | |
| return report.model_dump() | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=f'Report generation failed: {str(e)}') | |
| from ..workers.celery_worker import celery_app | |
| async def start_persona_generation(admin_user: User = Depends(require_admin)): | |
| '''Trigger background Celery task to generate personas.''' | |
| task = celery_app.send_task('generate_personas') | |
| return {'status': 'accepted', 'task_id': task.id} | |
| async def start_simulation_tick(admin_user: User = Depends(require_admin)): | |
| '''Trigger background Celery task to run a simulation loop tick.''' | |
| task = celery_app.send_task('run_simulation_tick') | |
| return {'status': 'accepted', 'task_id': task.id} | |