From b8a6496acb9522d571b2a56e16d066298ef0cd28 Mon Sep 17 00:00:00 2001 From: Sarah Wooders Date: Wed, 8 Oct 2025 15:25:26 -0700 Subject: [PATCH] feat: add `runs_metrics` table (#5169) --- fern/openapi.json | 97 ++++++++++++++++++++++++ letta/server/rest_api/routers/v1/runs.py | 17 +++++ 2 files changed, 114 insertions(+) diff --git a/fern/openapi.json b/fern/openapi.json index a1dffb57..a46c5abd 100644 --- a/fern/openapi.json +++ b/fern/openapi.json @@ -38347,6 +38347,103 @@ "required": ["id"], "title": "RunMetrics" }, + "RunMetrics": { + "properties": { + "id": { + "type": "string", + "title": "Id", + "description": "The id of the run this metric belongs to (matches runs.id)." + }, + "agent_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Agent Id", + "description": "The unique identifier of the agent." + }, + "project_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Project Id", + "description": "The project that the run belongs to (cloud only)." + }, + "run_start_ns": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Run Start Ns", + "description": "The timestamp of the start of the run in nanoseconds." + }, + "run_ns": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Run Ns", + "description": "Total time for the run in nanoseconds." + }, + "num_steps": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Num Steps", + "description": "The number of steps in the run." + }, + "template_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Template Id", + "description": "The template ID that the run belongs to (cloud only)." + }, + "base_template_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Base Template Id", + "description": "The base template ID that the run belongs to (cloud only)." + } + }, + "additionalProperties": false, + "type": "object", + "required": ["id"], + "title": "RunMetrics" + }, "RunStatus": { "type": "string", "enum": ["created", "running", "completed", "failed", "cancelled"], diff --git a/letta/server/rest_api/routers/v1/runs.py b/letta/server/rest_api/routers/v1/runs.py index d643e225..3bccc1f9 100644 --- a/letta/server/rest_api/routers/v1/runs.py +++ b/letta/server/rest_api/routers/v1/runs.py @@ -214,6 +214,23 @@ async def retrieve_metrics_for_run( return await runs_manager.get_run_metrics_async(run_id=run_id, actor=actor) +@router.get("/{run_id}/metrics", response_model=RunMetrics, operation_id="retrieve_metrics_for_run") +async def retrieve_metrics_for_run( + run_id: str, + headers: HeaderParams = Depends(get_headers), + server: "SyncServer" = Depends(get_letta_server), +): + """ + Get run metrics by run ID. + """ + try: + actor = await server.user_manager.get_actor_or_default_async(actor_id=headers.actor_id) + runs_manager = RunManager() + return await runs_manager.get_run_metrics_async(run_id=run_id, actor=actor) + except NoResultFound: + raise HTTPException(status_code=404, detail="Run metrics not found") + + @router.get( "/{run_id}/steps", response_model=List[Step],