agno 2.1.2__py3-none-any.whl → 2.3.13__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.
- agno/agent/agent.py +5540 -2273
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +689 -6
- agno/db/dynamo/dynamo.py +933 -37
- agno/db/dynamo/schemas.py +174 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +831 -9
- agno/db/firestore/schemas.py +51 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +660 -12
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +287 -14
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +590 -14
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +43 -13
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +15 -1
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +879 -11
- agno/db/mongo/schemas.py +42 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +946 -68
- agno/db/mysql/schemas.py +72 -10
- agno/db/mysql/utils.py +198 -7
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +942 -57
- agno/db/postgres/schemas.py +81 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +671 -7
- agno/db/redis/schemas.py +50 -0
- agno/db/redis/utils.py +65 -7
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +17 -2
- agno/db/singlestore/schemas.py +63 -0
- agno/db/singlestore/singlestore.py +949 -83
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +62 -0
- agno/db/sqlite/sqlite.py +965 -46
- agno/db/sqlite/utils.py +169 -8
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +2 -0
- agno/eval/__init__.py +10 -0
- agno/eval/accuracy.py +75 -55
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +16 -7
- agno/eval/reliability.py +28 -16
- agno/eval/utils.py +35 -17
- agno/exceptions.py +27 -2
- agno/filters.py +354 -0
- agno/guardrails/prompt_injection.py +1 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +1 -1
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/semantic.py +9 -4
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +8 -0
- agno/knowledge/embedder/openai.py +8 -8
- agno/knowledge/embedder/sentence_transformer.py +6 -2
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/knowledge.py +1618 -318
- agno/knowledge/reader/base.py +6 -2
- agno/knowledge/reader/csv_reader.py +8 -10
- agno/knowledge/reader/docx_reader.py +5 -6
- agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
- agno/knowledge/reader/json_reader.py +5 -4
- agno/knowledge/reader/markdown_reader.py +8 -8
- agno/knowledge/reader/pdf_reader.py +17 -19
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +32 -3
- agno/knowledge/reader/s3_reader.py +3 -3
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +22 -10
- agno/knowledge/reader/web_search_reader.py +1 -48
- agno/knowledge/reader/website_reader.py +10 -10
- agno/knowledge/reader/wikipedia_reader.py +33 -1
- agno/knowledge/types.py +1 -0
- agno/knowledge/utils.py +72 -7
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +544 -83
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +515 -40
- agno/models/aws/bedrock.py +102 -21
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +41 -19
- agno/models/azure/openai_chat.py +39 -8
- agno/models/base.py +1249 -525
- agno/models/cerebras/cerebras.py +91 -21
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +40 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +877 -80
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +51 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +44 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +28 -5
- agno/models/meta/llama.py +47 -14
- agno/models/meta/llama_openai.py +22 -17
- agno/models/mistral/mistral.py +8 -4
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/chat.py +24 -8
- agno/models/openai/chat.py +104 -29
- agno/models/openai/responses.py +101 -81
- agno/models/openrouter/openrouter.py +60 -3
- agno/models/perplexity/perplexity.py +17 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +24 -4
- agno/models/response.py +73 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +549 -152
- agno/os/auth.py +190 -3
- agno/os/config.py +23 -0
- agno/os/interfaces/a2a/router.py +8 -11
- agno/os/interfaces/a2a/utils.py +1 -1
- agno/os/interfaces/agui/router.py +18 -3
- agno/os/interfaces/agui/utils.py +152 -39
- agno/os/interfaces/slack/router.py +55 -37
- agno/os/interfaces/slack/slack.py +9 -1
- agno/os/interfaces/whatsapp/router.py +0 -1
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/mcp.py +110 -52
- agno/os/middleware/__init__.py +2 -0
- agno/os/middleware/jwt.py +676 -112
- agno/os/router.py +40 -1478
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/evals.py +96 -39
- agno/os/routers/evals/schemas.py +65 -33
- agno/os/routers/evals/utils.py +80 -10
- agno/os/routers/health.py +10 -4
- agno/os/routers/knowledge/knowledge.py +196 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +279 -52
- agno/os/routers/memory/schemas.py +46 -17
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +462 -34
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +256 -693
- agno/os/scopes.py +469 -0
- agno/os/utils.py +514 -36
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +155 -32
- agno/run/base.py +55 -3
- agno/run/requirement.py +181 -0
- agno/run/team.py +125 -38
- agno/run/workflow.py +72 -18
- agno/session/agent.py +102 -89
- agno/session/summary.py +56 -15
- agno/session/team.py +164 -90
- agno/session/workflow.py +405 -40
- agno/table.py +10 -0
- agno/team/team.py +3974 -1903
- agno/tools/dalle.py +2 -4
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +16 -10
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +193 -38
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +271 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +76 -36
- agno/tools/redshift.py +406 -0
- agno/tools/scrapegraph.py +1 -1
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +18 -3
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +146 -0
- agno/tools/toolkit.py +25 -0
- agno/tools/workflow.py +8 -1
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +151 -3
- agno/utils/gemini.py +15 -5
- agno/utils/hooks.py +118 -4
- agno/utils/http.py +113 -2
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +187 -1
- agno/utils/merge_dict.py +3 -3
- agno/utils/message.py +60 -0
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +49 -14
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/print_response/agent.py +109 -16
- agno/utils/print_response/team.py +223 -30
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/streamlit.py +1 -1
- agno/utils/team.py +98 -9
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +39 -7
- agno/vectordb/cassandra/cassandra.py +21 -5
- agno/vectordb/chroma/chromadb.py +43 -12
- agno/vectordb/clickhouse/clickhousedb.py +21 -5
- agno/vectordb/couchbase/couchbase.py +29 -5
- agno/vectordb/lancedb/lance_db.py +92 -181
- agno/vectordb/langchaindb/langchaindb.py +24 -4
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/llamaindexdb.py +25 -5
- agno/vectordb/milvus/milvus.py +50 -37
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +36 -30
- agno/vectordb/pgvector/pgvector.py +201 -77
- agno/vectordb/pineconedb/pineconedb.py +41 -23
- agno/vectordb/qdrant/qdrant.py +67 -54
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/singlestore.py +50 -29
- agno/vectordb/surrealdb/surrealdb.py +31 -41
- agno/vectordb/upstashdb/upstashdb.py +34 -6
- agno/vectordb/weaviate/weaviate.py +53 -14
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +120 -18
- agno/workflow/loop.py +77 -10
- agno/workflow/parallel.py +231 -143
- agno/workflow/router.py +118 -17
- agno/workflow/step.py +609 -170
- agno/workflow/steps.py +73 -6
- agno/workflow/types.py +96 -21
- agno/workflow/workflow.py +2039 -262
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
- agno-2.3.13.dist-info/RECORD +613 -0
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -679
- agno/tools/memori.py +0 -339
- agno-2.1.2.dist-info/RECORD +0 -543
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
|
|
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
|
|
|
6
|
-
from agno.db.base import BaseDb, SessionType
|
|
8
|
+
from agno.db.base import AsyncBaseDb, BaseDb, SessionType
|
|
7
9
|
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,17 +22,21 @@ 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
|
|
|
31
36
|
|
|
32
|
-
def get_session_router(
|
|
37
|
+
def get_session_router(
|
|
38
|
+
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], settings: AgnoAPISettings = AgnoAPISettings()
|
|
39
|
+
) -> APIRouter:
|
|
33
40
|
"""Create session router with comprehensive OpenAPI documentation for session management endpoints."""
|
|
34
41
|
session_router = APIRouter(
|
|
35
42
|
dependencies=[Depends(get_authentication_dependency(settings))],
|
|
@@ -45,7 +52,7 @@ def get_session_router(dbs: dict[str, BaseDb], settings: AgnoAPISettings = AgnoA
|
|
|
45
52
|
return attach_routes(router=session_router, dbs=dbs)
|
|
46
53
|
|
|
47
54
|
|
|
48
|
-
def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
55
|
+
def attach_routes(router: APIRouter, dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]]) -> APIRouter:
|
|
49
56
|
@router.get(
|
|
50
57
|
"/sessions",
|
|
51
58
|
response_model=PaginatedResponse[SessionSchema],
|
|
@@ -82,6 +89,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
82
89
|
},
|
|
83
90
|
},
|
|
84
91
|
400: {"description": "Invalid session type or filter parameters", "model": BadRequestResponse},
|
|
92
|
+
404: {"description": "Not found", "model": NotFoundResponse},
|
|
85
93
|
422: {"description": "Validation error in query parameters", "model": ValidationErrorResponse},
|
|
86
94
|
},
|
|
87
95
|
)
|
|
@@ -102,23 +110,41 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
102
110
|
sort_by: Optional[str] = Query(default="created_at", description="Field to sort sessions by"),
|
|
103
111
|
sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
|
|
104
112
|
db_id: Optional[str] = Query(default=None, description="Database ID to query sessions from"),
|
|
113
|
+
table: Optional[str] = Query(default=None, description="The database table to use"),
|
|
105
114
|
) -> PaginatedResponse[SessionSchema]:
|
|
106
|
-
|
|
115
|
+
try:
|
|
116
|
+
db = await get_db(dbs, db_id, table)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
raise HTTPException(status_code=404, detail=f"{e}")
|
|
107
119
|
|
|
108
120
|
if hasattr(request.state, "user_id"):
|
|
109
121
|
user_id = request.state.user_id
|
|
110
122
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
123
|
+
if isinstance(db, AsyncBaseDb):
|
|
124
|
+
db = cast(AsyncBaseDb, db)
|
|
125
|
+
sessions, total_count = await db.get_sessions(
|
|
126
|
+
session_type=session_type,
|
|
127
|
+
component_id=component_id,
|
|
128
|
+
user_id=user_id,
|
|
129
|
+
session_name=session_name,
|
|
130
|
+
limit=limit,
|
|
131
|
+
page=page,
|
|
132
|
+
sort_by=sort_by,
|
|
133
|
+
sort_order=sort_order,
|
|
134
|
+
deserialize=False,
|
|
135
|
+
)
|
|
136
|
+
else:
|
|
137
|
+
sessions, total_count = db.get_sessions( # type: ignore
|
|
138
|
+
session_type=session_type,
|
|
139
|
+
component_id=component_id,
|
|
140
|
+
user_id=user_id,
|
|
141
|
+
session_name=session_name,
|
|
142
|
+
limit=limit,
|
|
143
|
+
page=page,
|
|
144
|
+
sort_by=sort_by,
|
|
145
|
+
sort_order=sort_order,
|
|
146
|
+
deserialize=False,
|
|
147
|
+
)
|
|
122
148
|
|
|
123
149
|
return PaginatedResponse(
|
|
124
150
|
data=[SessionSchema.from_dict(session) for session in sessions], # type: ignore
|
|
@@ -130,6 +156,133 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
130
156
|
),
|
|
131
157
|
)
|
|
132
158
|
|
|
159
|
+
@router.post(
|
|
160
|
+
"/sessions",
|
|
161
|
+
response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
|
|
162
|
+
status_code=201,
|
|
163
|
+
operation_id="create_session",
|
|
164
|
+
summary="Create New Session",
|
|
165
|
+
description=(
|
|
166
|
+
"Create a new empty session with optional configuration. "
|
|
167
|
+
"Useful for pre-creating sessions with specific session_state, metadata, or other properties "
|
|
168
|
+
"before running any agent/team/workflow interactions. "
|
|
169
|
+
"The session can later be used by providing its session_id in run requests."
|
|
170
|
+
),
|
|
171
|
+
responses={
|
|
172
|
+
201: {
|
|
173
|
+
"description": "Session created successfully",
|
|
174
|
+
"content": {
|
|
175
|
+
"application/json": {
|
|
176
|
+
"examples": {
|
|
177
|
+
"agent_session_example": {
|
|
178
|
+
"summary": "Example created agent session",
|
|
179
|
+
"value": {
|
|
180
|
+
"user_id": "user-123",
|
|
181
|
+
"agent_session_id": "new-session-id",
|
|
182
|
+
"session_id": "new-session-id",
|
|
183
|
+
"session_name": "New Session",
|
|
184
|
+
"session_state": {"key": "value"},
|
|
185
|
+
"metadata": {"key": "value"},
|
|
186
|
+
"agent_id": "agent-1",
|
|
187
|
+
"created_at": "2025-10-21T12:00:00Z",
|
|
188
|
+
"updated_at": "2025-10-21T12:00:00Z",
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
400: {"description": "Invalid request parameters", "model": BadRequestResponse},
|
|
196
|
+
422: {"description": "Validation error", "model": ValidationErrorResponse},
|
|
197
|
+
500: {"description": "Failed to create session", "model": InternalServerErrorResponse},
|
|
198
|
+
},
|
|
199
|
+
)
|
|
200
|
+
async def create_session(
|
|
201
|
+
request: Request,
|
|
202
|
+
session_type: SessionType = Query(
|
|
203
|
+
default=SessionType.AGENT, alias="type", description="Type of session to create (agent, team, or workflow)"
|
|
204
|
+
),
|
|
205
|
+
create_session_request: CreateSessionRequest = Body(
|
|
206
|
+
default=CreateSessionRequest(), description="Session configuration data"
|
|
207
|
+
),
|
|
208
|
+
db_id: Optional[str] = Query(default=None, description="Database ID to create session in"),
|
|
209
|
+
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
210
|
+
db = await get_db(dbs, db_id)
|
|
211
|
+
|
|
212
|
+
# Get user_id from request state if available (from auth middleware)
|
|
213
|
+
user_id = create_session_request.user_id
|
|
214
|
+
if hasattr(request.state, "user_id"):
|
|
215
|
+
user_id = request.state.user_id
|
|
216
|
+
|
|
217
|
+
# Generate session_id if not provided
|
|
218
|
+
session_id = create_session_request.session_id or str(uuid4())
|
|
219
|
+
|
|
220
|
+
# Prepare session_data with session_state and session_name
|
|
221
|
+
session_data: dict[str, Any] = {}
|
|
222
|
+
if create_session_request.session_state is not None:
|
|
223
|
+
session_data["session_state"] = create_session_request.session_state
|
|
224
|
+
if create_session_request.session_name is not None:
|
|
225
|
+
session_data["session_name"] = create_session_request.session_name
|
|
226
|
+
|
|
227
|
+
current_time = int(time.time())
|
|
228
|
+
|
|
229
|
+
# Create the appropriate session type
|
|
230
|
+
session: Union[AgentSession, TeamSession, WorkflowSession]
|
|
231
|
+
if session_type == SessionType.AGENT:
|
|
232
|
+
session = AgentSession(
|
|
233
|
+
session_id=session_id,
|
|
234
|
+
agent_id=create_session_request.agent_id,
|
|
235
|
+
user_id=user_id,
|
|
236
|
+
session_data=session_data if session_data else None,
|
|
237
|
+
metadata=create_session_request.metadata,
|
|
238
|
+
created_at=current_time,
|
|
239
|
+
updated_at=current_time,
|
|
240
|
+
)
|
|
241
|
+
elif session_type == SessionType.TEAM:
|
|
242
|
+
session = TeamSession(
|
|
243
|
+
session_id=session_id,
|
|
244
|
+
team_id=create_session_request.team_id,
|
|
245
|
+
user_id=user_id,
|
|
246
|
+
session_data=session_data if session_data else None,
|
|
247
|
+
metadata=create_session_request.metadata,
|
|
248
|
+
created_at=current_time,
|
|
249
|
+
updated_at=current_time,
|
|
250
|
+
)
|
|
251
|
+
elif session_type == SessionType.WORKFLOW:
|
|
252
|
+
session = WorkflowSession(
|
|
253
|
+
session_id=session_id,
|
|
254
|
+
workflow_id=create_session_request.workflow_id,
|
|
255
|
+
user_id=user_id,
|
|
256
|
+
session_data=session_data if session_data else None,
|
|
257
|
+
metadata=create_session_request.metadata,
|
|
258
|
+
created_at=current_time,
|
|
259
|
+
updated_at=current_time,
|
|
260
|
+
)
|
|
261
|
+
else:
|
|
262
|
+
raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
|
|
263
|
+
|
|
264
|
+
# Upsert the session to the database
|
|
265
|
+
try:
|
|
266
|
+
if isinstance(db, AsyncBaseDb):
|
|
267
|
+
db = cast(AsyncBaseDb, db)
|
|
268
|
+
created_session = await db.upsert_session(session, deserialize=True)
|
|
269
|
+
else:
|
|
270
|
+
created_session = db.upsert_session(session, deserialize=True)
|
|
271
|
+
|
|
272
|
+
if not created_session:
|
|
273
|
+
raise HTTPException(status_code=500, detail="Failed to create session")
|
|
274
|
+
|
|
275
|
+
# Return appropriate schema based on session type
|
|
276
|
+
if session_type == SessionType.AGENT:
|
|
277
|
+
return AgentSessionDetailSchema.from_session(created_session) # type: ignore
|
|
278
|
+
elif session_type == SessionType.TEAM:
|
|
279
|
+
return TeamSessionDetailSchema.from_session(created_session) # type: ignore
|
|
280
|
+
else:
|
|
281
|
+
return WorkflowSessionDetailSchema.from_session(created_session) # type: ignore
|
|
282
|
+
except Exception as e:
|
|
283
|
+
logger.error(f"Error creating session: {e}")
|
|
284
|
+
raise HTTPException(status_code=500, detail=f"Failed to create session: {str(e)}")
|
|
285
|
+
|
|
133
286
|
@router.get(
|
|
134
287
|
"/sessions/{session_id}",
|
|
135
288
|
response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
|
|
@@ -225,13 +378,18 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
225
378
|
),
|
|
226
379
|
user_id: Optional[str] = Query(default=None, description="User ID to query session from"),
|
|
227
380
|
db_id: Optional[str] = Query(default=None, description="Database ID to query session from"),
|
|
381
|
+
table: Optional[str] = Query(default=None, description="Table to query session from"),
|
|
228
382
|
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
229
|
-
db = get_db(dbs, db_id)
|
|
383
|
+
db = await get_db(dbs, db_id, table)
|
|
230
384
|
|
|
231
385
|
if hasattr(request.state, "user_id"):
|
|
232
386
|
user_id = request.state.user_id
|
|
233
387
|
|
|
234
|
-
|
|
388
|
+
if isinstance(db, AsyncBaseDb):
|
|
389
|
+
db = cast(AsyncBaseDb, db)
|
|
390
|
+
session = await db.get_session(session_id=session_id, session_type=session_type, user_id=user_id)
|
|
391
|
+
else:
|
|
392
|
+
session = db.get_session(session_id=session_id, session_type=session_type, user_id=user_id)
|
|
235
393
|
if not session:
|
|
236
394
|
raise HTTPException(
|
|
237
395
|
status_code=404, detail=f"{session_type.value.title()} Session with id '{session_id}' not found"
|
|
@@ -251,8 +409,9 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
251
409
|
operation_id="get_session_runs",
|
|
252
410
|
summary="Get Session Runs",
|
|
253
411
|
description=(
|
|
254
|
-
"Retrieve all runs (executions) for a specific session
|
|
255
|
-
"interactions or executions within a session.
|
|
412
|
+
"Retrieve all runs (executions) for a specific session with optional timestamp filtering. "
|
|
413
|
+
"Runs represent individual interactions or executions within a session. "
|
|
414
|
+
"Response schema varies based on session type."
|
|
256
415
|
),
|
|
257
416
|
responses={
|
|
258
417
|
200: {
|
|
@@ -366,27 +525,68 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
366
525
|
default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
|
|
367
526
|
),
|
|
368
527
|
user_id: Optional[str] = Query(default=None, description="User ID to query runs from"),
|
|
528
|
+
created_after: Optional[int] = Query(
|
|
529
|
+
default=None,
|
|
530
|
+
description="Filter runs created after this Unix timestamp (epoch time in seconds)",
|
|
531
|
+
),
|
|
532
|
+
created_before: Optional[int] = Query(
|
|
533
|
+
default=None,
|
|
534
|
+
description="Filter runs created before this Unix timestamp (epoch time in seconds)",
|
|
535
|
+
),
|
|
369
536
|
db_id: Optional[str] = Query(default=None, description="Database ID to query runs from"),
|
|
537
|
+
table: Optional[str] = Query(default=None, description="Table to query runs from"),
|
|
370
538
|
) -> List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]]:
|
|
371
|
-
db = get_db(dbs, db_id)
|
|
539
|
+
db = await get_db(dbs, db_id, table)
|
|
372
540
|
|
|
373
541
|
if hasattr(request.state, "user_id"):
|
|
374
542
|
user_id = request.state.user_id
|
|
375
543
|
|
|
376
|
-
|
|
544
|
+
# Use timestamp filters directly (already in epoch format)
|
|
545
|
+
start_timestamp = created_after
|
|
546
|
+
end_timestamp = created_before
|
|
547
|
+
|
|
548
|
+
if isinstance(db, AsyncBaseDb):
|
|
549
|
+
db = cast(AsyncBaseDb, db)
|
|
550
|
+
session = await db.get_session(
|
|
551
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
|
|
552
|
+
)
|
|
553
|
+
else:
|
|
554
|
+
session = db.get_session(
|
|
555
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
|
|
556
|
+
)
|
|
557
|
+
|
|
377
558
|
if not session:
|
|
378
559
|
raise HTTPException(status_code=404, detail=f"Session with ID {session_id} not found")
|
|
379
560
|
|
|
380
561
|
runs = session.get("runs") # type: ignore
|
|
381
562
|
if not runs:
|
|
382
|
-
|
|
563
|
+
return []
|
|
564
|
+
|
|
565
|
+
# Filter runs by timestamp if specified
|
|
566
|
+
# TODO: Move this filtering into the DB layer
|
|
567
|
+
filtered_runs = []
|
|
568
|
+
for run in runs:
|
|
569
|
+
if start_timestamp or end_timestamp:
|
|
570
|
+
run_created_at = run.get("created_at")
|
|
571
|
+
if run_created_at:
|
|
572
|
+
# created_at is stored as epoch int
|
|
573
|
+
if start_timestamp and run_created_at < start_timestamp:
|
|
574
|
+
continue
|
|
575
|
+
if end_timestamp and run_created_at > end_timestamp:
|
|
576
|
+
continue
|
|
577
|
+
|
|
578
|
+
filtered_runs.append(run)
|
|
579
|
+
|
|
580
|
+
if not filtered_runs:
|
|
581
|
+
return []
|
|
582
|
+
|
|
583
|
+
run_responses: List[Union[RunSchema, TeamRunSchema, WorkflowRunSchema]] = []
|
|
383
584
|
|
|
384
585
|
if session_type == SessionType.AGENT:
|
|
385
|
-
return [RunSchema.from_dict(run) for run in
|
|
586
|
+
return [RunSchema.from_dict(run) for run in filtered_runs]
|
|
386
587
|
|
|
387
588
|
elif session_type == SessionType.TEAM:
|
|
388
|
-
|
|
389
|
-
for run in runs:
|
|
589
|
+
for run in filtered_runs:
|
|
390
590
|
if run.get("agent_id") is not None:
|
|
391
591
|
run_responses.append(RunSchema.from_dict(run))
|
|
392
592
|
elif run.get("team_id") is not None:
|
|
@@ -394,8 +594,7 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
394
594
|
return run_responses
|
|
395
595
|
|
|
396
596
|
elif session_type == SessionType.WORKFLOW:
|
|
397
|
-
|
|
398
|
-
for run in runs:
|
|
597
|
+
for run in filtered_runs:
|
|
399
598
|
if run.get("workflow_id") is not None:
|
|
400
599
|
run_responses.append(WorkflowRunSchema.from_dict(run))
|
|
401
600
|
elif run.get("team_id") is not None:
|
|
@@ -406,6 +605,93 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
406
605
|
else:
|
|
407
606
|
raise HTTPException(status_code=400, detail=f"Invalid session type: {session_type}")
|
|
408
607
|
|
|
608
|
+
@router.get(
|
|
609
|
+
"/sessions/{session_id}/runs/{run_id}",
|
|
610
|
+
response_model=Union[RunSchema, TeamRunSchema, WorkflowRunSchema],
|
|
611
|
+
status_code=200,
|
|
612
|
+
operation_id="get_session_run",
|
|
613
|
+
summary="Get Run by ID",
|
|
614
|
+
description=(
|
|
615
|
+
"Retrieve a specific run by its ID from a session. Response schema varies based on the "
|
|
616
|
+
"run type (agent run, team run, or workflow run)."
|
|
617
|
+
),
|
|
618
|
+
responses={
|
|
619
|
+
200: {
|
|
620
|
+
"description": "Run retrieved successfully",
|
|
621
|
+
"content": {
|
|
622
|
+
"application/json": {
|
|
623
|
+
"examples": {
|
|
624
|
+
"agent_run": {
|
|
625
|
+
"summary": "Example agent run",
|
|
626
|
+
"value": {
|
|
627
|
+
"run_id": "fcdf50f0-7c32-4593-b2ef-68a558774340",
|
|
628
|
+
"parent_run_id": "80056af0-c7a5-4d69-b6a2-c3eba9f040e0",
|
|
629
|
+
"agent_id": "basic-agent",
|
|
630
|
+
"user_id": "user_123",
|
|
631
|
+
"run_input": "Which tools do you have access to?",
|
|
632
|
+
"content": "I don't have access to external tools.",
|
|
633
|
+
"created_at": 1728499200,
|
|
634
|
+
},
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
404: {"description": "Session or run not found", "model": NotFoundResponse},
|
|
641
|
+
422: {"description": "Invalid session type", "model": ValidationErrorResponse},
|
|
642
|
+
},
|
|
643
|
+
)
|
|
644
|
+
async def get_session_run(
|
|
645
|
+
request: Request,
|
|
646
|
+
session_id: str = Path(description="Session ID to get run from"),
|
|
647
|
+
run_id: str = Path(description="Run ID to retrieve"),
|
|
648
|
+
session_type: SessionType = Query(
|
|
649
|
+
default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
|
|
650
|
+
),
|
|
651
|
+
user_id: Optional[str] = Query(default=None, description="User ID to query run from"),
|
|
652
|
+
db_id: Optional[str] = Query(default=None, description="Database ID to query run from"),
|
|
653
|
+
) -> Union[RunSchema, TeamRunSchema, WorkflowRunSchema]:
|
|
654
|
+
db = await get_db(dbs, db_id)
|
|
655
|
+
|
|
656
|
+
if hasattr(request.state, "user_id"):
|
|
657
|
+
user_id = request.state.user_id
|
|
658
|
+
|
|
659
|
+
if isinstance(db, AsyncBaseDb):
|
|
660
|
+
db = cast(AsyncBaseDb, db)
|
|
661
|
+
session = await db.get_session(
|
|
662
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
|
|
663
|
+
)
|
|
664
|
+
else:
|
|
665
|
+
session = db.get_session(
|
|
666
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=False
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
if not session:
|
|
670
|
+
raise HTTPException(status_code=404, detail=f"Session with ID {session_id} not found")
|
|
671
|
+
|
|
672
|
+
runs = session.get("runs") # type: ignore
|
|
673
|
+
if not runs:
|
|
674
|
+
raise HTTPException(status_code=404, detail=f"Session with ID {session_id} has no runs")
|
|
675
|
+
|
|
676
|
+
# Find the specific run
|
|
677
|
+
# TODO: Move this filtering into the DB layer
|
|
678
|
+
target_run = None
|
|
679
|
+
for run in runs:
|
|
680
|
+
if run.get("run_id") == run_id:
|
|
681
|
+
target_run = run
|
|
682
|
+
break
|
|
683
|
+
|
|
684
|
+
if not target_run:
|
|
685
|
+
raise HTTPException(status_code=404, detail=f"Run with ID {run_id} not found in session {session_id}")
|
|
686
|
+
|
|
687
|
+
# Return the appropriate schema based on run type
|
|
688
|
+
if target_run.get("workflow_id") is not None:
|
|
689
|
+
return WorkflowRunSchema.from_dict(target_run)
|
|
690
|
+
elif target_run.get("team_id") is not None:
|
|
691
|
+
return TeamRunSchema.from_dict(target_run)
|
|
692
|
+
else:
|
|
693
|
+
return RunSchema.from_dict(target_run)
|
|
694
|
+
|
|
409
695
|
@router.delete(
|
|
410
696
|
"/sessions/{session_id}",
|
|
411
697
|
status_code=204,
|
|
@@ -423,9 +709,14 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
423
709
|
async def delete_session(
|
|
424
710
|
session_id: str = Path(description="Session ID to delete"),
|
|
425
711
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
712
|
+
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
426
713
|
) -> None:
|
|
427
|
-
db = get_db(dbs, db_id)
|
|
428
|
-
db
|
|
714
|
+
db = await get_db(dbs, db_id, table)
|
|
715
|
+
if isinstance(db, AsyncBaseDb):
|
|
716
|
+
db = cast(AsyncBaseDb, db)
|
|
717
|
+
await db.delete_session(session_id=session_id)
|
|
718
|
+
else:
|
|
719
|
+
db.delete_session(session_id=session_id)
|
|
429
720
|
|
|
430
721
|
@router.delete(
|
|
431
722
|
"/sessions",
|
|
@@ -451,12 +742,17 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
451
742
|
default=SessionType.AGENT, description="Default session type filter", alias="type"
|
|
452
743
|
),
|
|
453
744
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for deletion"),
|
|
745
|
+
table: Optional[str] = Query(default=None, description="Table to use for deletion"),
|
|
454
746
|
) -> None:
|
|
455
747
|
if len(request.session_ids) != len(request.session_types):
|
|
456
748
|
raise HTTPException(status_code=400, detail="Session IDs and session types must have the same length")
|
|
457
749
|
|
|
458
|
-
db = get_db(dbs, db_id)
|
|
459
|
-
db
|
|
750
|
+
db = await get_db(dbs, db_id, table)
|
|
751
|
+
if isinstance(db, AsyncBaseDb):
|
|
752
|
+
db = cast(AsyncBaseDb, db)
|
|
753
|
+
await db.delete_sessions(session_ids=request.session_ids)
|
|
754
|
+
else:
|
|
755
|
+
db.delete_sessions(session_ids=request.session_ids)
|
|
460
756
|
|
|
461
757
|
@router.post(
|
|
462
758
|
"/sessions/{session_id}/rename",
|
|
@@ -553,9 +849,16 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
553
849
|
),
|
|
554
850
|
session_name: str = Body(embed=True, description="New name for the session"),
|
|
555
851
|
db_id: Optional[str] = Query(default=None, description="Database ID to use for rename operation"),
|
|
852
|
+
table: Optional[str] = Query(default=None, description="Table to use for rename operation"),
|
|
556
853
|
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
557
|
-
db = get_db(dbs, db_id)
|
|
558
|
-
|
|
854
|
+
db = await get_db(dbs, db_id, table)
|
|
855
|
+
if isinstance(db, AsyncBaseDb):
|
|
856
|
+
db = cast(AsyncBaseDb, db)
|
|
857
|
+
session = await db.rename_session(
|
|
858
|
+
session_id=session_id, session_type=session_type, session_name=session_name
|
|
859
|
+
)
|
|
860
|
+
else:
|
|
861
|
+
session = db.rename_session(session_id=session_id, session_type=session_type, session_name=session_name)
|
|
559
862
|
if not session:
|
|
560
863
|
raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
|
|
561
864
|
|
|
@@ -566,4 +869,129 @@ def attach_routes(router: APIRouter, dbs: dict[str, BaseDb]) -> APIRouter:
|
|
|
566
869
|
else:
|
|
567
870
|
return WorkflowSessionDetailSchema.from_session(session) # type: ignore
|
|
568
871
|
|
|
872
|
+
@router.patch(
|
|
873
|
+
"/sessions/{session_id}",
|
|
874
|
+
response_model=Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema],
|
|
875
|
+
status_code=200,
|
|
876
|
+
operation_id="update_session",
|
|
877
|
+
summary="Update Session",
|
|
878
|
+
description=(
|
|
879
|
+
"Update session properties such as session_name, session_state, metadata, or summary. "
|
|
880
|
+
"Use this endpoint to modify the session name, update state, add metadata, or update the session summary."
|
|
881
|
+
),
|
|
882
|
+
responses={
|
|
883
|
+
200: {
|
|
884
|
+
"description": "Session updated successfully",
|
|
885
|
+
"content": {
|
|
886
|
+
"application/json": {
|
|
887
|
+
"examples": {
|
|
888
|
+
"update_summary": {
|
|
889
|
+
"summary": "Update session summary",
|
|
890
|
+
"value": {
|
|
891
|
+
"summary": {
|
|
892
|
+
"summary": "The user discussed project planning with the agent.",
|
|
893
|
+
"updated_at": "2025-10-21T14:30:00Z",
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
},
|
|
897
|
+
"update_metadata": {
|
|
898
|
+
"summary": "Update session metadata",
|
|
899
|
+
"value": {
|
|
900
|
+
"metadata": {
|
|
901
|
+
"tags": ["planning", "project"],
|
|
902
|
+
"priority": "high",
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
"update_session_name": {
|
|
907
|
+
"summary": "Update session name",
|
|
908
|
+
"value": {"session_name": "Updated Session Name"},
|
|
909
|
+
},
|
|
910
|
+
"update_session_state": {
|
|
911
|
+
"summary": "Update session state",
|
|
912
|
+
"value": {
|
|
913
|
+
"session_state": {
|
|
914
|
+
"step": "completed",
|
|
915
|
+
"context": "Project planning finished",
|
|
916
|
+
"progress": 100,
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
404: {"description": "Session not found", "model": NotFoundResponse},
|
|
925
|
+
422: {"description": "Invalid request", "model": ValidationErrorResponse},
|
|
926
|
+
500: {"description": "Failed to update session", "model": InternalServerErrorResponse},
|
|
927
|
+
},
|
|
928
|
+
)
|
|
929
|
+
async def update_session(
|
|
930
|
+
request: Request,
|
|
931
|
+
session_id: str = Path(description="Session ID to update"),
|
|
932
|
+
session_type: SessionType = Query(
|
|
933
|
+
default=SessionType.AGENT, description="Session type (agent, team, or workflow)", alias="type"
|
|
934
|
+
),
|
|
935
|
+
update_data: UpdateSessionRequest = Body(description="Session update data"),
|
|
936
|
+
user_id: Optional[str] = Query(default=None, description="User ID"),
|
|
937
|
+
db_id: Optional[str] = Query(default=None, description="Database ID to use for update operation"),
|
|
938
|
+
) -> Union[AgentSessionDetailSchema, TeamSessionDetailSchema, WorkflowSessionDetailSchema]:
|
|
939
|
+
db = await get_db(dbs, db_id)
|
|
940
|
+
|
|
941
|
+
if hasattr(request.state, "user_id"):
|
|
942
|
+
user_id = request.state.user_id
|
|
943
|
+
|
|
944
|
+
# Get the existing session
|
|
945
|
+
if isinstance(db, AsyncBaseDb):
|
|
946
|
+
db = cast(AsyncBaseDb, db)
|
|
947
|
+
existing_session = await db.get_session(
|
|
948
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
|
|
949
|
+
)
|
|
950
|
+
else:
|
|
951
|
+
existing_session = db.get_session(
|
|
952
|
+
session_id=session_id, session_type=session_type, user_id=user_id, deserialize=True
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
if not existing_session:
|
|
956
|
+
raise HTTPException(status_code=404, detail=f"Session with id '{session_id}' not found")
|
|
957
|
+
|
|
958
|
+
# Update session properties
|
|
959
|
+
# Handle session_name - stored in session_data
|
|
960
|
+
if update_data.session_name is not None:
|
|
961
|
+
if existing_session.session_data is None: # type: ignore
|
|
962
|
+
existing_session.session_data = {} # type: ignore
|
|
963
|
+
existing_session.session_data["session_name"] = update_data.session_name # type: ignore
|
|
964
|
+
|
|
965
|
+
# Handle session_state - stored in session_data
|
|
966
|
+
if update_data.session_state is not None:
|
|
967
|
+
if existing_session.session_data is None: # type: ignore
|
|
968
|
+
existing_session.session_data = {} # type: ignore
|
|
969
|
+
existing_session.session_data["session_state"] = update_data.session_state # type: ignore
|
|
970
|
+
|
|
971
|
+
if update_data.metadata is not None:
|
|
972
|
+
existing_session.metadata = update_data.metadata # type: ignore
|
|
973
|
+
|
|
974
|
+
if update_data.summary is not None:
|
|
975
|
+
from agno.session.summary import SessionSummary
|
|
976
|
+
|
|
977
|
+
existing_session.summary = SessionSummary.from_dict(update_data.summary) # type: ignore
|
|
978
|
+
|
|
979
|
+
# Upsert the updated session
|
|
980
|
+
if isinstance(db, AsyncBaseDb):
|
|
981
|
+
db = cast(AsyncBaseDb, db)
|
|
982
|
+
updated_session = await db.upsert_session(existing_session, deserialize=True) # type: ignore
|
|
983
|
+
else:
|
|
984
|
+
updated_session = db.upsert_session(existing_session, deserialize=True) # type: ignore
|
|
985
|
+
|
|
986
|
+
if not updated_session:
|
|
987
|
+
raise HTTPException(status_code=500, detail="Failed to update session")
|
|
988
|
+
|
|
989
|
+
# Return appropriate schema based on session type
|
|
990
|
+
if session_type == SessionType.AGENT:
|
|
991
|
+
return AgentSessionDetailSchema.from_session(updated_session) # type: ignore
|
|
992
|
+
elif session_type == SessionType.TEAM:
|
|
993
|
+
return TeamSessionDetailSchema.from_session(updated_session) # type: ignore
|
|
994
|
+
else:
|
|
995
|
+
return WorkflowSessionDetailSchema.from_session(updated_session) # type: ignore
|
|
996
|
+
|
|
569
997
|
return router
|