agno 2.1.10__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. agno/agent/agent.py +1594 -1248
  2. agno/knowledge/knowledge.py +11 -0
  3. agno/knowledge/reader/pptx_reader.py +101 -0
  4. agno/knowledge/reader/reader_factory.py +14 -0
  5. agno/knowledge/types.py +1 -0
  6. agno/models/anthropic/claude.py +2 -2
  7. agno/models/base.py +4 -4
  8. agno/models/ollama/chat.py +7 -2
  9. agno/os/app.py +1 -1
  10. agno/os/interfaces/a2a/router.py +2 -2
  11. agno/os/interfaces/agui/router.py +2 -2
  12. agno/os/router.py +7 -7
  13. agno/os/routers/evals/schemas.py +31 -31
  14. agno/os/routers/health.py +6 -2
  15. agno/os/routers/knowledge/schemas.py +49 -47
  16. agno/os/routers/memory/schemas.py +16 -16
  17. agno/os/routers/metrics/schemas.py +16 -16
  18. agno/os/routers/session/session.py +382 -7
  19. agno/os/schema.py +254 -231
  20. agno/os/utils.py +1 -1
  21. agno/run/agent.py +54 -1
  22. agno/run/team.py +48 -0
  23. agno/run/workflow.py +15 -5
  24. agno/session/summary.py +45 -13
  25. agno/session/team.py +90 -5
  26. agno/team/team.py +1130 -849
  27. agno/utils/agent.py +372 -0
  28. agno/utils/events.py +144 -2
  29. agno/utils/message.py +60 -0
  30. agno/utils/print_response/agent.py +10 -6
  31. agno/utils/print_response/team.py +6 -4
  32. agno/utils/print_response/workflow.py +7 -5
  33. agno/utils/team.py +9 -8
  34. agno/workflow/condition.py +17 -9
  35. agno/workflow/loop.py +18 -10
  36. agno/workflow/parallel.py +14 -6
  37. agno/workflow/router.py +16 -8
  38. agno/workflow/step.py +14 -6
  39. agno/workflow/steps.py +14 -6
  40. agno/workflow/workflow.py +331 -123
  41. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/METADATA +63 -23
  42. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/RECORD +45 -43
  43. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/WHEEL +0 -0
  44. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/licenses/LICENSE +0 -0
  45. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/top_level.txt +0 -0
@@ -1,27 +1,27 @@
1
1
  from datetime import datetime
2
2
  from typing import Any, Dict, List, Optional
3
3
 
4
- from pydantic import BaseModel
4
+ from pydantic import BaseModel, Field
5
5
 
6
6
 
7
7
  class DayAggregatedMetrics(BaseModel):
8
8
  """Aggregated metrics for a given day"""
9
9
 
10
- id: str
10
+ id: str = Field(..., description="Unique identifier for the metrics record")
11
11
 
12
- agent_runs_count: int
13
- agent_sessions_count: int
14
- team_runs_count: int
15
- team_sessions_count: int
16
- workflow_runs_count: int
17
- workflow_sessions_count: int
18
- users_count: int
19
- token_metrics: Dict[str, Any]
20
- model_metrics: List[Dict[str, Any]]
12
+ agent_runs_count: int = Field(..., description="Total number of agent runs", ge=0)
13
+ agent_sessions_count: int = Field(..., description="Total number of agent sessions", ge=0)
14
+ team_runs_count: int = Field(..., description="Total number of team runs", ge=0)
15
+ team_sessions_count: int = Field(..., description="Total number of team sessions", ge=0)
16
+ workflow_runs_count: int = Field(..., description="Total number of workflow runs", ge=0)
17
+ workflow_sessions_count: int = Field(..., description="Total number of workflow sessions", ge=0)
18
+ users_count: int = Field(..., description="Total number of unique users", ge=0)
19
+ token_metrics: Dict[str, Any] = Field(..., description="Token usage metrics (input, output, cached, etc.)")
20
+ model_metrics: List[Dict[str, Any]] = Field(..., description="Metrics grouped by model (model_id, provider, count)")
21
21
 
