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,4 +1,4 @@
|
|
|
1
|
-
from typing import Optional
|
|
1
|
+
from typing import Optional, Union
|
|
2
2
|
|
|
3
3
|
from fastapi import APIRouter, BackgroundTasks, HTTPException, Request
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
@@ -24,19 +24,21 @@ class SlackChallengeResponse(BaseModel):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def attach_routes(
|
|
27
|
-
router: APIRouter,
|
|
27
|
+
router: APIRouter,
|
|
28
|
+
agent: Optional[Agent] = None,
|
|
29
|
+
team: Optional[Team] = None,
|
|
30
|
+
workflow: Optional[Workflow] = None,
|
|
31
|
+
reply_to_mentions_only: bool = True,
|
|
28
32
|
) -> APIRouter:
|
|
29
33
|
# Determine entity type for documentation
|
|
30
34
|
entity_type = "agent" if agent else "team" if team else "workflow" if workflow else "unknown"
|
|
31
|
-
entity_name = getattr(agent or team or workflow, "name", f"Unnamed {entity_type}")
|
|
32
35
|
|
|
33
36
|
@router.post(
|
|
34
37
|
"/events",
|
|
35
38
|
operation_id=f"slack_events_{entity_type}",
|
|
36
|
-
|
|
37
|
-
description=
|
|
38
|
-
|
|
39
|
-
response_model=SlackEventResponse,
|
|
39
|
+
name="slack_events",
|
|
40
|
+
description="Process incoming Slack events",
|
|
41
|
+
response_model=Union[SlackChallengeResponse, SlackEventResponse],
|
|
40
42
|
response_model_exclude_none=True,
|
|
41
43
|
responses={
|
|
42
44
|
200: {"description": "Event processed successfully"},
|
|
@@ -73,36 +75,52 @@ def attach_routes(
|
|
|
73
75
|
return SlackEventResponse(status="ok")
|
|
74
76
|
|
|
75
77
|
async def _process_slack_event(event: dict):
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
78
|
+
event_type = event.get("type")
|
|
79
|
+
|
|
80
|
+
# Only handle app_mention and message events
|
|
81
|
+
if event_type not in ("app_mention", "message"):
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
channel_type = event.get("channel_type", "")
|
|
85
|
+
|
|
86
|
+
# Handle duplicate replies
|
|
87
|
+
if not reply_to_mentions_only and event_type == "app_mention":
|
|
88
|
+
return
|
|
89
|
+
|
|
90
|
+
# If reply_to_mentions_only is True, ignore every message that is not a DM
|
|
91
|
+
if reply_to_mentions_only and event_type == "message" and channel_type != "im":
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# Extract event data
|
|
95
|
+
user = None
|
|
96
|
+
message_text = event.get("text", "")
|
|
97
|
+
channel_id = event.get("channel", "")
|
|
98
|
+
user = event.get("user")
|
|
99
|
+
if event.get("thread_ts"):
|
|
100
|
+
ts = event.get("thread_ts", "")
|
|
101
|
+
else:
|
|
102
|
+
ts = event.get("ts", "")
|
|
103
|
+
|
|
104
|
+
# Use the timestamp as the session id, so that each thread is a separate session
|
|
105
|
+
session_id = ts
|
|
106
|
+
|
|
107
|
+
if agent:
|
|
108
|
+
response = await agent.arun(message_text, user_id=user, session_id=session_id)
|
|
109
|
+
elif team:
|
|
110
|
+
response = await team.arun(message_text, user_id=user, session_id=session_id) # type: ignore
|
|
111
|
+
elif workflow:
|
|
112
|
+
response = await workflow.arun(message_text, user_id=user, session_id=session_id) # type: ignore
|
|
113
|
+
|
|
114
|
+
if response:
|
|
115
|
+
if hasattr(response, "reasoning_content") and response.reasoning_content:
|
|
116
|
+
_send_slack_message(
|
|
117
|
+
channel=channel_id,
|
|
118
|
+
message=f"Reasoning: \n{response.reasoning_content}",
|
|
119
|
+
thread_ts=ts,
|
|
120
|
+
italics=True,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
_send_slack_message(channel=channel_id, message=response.content or "", thread_ts=ts)
|
|
106
124
|
|
|
107
125
|
def _send_slack_message(channel: str, thread_ts: str, message: str, italics: bool = False):
|
|
108
126
|
if len(message) <= 40000:
|
|
@@ -21,12 +21,14 @@ class Slack(BaseInterface):
|
|
|
21
21
|
workflow: Optional[Workflow] = None,
|
|
22
22
|
prefix: str = "/slack",
|
|
23
23
|
tags: Optional[List[str]] = None,
|
|
24
|
+
reply_to_mentions_only: bool = True,
|
|
24
25
|
):
|
|
25
26
|
self.agent = agent
|
|
26
27
|
self.team = team
|
|
27
28
|
self.workflow = workflow
|
|
28
29
|
self.prefix = prefix
|
|
29
30
|
self.tags = tags or ["Slack"]
|
|
31
|
+
self.reply_to_mentions_only = reply_to_mentions_only
|
|
30
32
|
|
|
31
33
|
if not (self.agent or self.team or self.workflow):
|
|
32
34
|
raise ValueError("Slack requires an agent, team or workflow")
|
|
@@ -34,6 +36,12 @@ class Slack(BaseInterface):
|
|
|
34
36
|
def get_router(self) -> APIRouter:
|
|
35
37
|
self.router = APIRouter(prefix=self.prefix, tags=self.tags) # type: ignore
|
|
36
38
|
|
|
37
|
-
self.router = attach_routes(
|
|
39
|
+
self.router = attach_routes(
|
|
40
|
+
router=self.router,
|
|
41
|
+
agent=self.agent,
|
|
42
|
+
team=self.team,
|
|
43
|
+
workflow=self.workflow,
|
|
44
|
+
reply_to_mentions_only=self.reply_to_mentions_only,
|
|
45
|
+
)
|
|
38
46
|
|
|
39
47
|
return self.router
|
|
@@ -171,7 +171,6 @@ def attach_routes(router: APIRouter, agent: Optional[Agent] = None, team: Option
|
|
|
171
171
|
f"Could not process image content for user {phone_number}. Type: {type(image_content)}"
|
|
172
172
|
)
|
|
173
173
|
await _send_whatsapp_message(phone_number, response.content) # type: ignore
|
|
174
|
-
await _send_whatsapp_message(phone_number, response.content) # type: ignore
|
|
175
174
|
else:
|
|
176
175
|
await _send_whatsapp_message(phone_number, response.content) # type: ignore
|
|
177
176
|
|
|
@@ -3,6 +3,8 @@ import hmac
|
|
|
3
3
|
import os
|
|
4
4
|
from typing import Optional
|
|
5
5
|
|
|
6
|
+
from agno.utils.log import log_warning
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
def is_development_mode() -> bool:
|
|
8
10
|
"""Check if the application is running in development mode."""
|
|
@@ -36,7 +38,7 @@ def validate_webhook_signature(payload: bytes, signature_header: Optional[str])
|
|
|
36
38
|
"""
|
|
37
39
|
# In development mode, we can bypass signature validation
|
|
38
40
|
if is_development_mode():
|
|
39
|
-
|
|
41
|
+
log_warning("Bypassing signature validation in development mode")
|
|
40
42
|
return True
|
|
41
43
|
|
|
42
44
|
if not signature_header or not signature_header.startswith("sha256="):
|
agno/os/mcp.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""Router for MCP interface providing Model Context Protocol endpoints."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
4
|
+
from typing import TYPE_CHECKING, List, Optional, cast
|
|
5
5
|
from uuid import uuid4
|
|
6
6
|
|
|
7
7
|
from fastmcp import FastMCP
|
|
@@ -9,7 +9,7 @@ from fastmcp.server.http import (
|
|
|
9
9
|
StarletteWithLifespan,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
-
from agno.db.base import SessionType
|
|
12
|
+
from agno.db.base import AsyncBaseDb, SessionType
|
|
13
13
|
from agno.db.schemas import UserMemory
|
|
14
14
|
from agno.os.routers.memory.schemas import (
|
|
15
15
|
UserMemorySchema,
|
|
@@ -57,13 +57,14 @@ def get_mcp_server(
|
|
|
57
57
|
os_id=os.id or "AgentOS",
|
|
58
58
|
description=os.description,
|
|
59
59
|
available_models=os.config.available_models if os.config else [],
|
|
60
|
-
databases=[db.id for
|
|
60
|
+
databases=[db.id for db_list in os.dbs.values() for db in db_list],
|
|
61
61
|
chat=os.config.chat if os.config else None,
|
|
62
62
|
session=os._get_session_config(),
|
|
63
63
|
memory=os._get_memory_config(),
|
|
64
64
|
knowledge=os._get_knowledge_config(),
|
|
65
65
|
evals=os._get_evals_config(),
|
|
66
66
|
metrics=os._get_metrics_config(),
|
|
67
|
+
traces=os._get_traces_config(),
|
|
67
68
|
agents=[AgentSummaryResponse.from_agent(agent) for agent in os.agents] if os.agents else [],
|
|
68
69
|
teams=[TeamSummaryResponse.from_team(team) for team in os.teams] if os.teams else [],
|
|
69
70
|
workflows=[WorkflowSummaryResponse.from_workflow(w) for w in os.workflows] if os.workflows else [],
|
|
@@ -78,21 +79,21 @@ def get_mcp_server(
|
|
|
78
79
|
agent = get_agent_by_id(agent_id, os.agents)
|
|
79
80
|
if agent is None:
|
|
80
81
|
raise Exception(f"Agent {agent_id} not found")
|
|
81
|
-
return agent.
|
|
82
|
+
return await agent.arun(message)
|
|
82
83
|
|
|
83
84
|
@mcp.tool(name="run_team", description="Run a team", tags={"core"}) # type: ignore
|
|
84
85
|
async def run_team(team_id: str, message: str) -> TeamRunOutput:
|
|
85
86
|
team = get_team_by_id(team_id, os.teams)
|
|
86
87
|
if team is None:
|
|
87
88
|
raise Exception(f"Team {team_id} not found")
|
|
88
|
-
return team.
|
|
89
|
+
return await team.arun(message)
|
|
89
90
|
|
|
90
91
|
@mcp.tool(name="run_workflow", description="Run a workflow", tags={"core"}) # type: ignore
|
|
91
92
|
async def run_workflow(workflow_id: str, message: str) -> WorkflowRunOutput:
|
|
92
93
|
workflow = get_workflow_by_id(workflow_id, os.workflows)
|
|
93
94
|
if workflow is None:
|
|
94
95
|
raise Exception(f"Workflow {workflow_id} not found")
|
|
95
|
-
return workflow.
|
|
96
|
+
return await workflow.arun(message)
|
|
96
97
|
|
|
97
98
|
# Session Management Tools
|
|
98
99
|
@mcp.tool(name="get_sessions_for_agent", description="Get list of sessions for an agent", tags={"session"}) # type: ignore
|
|
@@ -103,15 +104,26 @@ def get_mcp_server(
|
|
|
103
104
|
sort_by: str = "created_at",
|
|
104
105
|
sort_order: str = "desc",
|
|
105
106
|
):
|
|
106
|
-
db = get_db(os.dbs, db_id)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
db = await get_db(os.dbs, db_id)
|
|
108
|
+
if isinstance(db, AsyncBaseDb):
|
|
109
|
+
db = cast(AsyncBaseDb, db)
|
|
110
|
+
sessions = await db.get_sessions(
|
|
111
|
+
session_type=SessionType.AGENT,
|
|
112
|
+
component_id=agent_id,
|
|
113
|
+
user_id=user_id,
|
|
114
|
+
sort_by=sort_by,
|
|
115
|
+
sort_order=sort_order,
|
|
116
|
+
deserialize=False,
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
sessions = db.get_sessions(
|
|
120
|
+
session_type=SessionType.AGENT,
|
|
121
|
+
component_id=agent_id,
|
|
122
|
+
user_id=user_id,
|
|
123
|
+
sort_by=sort_by,
|
|
124
|
+
sort_order=sort_order,
|
|
125
|
+
deserialize=False,
|
|
126
|
+
)
|
|
115
127
|
|
|
116
128
|
return {
|
|
117
129
|
"data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
|
|
@@ -125,15 +137,26 @@ def get_mcp_server(
|
|
|
125
137
|
sort_by: str = "created_at",
|
|
126
138
|
sort_order: str = "desc",
|
|
127
139
|
):
|
|
128
|
-
db = get_db(os.dbs, db_id)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
140
|
+
db = await get_db(os.dbs, db_id)
|
|
141
|
+
if isinstance(db, AsyncBaseDb):
|
|
142
|
+
db = cast(AsyncBaseDb, db)
|
|
143
|
+
sessions = await db.get_sessions(
|
|
144
|
+
session_type=SessionType.TEAM,
|
|
145
|
+
component_id=team_id,
|
|
146
|
+
user_id=user_id,
|
|
147
|
+
sort_by=sort_by,
|
|
148
|
+
sort_order=sort_order,
|
|
149
|
+
deserialize=False,
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
sessions = db.get_sessions(
|
|
153
|
+
session_type=SessionType.TEAM,
|
|
154
|
+
component_id=team_id,
|
|
155
|
+
user_id=user_id,
|
|
156
|
+
sort_by=sort_by,
|
|
157
|
+
sort_order=sort_order,
|
|
158
|
+
deserialize=False,
|
|
159
|
+
)
|
|
137
160
|
|
|
138
161
|
return {
|
|
139
162
|
"data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
|
|
@@ -147,15 +170,26 @@ def get_mcp_server(
|
|
|
147
170
|
sort_by: str = "created_at",
|
|
148
171
|
sort_order: str = "desc",
|
|
149
172
|
):
|
|
150
|
-
db = get_db(os.dbs, db_id)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
173
|
+
db = await get_db(os.dbs, db_id)
|
|
174
|
+
if isinstance(db, AsyncBaseDb):
|
|
175
|
+
db = cast(AsyncBaseDb, db)
|
|
176
|
+
sessions = await db.get_sessions(
|
|
177
|
+
session_type=SessionType.WORKFLOW,
|
|
178
|
+
component_id=workflow_id,
|
|
179
|
+
user_id=user_id,
|
|
180
|
+
sort_by=sort_by,
|
|
181
|
+
sort_order=sort_order,
|
|
182
|
+
deserialize=False,
|
|
183
|
+
)
|
|
184
|
+
else:
|
|
185
|
+
sessions = db.get_sessions(
|
|
186
|
+
session_type=SessionType.WORKFLOW,
|
|
187
|
+
component_id=workflow_id,
|
|
188
|
+
user_id=user_id,
|
|
189
|
+
sort_by=sort_by,
|
|
190
|
+
sort_order=sort_order,
|
|
191
|
+
deserialize=False,
|
|
192
|
+
)
|
|
159
193
|
|
|
160
194
|
return {
|
|
161
195
|
"data": [SessionSchema.from_dict(session) for session in sessions], # type: ignore
|
|
@@ -169,7 +203,7 @@ def get_mcp_server(
|
|
|
169
203
|
user_id: str,
|
|
170
204
|
topics: Optional[List[str]] = None,
|
|
171
205
|
) -> UserMemorySchema:
|
|
172
|
-
db = get_db(os.dbs, db_id)
|
|
206
|
+
db = await get_db(os.dbs, db_id)
|
|
173
207
|
user_memory = db.upsert_user_memory(
|
|
174
208
|
memory=UserMemory(
|
|
175
209
|
memory_id=str(uuid4()),
|
|
@@ -191,13 +225,22 @@ def get_mcp_server(
|
|
|
191
225
|
sort_order: str = "desc",
|
|
192
226
|
db_id: Optional[str] = None,
|
|
193
227
|
):
|
|
194
|
-
db = get_db(os.dbs, db_id)
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
228
|
+
db = await get_db(os.dbs, db_id)
|
|
229
|
+
if isinstance(db, AsyncBaseDb):
|
|
230
|
+
db = cast(AsyncBaseDb, db)
|
|
231
|
+
user_memories = await db.get_user_memories(
|
|
232
|
+
user_id=user_id,
|
|
233
|
+
sort_by=sort_by,
|
|
234
|
+
sort_order=sort_order,
|
|
235
|
+
deserialize=False,
|
|
236
|
+
)
|
|
237
|
+
else:
|
|
238
|
+
user_memories = db.get_user_memories(
|
|
239
|
+
user_id=user_id,
|
|
240
|
+
sort_by=sort_by,
|
|
241
|
+
sort_order=sort_order,
|
|
242
|
+
deserialize=False,
|
|
243
|
+
)
|
|
201
244
|
return {
|
|
202
245
|
"data": [UserMemorySchema.from_dict(user_memory) for user_memory in user_memories], # type: ignore
|
|
203
246
|
}
|
|
@@ -209,15 +252,26 @@ def get_mcp_server(
|
|
|
209
252
|
memory: str,
|
|
210
253
|
user_id: str,
|
|
211
254
|
) -> UserMemorySchema:
|
|
212
|
-
db = get_db(os.dbs, db_id)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
memory=
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
255
|
+
db = await get_db(os.dbs, db_id)
|
|
256
|
+
if isinstance(db, AsyncBaseDb):
|
|
257
|
+
db = cast(AsyncBaseDb, db)
|
|
258
|
+
user_memory = await db.upsert_user_memory(
|
|
259
|
+
memory=UserMemory(
|
|
260
|
+
memory_id=memory_id,
|
|
261
|
+
memory=memory,
|
|
262
|
+
user_id=user_id,
|
|
263
|
+
),
|
|
264
|
+
deserialize=False,
|
|
265
|
+
)
|
|
266
|
+
else:
|
|
267
|
+
user_memory = db.upsert_user_memory(
|
|
268
|
+
memory=UserMemory(
|
|
269
|
+
memory_id=memory_id,
|
|
270
|
+
memory=memory,
|
|
271
|
+
user_id=user_id,
|
|
272
|
+
),
|
|
273
|
+
deserialize=False,
|
|
274
|
+
)
|
|
221
275
|
if not user_memory:
|
|
222
276
|
raise Exception("Failed to update memory")
|
|
223
277
|
|
|
@@ -228,8 +282,12 @@ def get_mcp_server(
|
|
|
228
282
|
db_id: str,
|
|
229
283
|
memory_id: str,
|
|
230
284
|
) -> None:
|
|
231
|
-
db = get_db(os.dbs, db_id)
|
|
232
|
-
db
|
|
285
|
+
db = await get_db(os.dbs, db_id)
|
|
286
|
+
if isinstance(db, AsyncBaseDb):
|
|
287
|
+
db = cast(AsyncBaseDb, db)
|
|
288
|
+
await db.delete_user_memory(memory_id=memory_id)
|
|
289
|
+
else:
|
|
290
|
+
db.delete_user_memory(memory_id=memory_id)
|
|
233
291
|
|
|
234
292
|
mcp_app = mcp.http_app(path="/mcp")
|
|
235
293
|
return mcp_app
|