fix(agents): move project metrics endpoint before {agent_id} routes
FastAPI processes routes in order, so /agents/metrics must be defined
before /agents/{agent_id} to prevent "metrics" from being parsed as a UUID.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -363,6 +363,73 @@ async def list_project_agents(
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
# ===== Project Agent Metrics Endpoint =====
|
||||||
|
# NOTE: This endpoint MUST be defined before /{agent_id} routes
|
||||||
|
# to prevent FastAPI from trying to parse "metrics" as a UUID
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/projects/{project_id}/agents/metrics",
|
||||||
|
response_model=AgentInstanceMetrics,
|
||||||
|
summary="Get Project Agent Metrics",
|
||||||
|
description="Get aggregated usage metrics for all agents in a project.",
|
||||||
|
operation_id="get_project_agent_metrics",
|
||||||
|
)
|
||||||
|
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
|
||||||
|
async def get_project_agent_metrics(
|
||||||
|
request: Request,
|
||||||
|
project_id: UUID,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
) -> Any:
|
||||||
|
"""
|
||||||
|
Get aggregated usage metrics for all agents in a project.
|
||||||
|
|
||||||
|
Returns aggregated metrics across all agents including total
|
||||||
|
tasks completed, tokens used, and cost incurred.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
request: FastAPI request object (for rate limiting)
|
||||||
|
project_id: UUID of the project
|
||||||
|
current_user: Current authenticated user
|
||||||
|
db: Database session
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AgentInstanceMetrics: Aggregated project agent metrics
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
NotFoundError: If the project is not found
|
||||||
|
AuthorizationError: If the user lacks access to the project
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Verify project access
|
||||||
|
project = await verify_project_access(db, project_id, current_user)
|
||||||
|
|
||||||
|
# Get aggregated metrics for the project
|
||||||
|
metrics = await agent_instance_crud.get_project_metrics(
|
||||||
|
db, project_id=project_id
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
f"User {current_user.email} retrieved project metrics for {project.slug}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return AgentInstanceMetrics(
|
||||||
|
total_instances=metrics["total_instances"],
|
||||||
|
active_instances=metrics["active_instances"],
|
||||||
|
idle_instances=metrics["idle_instances"],
|
||||||
|
total_tasks_completed=metrics["total_tasks_completed"],
|
||||||
|
total_tokens_used=metrics["total_tokens_used"],
|
||||||
|
total_cost_incurred=metrics["total_cost_incurred"],
|
||||||
|
)
|
||||||
|
|
||||||
|
except (NotFoundError, AuthorizationError):
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error getting project agent metrics: {e!s}", exc_info=True)
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/projects/{project_id}/agents/{agent_id}",
|
"/projects/{project_id}/agents/{agent_id}",
|
||||||
response_model=AgentInstanceResponse,
|
response_model=AgentInstanceResponse,
|
||||||
@@ -908,65 +975,3 @@ async def get_agent_metrics(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error getting agent metrics: {e!s}", exc_info=True)
|
logger.error(f"Error getting agent metrics: {e!s}", exc_info=True)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
|
||||||
"/projects/{project_id}/agents/metrics",
|
|
||||||
response_model=AgentInstanceMetrics,
|
|
||||||
summary="Get Project Agent Metrics",
|
|
||||||
description="Get aggregated usage metrics for all agents in a project.",
|
|
||||||
operation_id="get_project_agent_metrics",
|
|
||||||
)
|
|
||||||
@limiter.limit(f"{60 * RATE_MULTIPLIER}/minute")
|
|
||||||
async def get_project_agent_metrics(
|
|
||||||
request: Request,
|
|
||||||
project_id: UUID,
|
|
||||||
current_user: User = Depends(get_current_user),
|
|
||||||
db: AsyncSession = Depends(get_db),
|
|
||||||
) -> Any:
|
|
||||||
"""
|
|
||||||
Get aggregated usage metrics for all agents in a project.
|
|
||||||
|
|
||||||
Returns aggregated metrics across all agents including total
|
|
||||||
tasks completed, tokens used, and cost incurred.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
request: FastAPI request object (for rate limiting)
|
|
||||||
project_id: UUID of the project
|
|
||||||
current_user: Current authenticated user
|
|
||||||
db: Database session
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AgentInstanceMetrics: Aggregated project agent metrics
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NotFoundError: If the project is not found
|
|
||||||
AuthorizationError: If the user lacks access to the project
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# Verify project access
|
|
||||||
project = await verify_project_access(db, project_id, current_user)
|
|
||||||
|
|
||||||
# Get aggregated metrics for the project
|
|
||||||
metrics = await agent_instance_crud.get_project_metrics(
|
|
||||||
db, project_id=project_id
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
f"User {current_user.email} retrieved project metrics for {project.slug}"
|
|
||||||
)
|
|
||||||
|
|
||||||
return AgentInstanceMetrics(
|
|
||||||
total_instances=metrics["total_instances"],
|
|
||||||
active_instances=metrics["active_instances"],
|
|
||||||
idle_instances=metrics["idle_instances"],
|
|
||||||
total_tasks_completed=metrics["total_tasks_completed"],
|
|
||||||
total_tokens_used=metrics["total_tokens_used"],
|
|
||||||
total_cost_incurred=metrics["total_cost_incurred"],
|
|
||||||
)
|
|
||||||
|
|
||||||
except (NotFoundError, AuthorizationError):
|
|
||||||
raise
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error getting project agent metrics: {e!s}", exc_info=True)
|
|
||||||
raise
|
|
||||||
|
|||||||
Reference in New Issue
Block a user