22
- date: datetime
23
- created_at: int
24
- updated_at: int
22
+ date: datetime = Field(..., description="Date for which these metrics are aggregated")
23
+ created_at: int = Field(..., description="Unix timestamp when metrics were created", ge=0)
24
+ updated_at: int = Field(..., description="Unix timestamp when metrics were last updated", ge=0)
25
25
 
26
26
  @classmethod
27
27
  def from_dict(cls, metrics_dict: Dict[str, Any]) -> "DayAggregatedMetrics":
@@ -43,5 +43,5 @@ class DayAggregatedMetrics(BaseModel):
43
43
 
44
44
 
45
45
  class MetricsResponse(BaseModel):
46
- metrics: List[DayAggregatedMetrics]
47
- updated_at: Optional[datetime]
46
+ metrics: List[DayAggregatedMetrics] = Field(..., description="List of daily aggregated metrics")
47
+ updated_at: Optional[datetime] = Field(None, description="Timestamp of the most recent metrics update")
@@ -1,5 +1,7 @@
1
1
  import logging
2
- from typing import List, Optional, Union, cast
2
+ import time
3
+ from typing import Any, List, Optional, Union, cast
4
+ from uuid import uuid4
3
5
 
4
6
  from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request
5
7
 
@@ -8,6 +10,7 @@ from agno.os.auth import get_authentication_dependency
8
10
  from agno.os.schema import (
9
11
  AgentSessionDetailSchema,
10
12
  BadRequestResponse,
13
+ CreateSessionRequest,
11
14
  DeleteSessionRequest,
12
15
  InternalServerErrorResponse,
13
16
  NotFoundResponse,
@@ -19,12 +22,14 @@ from agno.os.schema import (
19
22
  TeamRunSchema,
20
23
  TeamSessionDetailSchema,
21
24
  UnauthenticatedResponse,
25
+ UpdateSessionRequest,
22
26
  ValidationErrorResponse,
23
27
  WorkflowRunSchema,
24
28
  WorkflowSessionDetailSchema,
25
29
  )
26
30
  from agno.os.settings import AgnoAPISettings
27
31
  from agno.os.utils import get_db
32
+ from agno.session import AgentSession, TeamSession, WorkflowSession
28
33
 
29
34
  logger = logging.getLogger(__name__)
30
35
 
@@ -146,6 +151,133 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
146
151
  ),
147
152
  )
148
153
 
