agno 2.0.1__py3-none-any.whl → 2.3.0__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 +6015 -2823
- agno/api/api.py +2 -0
- agno/api/os.py +1 -1
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +385 -6
- agno/db/dynamo/dynamo.py +388 -81
- agno/db/dynamo/schemas.py +47 -10
- agno/db/dynamo/utils.py +63 -4
- agno/db/firestore/firestore.py +435 -64
- agno/db/firestore/schemas.py +11 -0
- agno/db/firestore/utils.py +102 -4
- agno/db/gcs_json/gcs_json_db.py +384 -42
- agno/db/gcs_json/utils.py +60 -26
- agno/db/in_memory/in_memory_db.py +351 -66
- agno/db/in_memory/utils.py +60 -2
- agno/db/json/json_db.py +339 -48
- agno/db/json/utils.py +60 -26
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +510 -37
- 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 +2036 -0
- agno/db/mongo/mongo.py +653 -76
- agno/db/mongo/schemas.py +13 -0
- agno/db/mongo/utils.py +80 -8
- agno/db/mysql/mysql.py +687 -25
- agno/db/mysql/schemas.py +61 -37
- agno/db/mysql/utils.py +60 -2
- agno/db/postgres/__init__.py +2 -1
- agno/db/postgres/async_postgres.py +2001 -0
- agno/db/postgres/postgres.py +676 -57
- agno/db/postgres/schemas.py +43 -18
- agno/db/postgres/utils.py +164 -2
- agno/db/redis/redis.py +344 -38
- agno/db/redis/schemas.py +18 -0
- agno/db/redis/utils.py +60 -2
- agno/db/schemas/__init__.py +2 -1
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/memory.py +13 -0
- agno/db/singlestore/schemas.py +26 -1
- agno/db/singlestore/singlestore.py +687 -53
- agno/db/singlestore/utils.py +60 -2
- agno/db/sqlite/__init__.py +2 -1
- agno/db/sqlite/async_sqlite.py +2371 -0
- agno/db/sqlite/schemas.py +24 -0
- agno/db/sqlite/sqlite.py +774 -85
- agno/db/sqlite/utils.py +168 -5
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1361 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +50 -22
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +68 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/integrations/discord/client.py +1 -0
- agno/knowledge/chunking/agentic.py +13 -10
- agno/knowledge/chunking/fixed.py +1 -1
- agno/knowledge/chunking/semantic.py +40 -8
- agno/knowledge/chunking/strategy.py +59 -15
- agno/knowledge/embedder/aws_bedrock.py +9 -4
- agno/knowledge/embedder/azure_openai.py +54 -0
- agno/knowledge/embedder/base.py +2 -0
- agno/knowledge/embedder/cohere.py +184 -5
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/embedder/google.py +79 -1
- agno/knowledge/embedder/huggingface.py +9 -4
- agno/knowledge/embedder/jina.py +63 -0
- agno/knowledge/embedder/mistral.py +78 -11
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/ollama.py +13 -0
- agno/knowledge/embedder/openai.py +37 -65
- agno/knowledge/embedder/sentence_transformer.py +8 -4
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +69 -16
- agno/knowledge/knowledge.py +594 -186
- agno/knowledge/reader/base.py +9 -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 +290 -0
- agno/knowledge/reader/json_reader.py +6 -5
- agno/knowledge/reader/markdown_reader.py +13 -13
- agno/knowledge/reader/pdf_reader.py +43 -68
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +51 -6
- agno/knowledge/reader/s3_reader.py +3 -15
- agno/knowledge/reader/tavily_reader.py +194 -0
- agno/knowledge/reader/text_reader.py +13 -13
- agno/knowledge/reader/web_search_reader.py +2 -43
- agno/knowledge/reader/website_reader.py +43 -25
- agno/knowledge/reranker/__init__.py +2 -8
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +72 -0
- agno/memory/manager.py +336 -82
- agno/models/aimlapi/aimlapi.py +2 -2
- agno/models/anthropic/claude.py +183 -37
- agno/models/aws/bedrock.py +52 -112
- agno/models/aws/claude.py +33 -1
- agno/models/azure/ai_foundry.py +33 -15
- agno/models/azure/openai_chat.py +25 -8
- agno/models/base.py +999 -519
- agno/models/cerebras/cerebras.py +19 -13
- agno/models/cerebras/cerebras_openai.py +8 -5
- agno/models/cohere/chat.py +27 -1
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/dashscope.py +1 -0
- agno/models/deepinfra/deepinfra.py +2 -2
- agno/models/deepseek/deepseek.py +2 -2
- agno/models/fireworks/fireworks.py +2 -2
- agno/models/google/gemini.py +103 -31
- agno/models/groq/groq.py +28 -11
- agno/models/huggingface/huggingface.py +2 -1
- agno/models/internlm/internlm.py +2 -2
- agno/models/langdb/langdb.py +4 -4
- agno/models/litellm/chat.py +18 -1
- agno/models/litellm/litellm_openai.py +2 -2
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/message.py +139 -0
- agno/models/meta/llama.py +27 -10
- agno/models/meta/llama_openai.py +5 -17
- agno/models/nebius/nebius.py +6 -6
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/nvidia.py +2 -2
- agno/models/ollama/chat.py +59 -5
- agno/models/openai/chat.py +69 -29
- agno/models/openai/responses.py +103 -106
- agno/models/openrouter/openrouter.py +41 -3
- agno/models/perplexity/perplexity.py +4 -5
- agno/models/portkey/portkey.py +3 -3
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +77 -1
- agno/models/sambanova/sambanova.py +2 -2
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/together.py +2 -2
- agno/models/utils.py +254 -8
- agno/models/vercel/v0.py +2 -2
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +96 -0
- agno/models/vllm/vllm.py +1 -0
- agno/models/xai/xai.py +3 -2
- agno/os/app.py +543 -178
- agno/os/auth.py +24 -14
- agno/os/config.py +1 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +250 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/agui.py +23 -7
- agno/os/interfaces/agui/router.py +27 -3
- agno/os/interfaces/agui/utils.py +242 -142
- agno/os/interfaces/base.py +6 -2
- agno/os/interfaces/slack/router.py +81 -23
- agno/os/interfaces/slack/slack.py +29 -14
- agno/os/interfaces/whatsapp/router.py +11 -4
- agno/os/interfaces/whatsapp/whatsapp.py +14 -7
- agno/os/mcp.py +111 -54
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +556 -139
- agno/os/routers/evals/evals.py +71 -34
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/evals/utils.py +6 -5
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/knowledge.py +185 -38
- agno/os/routers/knowledge/schemas.py +82 -22
- agno/os/routers/memory/memory.py +158 -53
- agno/os/routers/memory/schemas.py +20 -16
- agno/os/routers/metrics/metrics.py +20 -8
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +499 -38
- agno/os/schema.py +308 -198
- agno/os/utils.py +401 -41
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +2 -2
- agno/reasoning/deepseek.py +2 -2
- agno/reasoning/default.py +3 -1
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +2 -2
- agno/reasoning/ollama.py +2 -2
- agno/reasoning/openai.py +7 -2
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +248 -94
- agno/run/base.py +44 -5
- agno/run/team.py +238 -97
- agno/run/workflow.py +144 -33
- agno/session/agent.py +105 -89
- agno/session/summary.py +65 -25
- agno/session/team.py +176 -96
- agno/session/workflow.py +406 -40
- agno/team/team.py +3854 -1610
- agno/tools/dalle.py +2 -4
- agno/tools/decorator.py +4 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +14 -7
- agno/tools/eleven_labs.py +23 -25
- agno/tools/exa.py +21 -16
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +250 -30
- agno/tools/gmail.py +238 -14
- agno/tools/google_drive.py +270 -0
- agno/tools/googlecalendar.py +36 -8
- agno/tools/googlesheets.py +20 -5
- agno/tools/jira.py +20 -0
- agno/tools/knowledge.py +3 -3
- 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 +284 -0
- agno/tools/mem0.py +11 -17
- agno/tools/memori.py +1 -53
- agno/tools/memory.py +419 -0
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/notion.py +204 -0
- agno/tools/parallel.py +314 -0
- agno/tools/scrapegraph.py +58 -31
- agno/tools/searxng.py +2 -2
- agno/tools/serper.py +2 -2
- agno/tools/slack.py +18 -3
- agno/tools/spider.py +2 -2
- agno/tools/tavily.py +146 -0
- agno/tools/whatsapp.py +1 -1
- agno/tools/workflow.py +278 -0
- agno/tools/yfinance.py +12 -11
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +27 -0
- agno/utils/common.py +90 -1
- agno/utils/events.py +217 -2
- agno/utils/gemini.py +180 -22
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +111 -0
- agno/utils/knowledge.py +12 -5
- agno/utils/log.py +1 -0
- agno/utils/mcp.py +92 -2
- agno/utils/media.py +188 -10
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +60 -0
- agno/utils/models/claude.py +40 -11
- agno/utils/print_response/agent.py +105 -21
- agno/utils/print_response/team.py +103 -38
- agno/utils/print_response/workflow.py +251 -34
- agno/utils/reasoning.py +22 -1
- agno/utils/serialize.py +32 -0
- agno/utils/streamlit.py +16 -10
- agno/utils/string.py +41 -0
- agno/utils/team.py +98 -9
- agno/utils/tools.py +1 -1
- agno/vectordb/base.py +23 -4
- agno/vectordb/cassandra/cassandra.py +65 -9
- agno/vectordb/chroma/chromadb.py +182 -38
- agno/vectordb/clickhouse/clickhousedb.py +64 -11
- agno/vectordb/couchbase/couchbase.py +105 -10
- agno/vectordb/lancedb/lance_db.py +124 -133
- agno/vectordb/langchaindb/langchaindb.py +25 -7
- agno/vectordb/lightrag/lightrag.py +17 -3
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +46 -7
- agno/vectordb/milvus/milvus.py +126 -9
- agno/vectordb/mongodb/__init__.py +7 -1
- agno/vectordb/mongodb/mongodb.py +112 -7
- agno/vectordb/pgvector/pgvector.py +142 -21
- agno/vectordb/pineconedb/pineconedb.py +80 -8
- agno/vectordb/qdrant/qdrant.py +125 -39
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/singlestore/singlestore.py +111 -25
- agno/vectordb/surrealdb/surrealdb.py +31 -5
- agno/vectordb/upstashdb/upstashdb.py +76 -8
- agno/vectordb/weaviate/weaviate.py +86 -15
- agno/workflow/__init__.py +2 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +112 -18
- agno/workflow/loop.py +69 -10
- agno/workflow/parallel.py +266 -118
- agno/workflow/router.py +110 -17
- agno/workflow/step.py +638 -129
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +61 -23
- agno/workflow/workflow.py +2085 -272
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
- agno-2.3.0.dist-info/RECORD +577 -0
- agno/knowledge/reader/url_reader.py +0 -128
- agno/tools/googlesearch.py +0 -98
- agno/tools/mcp.py +0 -610
- agno/utils/models/aws_claude.py +0 -170
- agno-2.0.1.dist-info/RECORD +0 -515
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/utils/agent.py
ADDED
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
from asyncio import Future, Task
|
|
2
|
+
from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Iterator, List, Optional, Sequence, Union
|
|
3
|
+
|
|
4
|
+
from agno.media import Audio, File, Image, Video
|
|
5
|
+
from agno.models.message import Message
|
|
6
|
+
from agno.models.metrics import Metrics
|
|
7
|
+
from agno.models.response import ModelResponse
|
|
8
|
+
from agno.run.agent import RunEvent, RunInput, RunOutput, RunOutputEvent
|
|
9
|
+
from agno.run.team import RunOutputEvent as TeamRunOutputEvent
|
|
10
|
+
from agno.run.team import TeamRunOutput
|
|
11
|
+
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
12
|
+
from agno.utils.events import (
|
|
13
|
+
create_memory_update_completed_event,
|
|
14
|
+
create_memory_update_started_event,
|
|
15
|
+
create_team_memory_update_completed_event,
|
|
16
|
+
create_team_memory_update_started_event,
|
|
17
|
+
handle_event,
|
|
18
|
+
)
|
|
19
|
+
from agno.utils.log import log_debug, log_warning
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from agno.agent.agent import Agent
|
|
23
|
+
from agno.team.team import Team
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def await_for_background_tasks(
|
|
27
|
+
memory_task: Optional[Task] = None,
|
|
28
|
+
cultural_knowledge_task: Optional[Task] = None,
|
|
29
|
+
) -> None:
|
|
30
|
+
if memory_task is not None:
|
|
31
|
+
try:
|
|
32
|
+
await memory_task
|
|
33
|
+
except Exception as e:
|
|
34
|
+
log_warning(f"Error in memory creation: {str(e)}")
|
|
35
|
+
|
|
36
|
+
if cultural_knowledge_task is not None:
|
|
37
|
+
try:
|
|
38
|
+
await cultural_knowledge_task
|
|
39
|
+
except Exception as e:
|
|
40
|
+
log_warning(f"Error in cultural knowledge creation: {str(e)}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def wait_for_background_tasks(
|
|
44
|
+
memory_future: Optional[Future] = None, cultural_knowledge_future: Optional[Future] = None
|
|
45
|
+
) -> None:
|
|
46
|
+
if memory_future is not None:
|
|
47
|
+
try:
|
|
48
|
+
memory_future.result()
|
|
49
|
+
except Exception as e:
|
|
50
|
+
log_warning(f"Error in memory creation: {str(e)}")
|
|
51
|
+
|
|
52
|
+
# Wait for cultural knowledge creation
|
|
53
|
+
if cultural_knowledge_future is not None:
|
|
54
|
+
try:
|
|
55
|
+
cultural_knowledge_future.result()
|
|
56
|
+
except Exception as e:
|
|
57
|
+
log_warning(f"Error in cultural knowledge creation: {str(e)}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
async def await_for_background_tasks_stream(
|
|
61
|
+
run_response: Union[RunOutput, TeamRunOutput],
|
|
62
|
+
memory_task: Optional[Task] = None,
|
|
63
|
+
cultural_knowledge_task: Optional[Task] = None,
|
|
64
|
+
stream_events: bool = False,
|
|
65
|
+
events_to_skip: Optional[List[RunEvent]] = None,
|
|
66
|
+
store_events: bool = False,
|
|
67
|
+
) -> AsyncIterator[RunOutputEvent]:
|
|
68
|
+
if memory_task is not None:
|
|
69
|
+
if stream_events:
|
|
70
|
+
if isinstance(run_response, TeamRunOutput):
|
|
71
|
+
yield handle_event( # type: ignore
|
|
72
|
+
create_team_memory_update_started_event(from_run_response=run_response),
|
|
73
|
+
run_response,
|
|
74
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
75
|
+
store_events=store_events,
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
yield handle_event( # type: ignore
|
|
79
|
+
create_memory_update_started_event(from_run_response=run_response),
|
|
80
|
+
run_response,
|
|
81
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
82
|
+
store_events=store_events,
|
|
83
|
+
)
|
|
84
|
+
try:
|
|
85
|
+
await memory_task
|
|
86
|
+
except Exception as e:
|
|
87
|
+
log_warning(f"Error in memory creation: {str(e)}")
|
|
88
|
+
if stream_events:
|
|
89
|
+
if isinstance(run_response, TeamRunOutput):
|
|
90
|
+
yield handle_event( # type: ignore
|
|
91
|
+
create_team_memory_update_completed_event(from_run_response=run_response),
|
|
92
|
+
run_response,
|
|
93
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
94
|
+
store_events=store_events,
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
yield handle_event( # type: ignore
|
|
98
|
+
create_memory_update_completed_event(from_run_response=run_response),
|
|
99
|
+
run_response,
|
|
100
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
101
|
+
store_events=store_events,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if cultural_knowledge_task is not None:
|
|
105
|
+
try:
|
|
106
|
+
await cultural_knowledge_task
|
|
107
|
+
except Exception as e:
|
|
108
|
+
log_warning(f"Error in cultural knowledge creation: {str(e)}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def wait_for_background_tasks_stream(
|
|
112
|
+
run_response: Union[TeamRunOutput, RunOutput],
|
|
113
|
+
memory_future: Optional[Future] = None,
|
|
114
|
+
cultural_knowledge_future: Optional[Future] = None,
|
|
115
|
+
stream_events: bool = False,
|
|
116
|
+
events_to_skip: Optional[List[RunEvent]] = None,
|
|
117
|
+
store_events: bool = False,
|
|
118
|
+
) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]:
|
|
119
|
+
if memory_future is not None:
|
|
120
|
+
if stream_events:
|
|
121
|
+
if isinstance(run_response, TeamRunOutput):
|
|
122
|
+
yield handle_event( # type: ignore
|
|
123
|
+
create_team_memory_update_started_event(from_run_response=run_response),
|
|
124
|
+
run_response,
|
|
125
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
126
|
+
store_events=store_events,
|
|
127
|
+
)
|
|
128
|
+
else:
|
|
129
|
+
yield handle_event( # type: ignore
|
|
130
|
+
create_memory_update_started_event(from_run_response=run_response),
|
|
131
|
+
run_response,
|
|
132
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
133
|
+
store_events=store_events,
|
|
134
|
+
)
|
|
135
|
+
try:
|
|
136
|
+
memory_future.result()
|
|
137
|
+
except Exception as e:
|
|
138
|
+
log_warning(f"Error in memory creation: {str(e)}")
|
|
139
|
+
if stream_events:
|
|
140
|
+
if isinstance(run_response, TeamRunOutput):
|
|
141
|
+
yield handle_event( # type: ignore
|
|
142
|
+
create_team_memory_update_completed_event(from_run_response=run_response),
|
|
143
|
+
run_response,
|
|
144
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
145
|
+
store_events=store_events,
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
yield handle_event( # type: ignore
|
|
149
|
+
create_memory_update_completed_event(from_run_response=run_response),
|
|
150
|
+
run_response,
|
|
151
|
+
events_to_skip=events_to_skip, # type: ignore
|
|
152
|
+
store_events=store_events,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Wait for cultural knowledge creation
|
|
156
|
+
if cultural_knowledge_future is not None:
|
|
157
|
+
# TODO: Add events
|
|
158
|
+
try:
|
|
159
|
+
cultural_knowledge_future.result()
|
|
160
|
+
except Exception as e:
|
|
161
|
+
log_warning(f"Error in cultural knowledge creation: {str(e)}")
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def collect_joint_images(
|
|
165
|
+
run_input: Optional[RunInput] = None,
|
|
166
|
+
session: Optional[Union[AgentSession, TeamSession]] = None,
|
|
167
|
+
) -> Optional[Sequence[Image]]:
|
|
168
|
+
"""Collect images from input, session history, and current run response."""
|
|
169
|
+
joint_images: List[Image] = []
|
|
170
|
+
|
|
171
|
+
# 1. Add images from current input
|
|
172
|
+
if run_input and run_input.images:
|
|
173
|
+
joint_images.extend(run_input.images)
|
|
174
|
+
log_debug(f"Added {len(run_input.images)} input images to joint list")
|
|
175
|
+
|
|
176
|
+
# 2. Add images from session history (from both input and generated sources)
|
|
177
|
+
try:
|
|
178
|
+
if session and session.runs:
|
|
179
|
+
for historical_run in session.runs:
|
|
180
|
+
# Add generated images from previous runs
|
|
181
|
+
if historical_run.images:
|
|
182
|
+
joint_images.extend(historical_run.images)
|
|
183
|
+
log_debug(
|
|
184
|
+
f"Added {len(historical_run.images)} generated images from historical run {historical_run.run_id}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Add input images from previous runs
|
|
188
|
+
if historical_run.input and historical_run.input.images:
|
|
189
|
+
joint_images.extend(historical_run.input.images)
|
|
190
|
+
log_debug(
|
|
191
|
+
f"Added {len(historical_run.input.images)} input images from historical run {historical_run.run_id}"
|
|
192
|
+
)
|
|
193
|
+
except Exception as e:
|
|
194
|
+
log_debug(f"Could not access session history for images: {e}")
|
|
195
|
+
|
|
196
|
+
if joint_images:
|
|
197
|
+
log_debug(f"Images Available to Model: {len(joint_images)} images")
|
|
198
|
+
return joint_images if joint_images else None
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def collect_joint_videos(
|
|
202
|
+
run_input: Optional[RunInput] = None,
|
|
203
|
+
session: Optional[Union[AgentSession, TeamSession]] = None,
|
|
204
|
+
) -> Optional[Sequence[Video]]:
|
|
205
|
+
"""Collect videos from input, session history, and current run response."""
|
|
206
|
+
joint_videos: List[Video] = []
|
|
207
|
+
|
|
208
|
+
# 1. Add videos from current input
|
|
209
|
+
if run_input and run_input.videos:
|
|
210
|
+
joint_videos.extend(run_input.videos)
|
|
211
|
+
log_debug(f"Added {len(run_input.videos)} input videos to joint list")
|
|
212
|
+
|
|
213
|
+
# 2. Add videos from session history (from both input and generated sources)
|
|
214
|
+
try:
|
|
215
|
+
if session and session.runs:
|
|
216
|
+
for historical_run in session.runs:
|
|
217
|
+
# Add generated videos from previous runs
|
|
218
|
+
if historical_run.videos:
|
|
219
|
+
joint_videos.extend(historical_run.videos)
|
|
220
|
+
log_debug(
|
|
221
|
+
f"Added {len(historical_run.videos)} generated videos from historical run {historical_run.run_id}"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# Add input videos from previous runs
|
|
225
|
+
if historical_run.input and historical_run.input.videos:
|
|
226
|
+
joint_videos.extend(historical_run.input.videos)
|
|
227
|
+
log_debug(
|
|
228
|
+
f"Added {len(historical_run.input.videos)} input videos from historical run {historical_run.run_id}"
|
|
229
|
+
)
|
|
230
|
+
except Exception as e:
|
|
231
|
+
log_debug(f"Could not access session history for videos: {e}")
|
|
232
|
+
|
|
233
|
+
if joint_videos:
|
|
234
|
+
log_debug(f"Videos Available to Model: {len(joint_videos)} videos")
|
|
235
|
+
return joint_videos if joint_videos else None
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def collect_joint_audios(
|
|
239
|
+
run_input: Optional[RunInput] = None,
|
|
240
|
+
session: Optional[Union[AgentSession, TeamSession]] = None,
|
|
241
|
+
) -> Optional[Sequence[Audio]]:
|
|
242
|
+
"""Collect audios from input, session history, and current run response."""
|
|
243
|
+
joint_audios: List[Audio] = []
|
|
244
|
+
|
|
245
|
+
# 1. Add audios from current input
|
|
246
|
+
if run_input and run_input.audios:
|
|
247
|
+
joint_audios.extend(run_input.audios)
|
|
248
|
+
log_debug(f"Added {len(run_input.audios)} input audios to joint list")
|
|
249
|
+
|
|
250
|
+
# 2. Add audios from session history (from both input and generated sources)
|
|
251
|
+
try:
|
|
252
|
+
if session and session.runs:
|
|
253
|
+
for historical_run in session.runs:
|
|
254
|
+
# Add generated audios from previous runs
|
|
255
|
+
if historical_run.audio:
|
|
256
|
+
joint_audios.extend(historical_run.audio)
|
|
257
|
+
log_debug(
|
|
258
|
+
f"Added {len(historical_run.audio)} generated audios from historical run {historical_run.run_id}"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Add input audios from previous runs
|
|
262
|
+
if historical_run.input and historical_run.input.audios:
|
|
263
|
+
joint_audios.extend(historical_run.input.audios)
|
|
264
|
+
log_debug(
|
|
265
|
+
f"Added {len(historical_run.input.audios)} input audios from historical run {historical_run.run_id}"
|
|
266
|
+
)
|
|
267
|
+
except Exception as e:
|
|
268
|
+
log_debug(f"Could not access session history for audios: {e}")
|
|
269
|
+
|
|
270
|
+
if joint_audios:
|
|
271
|
+
log_debug(f"Audios Available to Model: {len(joint_audios)} audios")
|
|
272
|
+
return joint_audios if joint_audios else None
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def collect_joint_files(
|
|
276
|
+
run_input: Optional[RunInput] = None,
|
|
277
|
+
) -> Optional[Sequence[File]]:
|
|
278
|
+
"""Collect files from input and session history."""
|
|
279
|
+
from agno.utils.log import log_debug
|
|
280
|
+
|
|
281
|
+
joint_files: List[File] = []
|
|
282
|
+
|
|
283
|
+
# 1. Add files from current input
|
|
284
|
+
if run_input and run_input.files:
|
|
285
|
+
joint_files.extend(run_input.files)
|
|
286
|
+
|
|
287
|
+
# TODO: Files aren't stored in session history yet and dont have a FileArtifact
|
|
288
|
+
|
|
289
|
+
if joint_files:
|
|
290
|
+
log_debug(f"Files Available to Model: {len(joint_files)} files")
|
|
291
|
+
|
|
292
|
+
return joint_files if joint_files else None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def store_media_util(run_response: Union[RunOutput, TeamRunOutput], model_response: ModelResponse):
|
|
296
|
+
"""Store media from model response in run_response for persistence"""
|
|
297
|
+
# Handle generated media fields from ModelResponse (generated media)
|
|
298
|
+
if model_response.images is not None:
|
|
299
|
+
for image in model_response.images:
|
|
300
|
+
if run_response.images is None:
|
|
301
|
+
run_response.images = []
|
|
302
|
+
run_response.images.append(image) # Generated images go to run_response.images
|
|
303
|
+
|
|
304
|
+
if model_response.videos is not None:
|
|
305
|
+
for video in model_response.videos:
|
|
306
|
+
if run_response.videos is None:
|
|
307
|
+
run_response.videos = []
|
|
308
|
+
run_response.videos.append(video) # Generated videos go to run_response.videos
|
|
309
|
+
|
|
310
|
+
if model_response.audios is not None:
|
|
311
|
+
for audio in model_response.audios:
|
|
312
|
+
if run_response.audio is None:
|
|
313
|
+
run_response.audio = []
|
|
314
|
+
run_response.audio.append(audio) # Generated audio go to run_response.audio
|
|
315
|
+
|
|
316
|
+
if model_response.files is not None:
|
|
317
|
+
for file in model_response.files:
|
|
318
|
+
if run_response.files is None:
|
|
319
|
+
run_response.files = []
|
|
320
|
+
run_response.files.append(file) # Generated files go to run_response.files
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
def validate_media_object_id(
|
|
324
|
+
images: Optional[Sequence[Image]] = None,
|
|
325
|
+
videos: Optional[Sequence[Video]] = None,
|
|
326
|
+
audios: Optional[Sequence[Audio]] = None,
|
|
327
|
+
files: Optional[Sequence[File]] = None,
|
|
328
|
+
) -> tuple:
|
|
329
|
+
image_list = None
|
|
330
|
+
if images:
|
|
331
|
+
image_list = []
|
|
332
|
+
for img in images:
|
|
333
|
+
if not img.id:
|
|
334
|
+
from uuid import uuid4
|
|
335
|
+
|
|
336
|
+
img.id = str(uuid4())
|
|
337
|
+
image_list.append(img)
|
|
338
|
+
|
|
339
|
+
video_list = None
|
|
340
|
+
if videos:
|
|
341
|
+
video_list = []
|
|
342
|
+
for vid in videos:
|
|
343
|
+
if not vid.id:
|
|
344
|
+
from uuid import uuid4
|
|
345
|
+
|
|
346
|
+
vid.id = str(uuid4())
|
|
347
|
+
video_list.append(vid)
|
|
348
|
+
|
|
349
|
+
audio_list = None
|
|
350
|
+
if audios:
|
|
351
|
+
audio_list = []
|
|
352
|
+
for aud in audios:
|
|
353
|
+
if not aud.id:
|
|
354
|
+
from uuid import uuid4
|
|
355
|
+
|
|
356
|
+
aud.id = str(uuid4())
|
|
357
|
+
audio_list.append(aud)
|
|
358
|
+
|
|
359
|
+
file_list = None
|
|
360
|
+
if files:
|
|
361
|
+
file_list = []
|
|
362
|
+
for file in files:
|
|
363
|
+
if not file.id:
|
|
364
|
+
from uuid import uuid4
|
|
365
|
+
|
|
366
|
+
file.id = str(uuid4())
|
|
367
|
+
file_list.append(file)
|
|
368
|
+
|
|
369
|
+
return image_list, video_list, audio_list, file_list
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def scrub_media_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
|
|
373
|
+
"""
|
|
374
|
+
Completely remove all media from RunOutput when store_media=False.
|
|
375
|
+
This includes media in input, output artifacts, and all messages.
|
|
376
|
+
"""
|
|
377
|
+
# 1. Scrub RunInput media
|
|
378
|
+
if run_response.input is not None:
|
|
379
|
+
run_response.input.images = []
|
|
380
|
+
run_response.input.videos = []
|
|
381
|
+
run_response.input.audios = []
|
|
382
|
+
run_response.input.files = []
|
|
383
|
+
|
|
384
|
+
# 3. Scrub media from all messages
|
|
385
|
+
if run_response.messages:
|
|
386
|
+
for message in run_response.messages:
|
|
387
|
+
scrub_media_from_message(message)
|
|
388
|
+
|
|
389
|
+
# 4. Scrub media from additional_input messages if any
|
|
390
|
+
if run_response.additional_input:
|
|
391
|
+
for message in run_response.additional_input:
|
|
392
|
+
scrub_media_from_message(message)
|
|
393
|
+
|
|
394
|
+
# 5. Scrub media from reasoning_messages if any
|
|
395
|
+
if run_response.reasoning_messages:
|
|
396
|
+
for message in run_response.reasoning_messages:
|
|
397
|
+
scrub_media_from_message(message)
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def scrub_media_from_message(message: Message) -> None:
|
|
401
|
+
"""Remove all media from a Message object."""
|
|
402
|
+
# Input media
|
|
403
|
+
message.images = None
|
|
404
|
+
message.videos = None
|
|
405
|
+
message.audio = None
|
|
406
|
+
message.files = None
|
|
407
|
+
|
|
408
|
+
# Output media
|
|
409
|
+
message.audio_output = None
|
|
410
|
+
message.image_output = None
|
|
411
|
+
message.video_output = None
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def scrub_tool_results_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
|
|
415
|
+
"""
|
|
416
|
+
Remove all tool-related data from RunOutput when store_tool_messages=False.
|
|
417
|
+
This removes both the tool call and its corresponding result to maintain API consistency.
|
|
418
|
+
"""
|
|
419
|
+
if not run_response.messages:
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
# Step 1: Collect all tool_call_ids from tool result messages
|
|
423
|
+
tool_call_ids_to_remove = set()
|
|
424
|
+
for message in run_response.messages:
|
|
425
|
+
if message.role == "tool" and message.tool_call_id:
|
|
426
|
+
tool_call_ids_to_remove.add(message.tool_call_id)
|
|
427
|
+
|
|
428
|
+
# Step 2: Remove tool result messages (role="tool")
|
|
429
|
+
run_response.messages = [msg for msg in run_response.messages if msg.role != "tool"]
|
|
430
|
+
|
|
431
|
+
# Step 3: Remove assistant messages that made those tool calls
|
|
432
|
+
filtered_messages = []
|
|
433
|
+
for message in run_response.messages:
|
|
434
|
+
# Check if this assistant message made any of the tool calls we're removing
|
|
435
|
+
should_remove = False
|
|
436
|
+
if message.role == "assistant" and message.tool_calls:
|
|
437
|
+
for tool_call in message.tool_calls:
|
|
438
|
+
if tool_call.get("id") in tool_call_ids_to_remove:
|
|
439
|
+
should_remove = True
|
|
440
|
+
break
|
|
441
|
+
|
|
442
|
+
if not should_remove:
|
|
443
|
+
filtered_messages.append(message)
|
|
444
|
+
|
|
445
|
+
run_response.messages = filtered_messages
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def scrub_history_messages_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
|
|
449
|
+
"""
|
|
450
|
+
Remove all history messages from TeamRunOutput when store_history_messages=False.
|
|
451
|
+
This removes messages that were loaded from the team's memory.
|
|
452
|
+
"""
|
|
453
|
+
# Remove messages with from_history=True
|
|
454
|
+
if run_response.messages:
|
|
455
|
+
run_response.messages = [msg for msg in run_response.messages if not msg.from_history]
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def get_run_output_util(
|
|
459
|
+
entity: Union["Agent", "Team"], run_id: str, session_id: Optional[str] = None
|
|
460
|
+
) -> Optional[
|
|
461
|
+
Union[
|
|
462
|
+
RunOutput,
|
|
463
|
+
TeamRunOutput,
|
|
464
|
+
]
|
|
465
|
+
]:
|
|
466
|
+
"""
|
|
467
|
+
Get a RunOutput from the database.
|
|
468
|
+
|
|
469
|
+
Args:
|
|
470
|
+
run_id (str): The run_id to load from storage.
|
|
471
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
472
|
+
"""
|
|
473
|
+
if session_id is not None:
|
|
474
|
+
if entity._has_async_db():
|
|
475
|
+
raise ValueError("Async database not supported for sync functions")
|
|
476
|
+
|
|
477
|
+
session = entity.get_session(session_id=session_id)
|
|
478
|
+
if session is not None:
|
|
479
|
+
run_response = session.get_run(run_id=run_id)
|
|
480
|
+
if run_response is not None:
|
|
481
|
+
return run_response # type: ignore
|
|
482
|
+
else:
|
|
483
|
+
log_warning(f"RunOutput {run_id} not found in Session {session_id}")
|
|
484
|
+
elif entity.cached_session is not None:
|
|
485
|
+
run_response = entity.cached_session.get_run(run_id=run_id)
|
|
486
|
+
if run_response is not None:
|
|
487
|
+
return run_response # type: ignore
|
|
488
|
+
else:
|
|
489
|
+
log_warning(f"RunOutput {run_id} not found in Session {entity.cached_session.session_id}")
|
|
490
|
+
return None
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
async def aget_run_output_util(
|
|
495
|
+
entity: Union["Agent", "Team"], run_id: str, session_id: Optional[str] = None
|
|
496
|
+
) -> Optional[Union[RunOutput, TeamRunOutput]]:
|
|
497
|
+
"""
|
|
498
|
+
Get a RunOutput from the database.
|
|
499
|
+
|
|
500
|
+
Args:
|
|
501
|
+
run_id (str): The run_id to load from storage.
|
|
502
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
503
|
+
"""
|
|
504
|
+
if session_id is not None:
|
|
505
|
+
session = await entity.aget_session(session_id=session_id)
|
|
506
|
+
if session is not None:
|
|
507
|
+
run_response = session.get_run(run_id=run_id)
|
|
508
|
+
if run_response is not None:
|
|
509
|
+
return run_response # type: ignore
|
|
510
|
+
else:
|
|
511
|
+
log_warning(f"RunOutput {run_id} not found in Session {session_id}")
|
|
512
|
+
elif entity.cached_session is not None:
|
|
513
|
+
run_response = entity.cached_session.get_run(run_id=run_id)
|
|
514
|
+
if run_response is not None:
|
|
515
|
+
return run_response
|
|
516
|
+
else:
|
|
517
|
+
log_warning(f"RunOutput {run_id} not found in Session {entity.cached_session.session_id}")
|
|
518
|
+
return None
|
|
519
|
+
return None
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def get_last_run_output_util(
|
|
523
|
+
entity: Union["Agent", "Team"], session_id: Optional[str] = None
|
|
524
|
+
) -> Optional[Union[RunOutput, TeamRunOutput]]:
|
|
525
|
+
"""
|
|
526
|
+
Get the last run response from the database.
|
|
527
|
+
|
|
528
|
+
Args:
|
|
529
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
RunOutput: The last run response from the database.
|
|
533
|
+
"""
|
|
534
|
+
if session_id is not None:
|
|
535
|
+
if entity._has_async_db():
|
|
536
|
+
raise ValueError("Async database not supported for sync functions")
|
|
537
|
+
|
|
538
|
+
session = entity.get_session(session_id=session_id)
|
|
539
|
+
if session is not None and session.runs is not None and len(session.runs) > 0:
|
|
540
|
+
for run_output in reversed(session.runs):
|
|
541
|
+
if entity.__class__.__name__ == "Agent":
|
|
542
|
+
if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
|
|
543
|
+
return run_output # type: ignore
|
|
544
|
+
elif entity.__class__.__name__ == "Team":
|
|
545
|
+
if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
|
|
546
|
+
return run_output # type: ignore
|
|
547
|
+
else:
|
|
548
|
+
log_warning(f"No run responses found in Session {session_id}")
|
|
549
|
+
|
|
550
|
+
elif (
|
|
551
|
+
entity.cached_session is not None
|
|
552
|
+
and entity.cached_session.runs is not None
|
|
553
|
+
and len(entity.cached_session.runs) > 0
|
|
554
|
+
):
|
|
555
|
+
for run_output in reversed(entity.cached_session.runs):
|
|
556
|
+
if entity.__class__.__name__ == "Agent":
|
|
557
|
+
if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
|
|
558
|
+
return run_output # type: ignore
|
|
559
|
+
elif entity.__class__.__name__ == "Team":
|
|
560
|
+
if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
|
|
561
|
+
return run_output # type: ignore
|
|
562
|
+
return None
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
async def aget_last_run_output_util(
|
|
566
|
+
entity: Union["Agent", "Team"], session_id: Optional[str] = None
|
|
567
|
+
) -> Optional[Union[RunOutput, TeamRunOutput]]:
|
|
568
|
+
"""
|
|
569
|
+
Get the last run response from the database.
|
|
570
|
+
|
|
571
|
+
Args:
|
|
572
|
+
session_id (Optional[str]): The session_id to load from storage.
|
|
573
|
+
|
|
574
|
+
Returns:
|
|
575
|
+
RunOutput: The last run response from the database.
|
|
576
|
+
"""
|
|
577
|
+
if session_id is not None:
|
|
578
|
+
session = await entity.aget_session(session_id=session_id)
|
|
579
|
+
if session is not None and session.runs is not None and len(session.runs) > 0:
|
|
580
|
+
for run_output in reversed(session.runs):
|
|
581
|
+
if entity.__class__.__name__ == "Agent":
|
|
582
|
+
if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
|
|
583
|
+
return run_output # type: ignore
|
|
584
|
+
elif entity.__class__.__name__ == "Team":
|
|
585
|
+
if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
|
|
586
|
+
return run_output # type: ignore
|
|
587
|
+
else:
|
|
588
|
+
log_warning(f"No run responses found in Session {session_id}")
|
|
589
|
+
|
|
590
|
+
elif (
|
|
591
|
+
entity.cached_session is not None
|
|
592
|
+
and entity.cached_session.runs is not None
|
|
593
|
+
and len(entity.cached_session.runs) > 0
|
|
594
|
+
):
|
|
595
|
+
for run_output in reversed(entity.cached_session.runs):
|
|
596
|
+
if entity.__class__.__name__ == "Agent":
|
|
597
|
+
if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
|
|
598
|
+
return run_output # type: ignore
|
|
599
|
+
elif entity.__class__.__name__ == "Team":
|
|
600
|
+
if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
|
|
601
|
+
return run_output # type: ignore
|
|
602
|
+
return None
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def set_session_name_util(
|
|
606
|
+
entity: Union["Agent", "Team"], session_id: str, autogenerate: bool = False, session_name: Optional[str] = None
|
|
607
|
+
) -> Union[AgentSession, TeamSession, WorkflowSession]:
|
|
608
|
+
"""Set the session name and save to storage"""
|
|
609
|
+
if entity._has_async_db():
|
|
610
|
+
raise ValueError("Async database not supported for sync functions")
|
|
611
|
+
|
|
612
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
613
|
+
|
|
614
|
+
if session is None:
|
|
615
|
+
raise Exception("No session found")
|
|
616
|
+
|
|
617
|
+
# -*- Generate name for session
|
|
618
|
+
if autogenerate:
|
|
619
|
+
session_name = entity.generate_session_name(session=session) # type: ignore
|
|
620
|
+
log_debug(f"Generated Session Name: {session_name}")
|
|
621
|
+
elif session_name is None:
|
|
622
|
+
raise Exception("No session name provided")
|
|
623
|
+
|
|
624
|
+
# -*- Rename session
|
|
625
|
+
if session.session_data is None:
|
|
626
|
+
session.session_data = {"session_name": session_name}
|
|
627
|
+
else:
|
|
628
|
+
session.session_data["session_name"] = session_name
|
|
629
|
+
# -*- Save to storage
|
|
630
|
+
entity.save_session(session=session) # type: ignore
|
|
631
|
+
|
|
632
|
+
return session
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
async def aset_session_name_util(
|
|
636
|
+
entity: Union["Agent", "Team"], session_id: str, autogenerate: bool = False, session_name: Optional[str] = None
|
|
637
|
+
) -> Union[AgentSession, TeamSession, WorkflowSession]:
|
|
638
|
+
"""Set the session name and save to storage"""
|
|
639
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
640
|
+
|
|
641
|
+
if session is None:
|
|
642
|
+
raise Exception("Session not found")
|
|
643
|
+
|
|
644
|
+
# -*- Generate name for session
|
|
645
|
+
if autogenerate:
|
|
646
|
+
session_name = entity.generate_session_name(session=session) # type: ignore
|
|
647
|
+
log_debug(f"Generated Session Name: {session_name}")
|
|
648
|
+
elif session_name is None:
|
|
649
|
+
raise Exception("No session name provided")
|
|
650
|
+
|
|
651
|
+
# -*- Rename session
|
|
652
|
+
if session.session_data is None:
|
|
653
|
+
session.session_data = {"session_name": session_name}
|
|
654
|
+
else:
|
|
655
|
+
session.session_data["session_name"] = session_name
|
|
656
|
+
|
|
657
|
+
# -*- Save to storage
|
|
658
|
+
await entity.asave_session(session=session) # type: ignore
|
|
659
|
+
|
|
660
|
+
return session
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def get_session_name_util(entity: Union["Agent", "Team"], session_id: str) -> str:
|
|
664
|
+
"""Get the session name for the given session ID and user ID."""
|
|
665
|
+
|
|
666
|
+
if entity._has_async_db():
|
|
667
|
+
raise ValueError("Async database not supported for sync functions")
|
|
668
|
+
|
|
669
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
670
|
+
if session is None:
|
|
671
|
+
raise Exception("Session not found")
|
|
672
|
+
return session.session_data.get("session_name", "") if session.session_data is not None else "" # type: ignore
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
async def aget_session_name_util(entity: Union["Agent", "Team"], session_id: str) -> str:
|
|
676
|
+
"""Get the session name for the given session ID and user ID."""
|
|
677
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
678
|
+
if session is None:
|
|
679
|
+
raise Exception("Session not found")
|
|
680
|
+
return session.session_data.get("session_name", "") if session.session_data is not None else "" # type: ignore
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def get_session_state_util(entity: Union["Agent", "Team"], session_id: str) -> Dict[str, Any]:
|
|
684
|
+
"""Get the session state for the given session ID and user ID."""
|
|
685
|
+
if entity._has_async_db():
|
|
686
|
+
raise ValueError("Async database not supported for sync functions")
|
|
687
|
+
|
|
688
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
689
|
+
if session is None:
|
|
690
|
+
raise Exception("Session not found")
|
|
691
|
+
return session.session_data.get("session_state", {}) if session.session_data is not None else {} # type: ignore
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
async def aget_session_state_util(entity: Union["Agent", "Team"], session_id: str) -> Dict[str, Any]:
|
|
695
|
+
"""Get the session state for the given session ID and user ID."""
|
|
696
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
697
|
+
if session is None:
|
|
698
|
+
raise Exception("Session not found")
|
|
699
|
+
return session.session_data.get("session_state", {}) if session.session_data is not None else {} # type: ignore
|
|
700
|
+
|
|
701
|
+
|
|
702
|
+
def update_session_state_util(
|
|
703
|
+
entity: Union["Agent", "Team"], session_state_updates: Dict[str, Any], session_id: str
|
|
704
|
+
) -> str:
|
|
705
|
+
"""
|
|
706
|
+
Update the session state for the given session ID and user ID.
|
|
707
|
+
Args:
|
|
708
|
+
session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
|
|
709
|
+
session_id: The session ID to update. If not provided, the current cached session ID is used.
|
|
710
|
+
Returns:
|
|
711
|
+
dict: The updated session state.
|
|
712
|
+
"""
|
|
713
|
+
if entity._has_async_db():
|
|
714
|
+
raise ValueError("Async database not supported for sync functions")
|
|
715
|
+
|
|
716
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
717
|
+
if session is None:
|
|
718
|
+
raise Exception("Session not found")
|
|
719
|
+
|
|
720
|
+
if session.session_data is not None and "session_state" not in session.session_data:
|
|
721
|
+
session.session_data["session_state"] = {}
|
|
722
|
+
|
|
723
|
+
for key, value in session_state_updates.items():
|
|
724
|
+
session.session_data["session_state"][key] = value # type: ignore
|
|
725
|
+
|
|
726
|
+
entity.save_session(session=session) # type: ignore
|
|
727
|
+
|
|
728
|
+
return session.session_data["session_state"] # type: ignore
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
async def aupdate_session_state_util(
|
|
732
|
+
entity: Union["Agent", "Team"], session_state_updates: Dict[str, Any], session_id: str
|
|
733
|
+
) -> str:
|
|
734
|
+
"""
|
|
735
|
+
Update the session state for the given session ID and user ID.
|
|
736
|
+
Args:
|
|
737
|
+
session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
|
|
738
|
+
session_id: The session ID to update. If not provided, the current cached session ID is used.
|
|
739
|
+
Returns:
|
|
740
|
+
dict: The updated session state.
|
|
741
|
+
"""
|
|
742
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
743
|
+
if session is None:
|
|
744
|
+
raise Exception("Session not found")
|
|
745
|
+
|
|
746
|
+
if session.session_data is not None and "session_state" not in session.session_data:
|
|
747
|
+
session.session_data["session_state"] = {}
|
|
748
|
+
|
|
749
|
+
for key, value in session_state_updates.items():
|
|
750
|
+
session.session_data["session_state"][key] = value # type: ignore
|
|
751
|
+
|
|
752
|
+
await entity.asave_session(session=session) # type: ignore
|
|
753
|
+
|
|
754
|
+
return session.session_data["session_state"] # type: ignore
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def get_session_metrics_util(entity: Union["Agent", "Team"], session_id: str) -> Optional[Metrics]:
|
|
758
|
+
"""Get the session metrics for the given session ID and user ID."""
|
|
759
|
+
if entity._has_async_db():
|
|
760
|
+
raise ValueError("Async database not supported for sync functions")
|
|
761
|
+
|
|
762
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
763
|
+
if session is None:
|
|
764
|
+
raise Exception("Session not found")
|
|
765
|
+
|
|
766
|
+
if session.session_data is not None:
|
|
767
|
+
if isinstance(session.session_data.get("session_metrics"), dict):
|
|
768
|
+
return Metrics(**session.session_data.get("session_metrics", {}))
|
|
769
|
+
elif isinstance(session.session_data.get("session_metrics"), Metrics):
|
|
770
|
+
return session.session_data.get("session_metrics")
|
|
771
|
+
return None
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
async def aget_session_metrics_util(entity: Union["Agent", "Team"], session_id: str) -> Optional[Metrics]:
|
|
775
|
+
"""Get the session metrics for the given session ID and user ID."""
|
|
776
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
777
|
+
if session is None:
|
|
778
|
+
raise Exception("Session not found")
|
|
779
|
+
|
|
780
|
+
if session.session_data is not None:
|
|
781
|
+
if isinstance(session.session_data.get("session_metrics"), dict):
|
|
782
|
+
return Metrics(**session.session_data.get("session_metrics", {}))
|
|
783
|
+
elif isinstance(session.session_data.get("session_metrics"), Metrics):
|
|
784
|
+
return session.session_data.get("session_metrics")
|
|
785
|
+
return None
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def get_chat_history_util(entity: Union["Agent", "Team"], session_id: str) -> List[Message]:
|
|
789
|
+
"""Read the chat history from the session
|
|
790
|
+
|
|
791
|
+
Args:
|
|
792
|
+
session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
|
|
793
|
+
Returns:
|
|
794
|
+
List[Message]: The chat history from the session.
|
|
795
|
+
"""
|
|
796
|
+
if entity._has_async_db():
|
|
797
|
+
raise ValueError("Async database not supported for sync functions")
|
|
798
|
+
|
|
799
|
+
session = entity.get_session(session_id=session_id) # type: ignore
|
|
800
|
+
|
|
801
|
+
if session is None:
|
|
802
|
+
raise Exception("Session not found")
|
|
803
|
+
|
|
804
|
+
return session.get_chat_history() # type: ignore
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
async def aget_chat_history_util(entity: Union["Agent", "Team"], session_id: str) -> List[Message]:
|
|
808
|
+
"""Read the chat history from the session
|
|
809
|
+
|
|
810
|
+
Args:
|
|
811
|
+
session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
|
|
812
|
+
Returns:
|
|
813
|
+
List[Message]: The chat history from the session.
|
|
814
|
+
"""
|
|
815
|
+
session = await entity.aget_session(session_id=session_id) # type: ignore
|
|
816
|
+
|
|
817
|
+
if session is None:
|
|
818
|
+
raise Exception("Session not found")
|
|
819
|
+
|
|
820
|
+
return session.get_chat_history() # type: ignore
|