(session_id: str, request: Request, supabase: AsyncSupabaseClient)
| 744 | |
| 745 | @router.get("/sessions/{session_id}/stats") |
| 746 | async def v2_get_session_stats(session_id: str, request: Request, supabase: AsyncSupabaseClient): |
| 747 | try: |
| 748 | api_key = request.headers.get("X-Agentops-Api-Key") |
| 749 | if api_key is None: |
| 750 | raise RuntimeError("API Key is Missing") |
| 751 | |
| 752 | validate_uuid(api_key) |
| 753 | |
| 754 | # Get project ID from API key |
| 755 | project_response = await supabase.table("projects").select("id").eq("api_key", api_key).execute() |
| 756 | project = project_response.data[0] if project_response.data else None |
| 757 | |
| 758 | if not project: |
| 759 | raise RuntimeError("Invalid API Key") |
| 760 | |
| 761 | # Check if session belongs to this project |
| 762 | session_response = ( |
| 763 | await supabase.table("sessions") |
| 764 | .select("id") |
| 765 | .eq("id", session_id) |
| 766 | .eq("project_id", project["id"]) |
| 767 | .execute() |
| 768 | ) |
| 769 | if not session_response.data: |
| 770 | raise RuntimeError("Session does not belong to this project") |
| 771 | |
| 772 | stats_response = await supabase.table("stats").select("*").eq("session_id", session_id).execute() |
| 773 | stats = ( |
| 774 | stats_response.data[0] |
| 775 | if stats_response.data |
| 776 | else { |
| 777 | "events": 0, |
| 778 | "cost": "0.00", |
| 779 | "prompt_tokens": 0, |
| 780 | "completion_tokens": 0, |
| 781 | "errors": 0, |
| 782 | } |
| 783 | ) |
| 784 | |
| 785 | return JSONResponse(stats) |
| 786 | |
| 787 | except InvalidAPIKeyError as e: |
| 788 | # This is a user error (invalid API key format), not a system error |
| 789 | # Log as warning to avoid Sentry alerts |
| 790 | logger.warning(f"{request.url.path}: Invalid API key format for session {session_id}") |
| 791 | return JSONResponse({"path": request.url.path, "message": "Invalid API key format"}, status_code=400) |
| 792 | except RuntimeError as e: |
| 793 | logger.error(f"Error getting session stats: {e}") |
| 794 | return JSONResponse({"path": request.url.path, "message": str(e)}, status_code=400) |
| 795 | |
| 796 | |
| 797 | @router.get("/sessions/{session_id}/export") |
nothing calls this directly
no test coverage detected
searching dependent graphs…