154
+ @router.post(
155
+ "/sessions",
156
+ response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
157
+ status_code=201,
158
+ operation_id="create_session",
159
+ summary="Create New Session",
160
+ description=(
161
+ "Create a new empty session with optional configuration. "
162
+ "Useful for pre-creating sessions with specific session_state, metadata, or other properties "
163
+ "before running any agent/team/workflow interactions. "
164
+ "The session can later be used by providing its session_id in run requests."
165
+ ),
166
+ responses={
167
+ 201: {
168
+ "description": "Session created successfully",
169
+ "content": {
170
+ "application/json": {
171
+ "examples": {
172
+ "agent_session_example": {
173
+ "summary": "Example created agent session",
174
+ "value": {
175
+ "user_id": "user-123",
176
+ "agent_session_id": "new-session-id",
177
+ "session_id": "new-session-id",
178
+ "session_name": "New Session",
179
+ "session_state": {"key": "value"},
180
+ "metadata": {"key": "value"},
181
+ "agent_id": "agent-1",
182
+ "created_at": "2025-10-21T12:00:00Z",
183
+ "updated_at": "2025-10-21T12:00:00Z",
184
+ },
185
+ }
186
+ }
187
+ }
188
+ },
189
+ },
190
+ 400: {"description": "Invalid request parameters", "model": BadRequestResponse},
191
+ 422: {"description": "Validation error", "model": ValidationErrorResponse},
192
+ 500: {"description": "Failed to create session", "model": InternalServerErrorResponse},
193
+ },
194
+ )
195
+ async def create_session(
196
+ request: Request,
197
+ session_type: SessionType = Query(
198
+ default=SessionType.AGENT, alias="type", description="Type of session to create (agent, team, or workflow)"
199
+ ),
200
+ create_session_request: CreateSessionRequest = Body(
201
+ default=CreateSessionRequest(), description="Session configuration data"
202
+ ),
203
+ db_id: Optional[str] = Query(default=None, description="Database ID to create session in"),
204
+ ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
205
+ db = get_db(dbs, db_id)
206
+
207
+ # Get user_id from request state if available (from auth middleware)
208
+ user_id = create_session_request.user_id
209
+ if hasattr(request.state, "user_id"):
210
+ user_id = request.state.user_id
211
+
212
+ # Generate session_id if not provided
213
+ session_id = create_session_request.session_id or str(uuid4())
214
+
215
+ # Prepare session_data with session_state and session_name
216
+ session_data: dict[str, Any] = {}
217
+ if create_session_request.session_state is not None:
218
+ session_data["session_state"] = create_session_request.session_state
219
+ if create_session_request.session_name is not None:
220
+ session_data["session_name"] = create_session_request.session_name
221
+
222
+ current_time = int(time.time())
223
+
224
+ # Create the appropriate session type
225
+ session: Union[AgentSession, TeamSession, WorkflowSession]
226
+ if session_type == SessionType.AGENT:
227
+ session = AgentSession(
228
+ session_id=session_id,
229
+ agent_id=create_session_request.agent_id,
230
+ user_id=user_id,
231
+ session_data=session_data if session_data else None,
232
+ metadata=create_session_request.metadata,
233
+ created_at=current_time,
234
+ updated_at=current_time,
235
+ )
236
+ elif session_type == SessionType.TEAM:
237
+ session = TeamSession(
238
+ session_id=session_id,
239
+ team_id=create_session_request.team_id,
240
+ user_id=user_id,
241
+ session_data=session_data if session_data else None,
242
+ metadata=create_session_request.metadata,
243
+ created_at=current_time,
244
+ updated_at=current_time,
245
+ )
246
+ elif session_type == SessionType.WORKFLOW:
247
+ session = WorkflowSession(
248
+ session_id=session_id,
249
+ workflow_id=create_session_request.workflow_id,
250
+ user_id=user_id,
251
+ session_data=session_data if session_data else None,
252
+ metadata=create_session_request.metadata,
253
+ created_at=current_time,
254
+ updated_at=current_time,
255
+ )
256
+ else:
257
+ raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
258
+
259
+ # Upsert the session to the database
260
+ try:
261
+ if isinstance(db, AsyncBaseDb):
262
+ db = cast(AsyncBaseDb, db)
263
+ created_session = await db.upsert_session(session, deserialize=True)
264
+ else:
265
+ created_session = db.upsert_session(session, deserialize=True)
266
+
267
+ if not created_session:
268
+ raise HTTPException(status_code=500, detail="Failed to create session")
269
+
270
+ # Return appropriate schema based on session type
271
+ if session_type == SessionType.AGENT:
272
+ return AgentSessionDetailSchema.from_session(created_session) # type: ignore
273
+ elif session_type == SessionType.TEAM:
274
+ return TeamSessionDetailSchema.from_session(created_session) # type: ignore
275
+ else:
276
+ return WorkflowSessionDetailSchema.from_session(created_session) # type: ignore
277
+ except Exception as e:
278
+ logger.error(f"Error creating session: {e}")
279
+ raise HTTPException(status_code=500, detail=f"Failed to create session: {str(e)}")
280
+
149
281
  @router.get(
150
282
  "/sessions/{session_id}",
151
283
  response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
@@ -271,8 +403,9 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
271
403
  operation_id="get_session_runs",
272
404
  summary="Get Session Runs",
273
405
  description=(
274
- "Retrieve all runs (executions) for a specific session. Runs represent individual "
275
- "interactions or executions within a session. Response schema varies based on session type."
406
+ "Retrieve all runs (executions) for a specific session with optional timestamp filtering. "
407
+ "Runs represent individual interactions or executions within a session. "
408
+ "Response schema varies based on session type."
276
409
  ),
277
410
  responses={
278
411
  200: {
@@ -386,6 +519,14 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
386
519
  default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
387
520
  ),
388
521
  user_id: Optional[str] = Query(default=None, description="User ID to query runs from"),
522
+ created_after: Optional[int] = Query(
523
+ default=None,
524
+ description="Filter runs created after this Unix timestamp (epoch time in seconds)",
525
+ ),
526
+ created_before: Optional[int] = Query(
527
+ default=None,
528
+ description="Filter runs created before this Unix timestamp (epoch time in seconds)",
529
+ ),
389
530
  db_id: Optional[str] = Query(default=None, description="Database ID to query runs from"),
390
531
  ) -> List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]]:
391
532
  db = get_db(dbs, db_id)
@@ -393,6 +534,10 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
393
534
  if hasattr(request.state, "user_id"):
394
535
  user_id = request.state.user_id
395
536
 
537
+ # Use timestamp filters directly (already in epoch format)
538
+ start_timestamp = created_after
539
+ end_timestamp = created_before
540
+
396
541
  if isinstance(db, AsyncBaseDb):
397
542
  db = cast(AsyncBaseDb, db)
398
543
  session = await db.get_session(
@@ -408,15 +553,33 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
408
553
 
409
554
  runs = session.get("runs") # type: ignore
410
555
  if not runs:
411
- raise HTTPException(status_code=404, detail=f"Session with ID {session_id} has no runs")
556
+ return []
557
+
558
+ # Filter runs by timestamp if specified
559
+ # TODO: Move this filtering into the DB layer
560
+ filtered_runs = []
561
+ for run in runs:
562
+ if start_timestamp or end_timestamp:
563
+ run_created_at = run.get("created_at")
564
+ if run_created_at:
565
+ # created_at is stored as epoch int
566
+ if start_timestamp and run_created_at < start_timestamp:
567
+ continue
568
+ if end_timestamp and run_created_at > end_timestamp:
569
+ continue
570
+
571
+ filtered_runs.append(run)
572
+
573
+ if not filtered_runs:
574
+ return []
412
575
 
413
576
  run_responses: List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]] = []
414
577
 
415
578
  if session_type == SessionType.AGENT:
416
- return [RunSchema.from_dict(run) for run in runs]
579
+ return [RunSchema.from_dict(run) for run in filtered_runs]
417
580
 
418
581
  elif session_type == SessionType.TEAM:
419
- for run in runs:
582
+ for run in filtered_runs:
420
583
  if run.get("agent_id") is not None:
421
584
  run_responses.append(RunSchema.from_dict(run))
422
585
  elif run.get("team_id") is not None:
@@ -424,7 +587,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
424
587
  return run_responses
425
588
 
426
589
  elif session_type == SessionType.WORKFLOW:
427
- for run in runs:
590
+ for run in filtered_runs:
428
591
  if run.get("workflow_id") is not None:
429
592
  run_responses.append(WorkflowRunSchema.from_dict(run))
430
593
  elif run.get("team_id") is not None:
@@ -435,6 +598,93 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
435
598
  else:
436
599
  raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
437
600
 
601
+ @router.get(
602
+ "/sessions/{session_id}/runs/{run_id}",
603
+ response_model=Union[RunSchema, TeamRunSchema, WorkflowRunSchema],
604
+ status_code=200,
605
+ operation_id="get_session_run",
606
+ summary="Get Run by ID",
607
+ description=(
608
+ "Retrieve a specific run by its ID from a session. Response schema varies based on the "
609
+ "run type (agent run, team run, or workflow run)."
610
+ ),
611
+ responses={
612
+ 200: {
613
+ "description": "Run retrieved successfully",
614
+ "content": {
615
+ "application/json": {
616
+ "examples": {
617
+ "agent_run": {
618
+ "summary": "Example agent run",
619
+ "value": {
620
+ "run_id": "fcdf50f0-7c32-4593-b2ef-68a558774340",
621
+ "parent_run_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
622
+ "agent_id": "basic-agent",
623
+ "user_id": "user_123",
624
+ "run_input": "Which tools do you have access to?",
625
+ "content": "I don't have access to external tools.",
626
+ "created_at": 1728499200,
627
+ },
628
+ }
629
+ }
630
+ }
631
+ },
632
+ },
633
+ 404: {"description": "Session or run not found", "model": NotFoundResponse},
634
+ 422: {"description": "Invalid session type", "model": ValidationErrorResponse},
635
+ },
636
+ )
637
+ async def get_session_run(
638
+ request: Request,
639
+ session_id: str = Path(description="Session ID to get run from"),
640
+ run_id: str = Path(description="Run ID to retrieve"),
641
+ session_type: SessionType = Query(
642
+ default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
643
+ ),
644
+ user_id: Optional[str] = Query(default=None, description="User ID to query run from"),
645
+ db_id: Optional[str] = Query(default=None, description="Database ID to query run from"),
646
+ ) -> Union[RunSchema, TeamRunSchema, WorkflowRunSchema]:
647
+ db = get_db(dbs, db_id)
648
+
649
+ if hasattr(request.state, "user_id"):
650
+ user_id = request.state.user_id
651
+
652
+ if isinstance(db, AsyncBaseDb):
653
+ db = cast(AsyncBaseDb, db)
654
+ session = await db.get_session(
655
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
656
+ )
657
+ else:
658
+ session = db.get_session(
659
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
660
+ )
661
+
662
+ if not session:
663
+ raise HTTPException(status_code=404, detail=f"Session with ID {session_id} not found")
664
+
665
+ runs = session.get("runs") # type: ignore
666
+ if not runs:
667
+ raise HTTPException(status_code=404, detail=f"Session with ID {session_id} has no runs")
668
+
669
+ # Find the specific run
670
+ # TODO: Move this filtering into the DB layer
671
+ target_run = None
672
+ for run in runs:
673
+ if run.get("run_id") == run_id:
674
+ target_run = run
675
+ break
676
+
677
+ if not target_run:
678
+ raise HTTPException(status_code=404, detail=f"Run with ID {run_id} not found in session {session_id}")
679
+
680
+ # Return the appropriate schema based on run type
681
+ if target_run.get("workflow_id") is not None:
682
+ return WorkflowRunSchema.from_dict(target_run)
683
+ elif target_run.get("team_id") is not None:
684
+ return TeamRunSchema.from_dict(target_run)
685
+ else:
686
+ return RunSchema.from_dict(target_run)
687
+
438
688
  @router.delete(
439
689
  "/sessions/{session_id}",
440
690
  status_code=204,
@@ -609,4 +859,129 @@ def attach_routes(router: APIRouter, dbs: dict[str, Union[BaseDb, AsyncBaseDb]])
609
859
  else:
610
860
  return WorkflowSessionDetailSchema.from_session(session) # type: ignore
611
861
 
862
+ @router.patch(
863
+ "/sessions/{session_id}",
864
+ response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
865
+ status_code=200,
866
+ operation_id="update_session",
867
+ summary="Update Session",
868
+ description=(
869
+ "Update session properties such as session_name, session_state, metadata, or summary. "
870
+ "Use this endpoint to modify the session name, update state, add metadata, or update the session summary."
871
+ ),
872
+ responses={
873
+ 200: {
874
+ "description": "Session updated successfully",
875
+ "content": {
876
+ "application/json": {
877
+ "examples": {
878
+ "update_summary": {
879
+ "summary": "Update session summary",
880
+ "value": {
881
+ "summary": {
882
+ "summary": "The user discussed project planning with the agent.",
883
+ "updated_at": "2025-10-21T14:30:00Z",
884
+ }
885
+ },
886
+ },
887
+ "update_metadata": {
888
+ "summary": "Update session metadata",
889
+ "value": {
890
+ "metadata": {
891
+ "tags": ["planning", "project"],
892
+ "priority": "high",
893
+ }
894
+ },
895
+ },
896
+ "update_session_name": {
897
+ "summary": "Update session name",
898
+ "value": {"session_name": "Updated Session Name"},
899
+ },
900
+ "update_session_state": {
901
+ "summary": "Update session state",
902
+ "value": {
903
+ "session_state": {
904
+ "step": "completed",
905
+ "context": "Project planning finished",
906
+ "progress": 100,
907
+ }
908
+ },
909
+ },
910
+ }
911
+ }
912
+ },
913
+ },
914
+ 404: {"description": "Session not found", "model": NotFoundResponse},
915
+ 422: {"description": "Invalid request", "model": ValidationErrorResponse},
916
+ 500: {"description": "Failed to update session", "model": InternalServerErrorResponse},
917
+ },
918
+ )
919
+ async def update_session(
920
+ request: Request,
921
+ session_id: str = Path(description="Session ID to update"),
922
+ session_type: SessionType = Query(
923
+ default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
924
+ ),
925
+ update_data: UpdateSessionRequest = Body(description="Session update data"),
926
+ user_id: Optional[str] = Query(default=None, description="User ID"),
927
+ db_id: Optional[str] = Query(default=None, description="Database ID to use for update operation"),
928
+ ) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
929
+ db = get_db(dbs, db_id)
930
+
931
+ if hasattr(request.state, "user_id"):
932
+ user_id = request.state.user_id
933
+
934
+ # Get the existing session
935
+ if isinstance(db, AsyncBaseDb):
936
+ db = cast(AsyncBaseDb, db)
937
+ existing_session = await db.get_session(
938
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
939
+ )
940
+ else:
941
+ existing_session = db.get_session(
942
+ session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
943
+ )
944
+
945
+ if not existing_session:
946
+ raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
947
+
948
+ # Update session properties
949
+ # Handle session_name - stored in session_data
950
+ if update_data.session_name is not None:
951
+ if existing_session.session_data is None: # type: ignore
952
+ existing_session.session_data = {} # type: ignore
953
+ existing_session.session_data["session_name"] = update_data.session_name # type: ignore
954
+
955
+ # Handle session_state - stored in session_data
956
+ if update_data.session_state is not None:
957
+ if existing_session.session_data is None: # type: ignore
958
+ existing_session.session_data = {} # type: ignore
959
+ existing_session.session_data["session_state"] = update_data.session_state # type: ignore
960
+
961
+ if update_data.metadata is not None:
962
+ existing_session.metadata = update_data.metadata # type: ignore
963
+
964
+ if update_data.summary is not None:
965
+ from agno.session.summary import SessionSummary
966
+
967
+ existing_session.summary = SessionSummary.from_dict(update_data.summary) # type: ignore
968
+
969
+ # Upsert the updated session
970
+ if isinstance(db, AsyncBaseDb):
971
+ db = cast(AsyncBaseDb, db)
972
+ updated_session = await db.upsert_session(existing_session, deserialize=True) # type: ignore
973
+ else:
974
+ updated_session = db.upsert_session(existing_session, deserialize=True) # type: ignore
975
+
976
+ if not updated_session:
977
+ raise HTTPException(status_code=500, detail="Failed to update session")
978
+
979
+ # Return appropriate schema based on session type
980
+ if session_type == SessionType.AGENT:
981
+ return AgentSessionDetailSchema.from_session(updated_session) # type: ignore
982
+ elif session_type == SessionType.TEAM:
983
+ return TeamSessionDetailSchema.from_session(updated_session) # type: ignore
984
+ else:
985
+ return WorkflowSessionDetailSchema.from_session(updated_session) # type: ignore
986
+
612
987
  return router