agno 2.2.13__py3-none-any.whl → 2.4.3__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/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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 +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -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 +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -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 +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, AsyncGenerator, List, Optional, Union
|
|
2
|
+
from uuid import uuid4
|
|
3
|
+
|
|
4
|
+
from fastapi import (
|
|
5
|
+
APIRouter,
|
|
6
|
+
BackgroundTasks,
|
|
7
|
+
Depends,
|
|
8
|
+
File,
|
|
9
|
+
Form,
|
|
10
|
+
HTTPException,
|
|
11
|
+
Request,
|
|
12
|
+
UploadFile,
|
|
13
|
+
)
|
|
14
|
+
from fastapi.responses import JSONResponse, StreamingResponse
|
|
15
|
+
|
|
16
|
+
from agno.db.base import BaseDb
|
|
17
|
+
from agno.exceptions import InputCheckError, OutputCheckError
|
|
18
|
+
from agno.media import Audio, Image, Video
|
|
19
|
+
from agno.media import File as FileMedia
|
|
20
|
+
from agno.os.auth import get_auth_token_from_request, get_authentication_dependency, require_resource_access
|
|
21
|
+
from agno.os.routers.teams.schema import TeamResponse
|
|
22
|
+
from agno.os.schema import (
|
|
23
|
+
BadRequestResponse,
|
|
24
|
+
InternalServerErrorResponse,
|
|
25
|
+
NotFoundResponse,
|
|
26
|
+
UnauthenticatedResponse,
|
|
27
|
+
ValidationErrorResponse,
|
|
28
|
+
)
|
|
29
|
+
from agno.os.settings import AgnoAPISettings
|
|
30
|
+
from agno.os.utils import (
|
|
31
|
+
format_sse_event,
|
|
32
|
+
get_request_kwargs,
|
|
33
|
+
get_team_by_id,
|
|
34
|
+
process_audio,
|
|
35
|
+
process_document,
|
|
36
|
+
process_image,
|
|
37
|
+
process_video,
|
|
38
|
+
)
|
|
39
|
+
from agno.registry import Registry
|
|
40
|
+
from agno.run.team import RunErrorEvent as TeamRunErrorEvent
|
|
41
|
+
from agno.team.remote import RemoteTeam
|
|
42
|
+
from agno.team.team import Team
|
|
43
|
+
from agno.utils.log import log_warning, logger
|
|
44
|
+
|
|
45
|
+
if TYPE_CHECKING:
|
|
46
|
+
from agno.os.app import AgentOS
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def team_response_streamer(
|
|
50
|
+
team: Union[Team, RemoteTeam],
|
|
51
|
+
message: str,
|
|
52
|
+
session_id: Optional[str] = None,
|
|
53
|
+
user_id: Optional[str] = None,
|
|
54
|
+
images: Optional[List[Image]] = None,
|
|
55
|
+
audio: Optional[List[Audio]] = None,
|
|
56
|
+
videos: Optional[List[Video]] = None,
|
|
57
|
+
files: Optional[List[FileMedia]] = None,
|
|
58
|
+
background_tasks: Optional[BackgroundTasks] = None,
|
|
59
|
+
auth_token: Optional[str] = None,
|
|
60
|
+
**kwargs: Any,
|
|
61
|
+
) -> AsyncGenerator:
|
|
62
|
+
"""Run the given team asynchronously and yield its response"""
|
|
63
|
+
try:
|
|
64
|
+
# Pass background_tasks if provided
|
|
65
|
+
if background_tasks is not None:
|
|
66
|
+
kwargs["background_tasks"] = background_tasks
|
|
67
|
+
|
|
68
|
+
if "stream_events" in kwargs:
|
|
69
|
+
stream_events = kwargs.pop("stream_events")
|
|
70
|
+
else:
|
|
71
|
+
stream_events = True
|
|
72
|
+
|
|
73
|
+
# Pass auth_token for remote teams
|
|
74
|
+
if auth_token and isinstance(team, RemoteTeam):
|
|
75
|
+
kwargs["auth_token"] = auth_token
|
|
76
|
+
|
|
77
|
+
run_response = team.arun(
|
|
78
|
+
input=message,
|
|
79
|
+
session_id=session_id,
|
|
80
|
+
user_id=user_id,
|
|
81
|
+
images=images,
|
|
82
|
+
audio=audio,
|
|
83
|
+
videos=videos,
|
|
84
|
+
files=files,
|
|
85
|
+
stream=True,
|
|
86
|
+
stream_events=stream_events,
|
|
87
|
+
**kwargs,
|
|
88
|
+
)
|
|
89
|
+
async for run_response_chunk in run_response:
|
|
90
|
+
yield format_sse_event(run_response_chunk) # type: ignore
|
|
91
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
92
|
+
error_response = TeamRunErrorEvent(
|
|
93
|
+
content=str(e),
|
|
94
|
+
error_type=e.type,
|
|
95
|
+
error_id=e.error_id,
|
|
96
|
+
additional_data=e.additional_data,
|
|
97
|
+
)
|
|
98
|
+
yield format_sse_event(error_response)
|
|
99
|
+
|
|
100
|
+
except BaseException as e:
|
|
101
|
+
import traceback
|
|
102
|
+
|
|
103
|
+
traceback.print_exc()
|
|
104
|
+
error_response = TeamRunErrorEvent(
|
|
105
|
+
content=str(e),
|
|
106
|
+
error_type=e.type if hasattr(e, "type") else None,
|
|
107
|
+
error_id=e.error_id if hasattr(e, "error_id") else None,
|
|
108
|
+
)
|
|
109
|
+
yield format_sse_event(error_response)
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_team_router(
|
|
114
|
+
os: "AgentOS",
|
|
115
|
+
settings: AgnoAPISettings = AgnoAPISettings(),
|
|
116
|
+
registry: Optional[Registry] = None,
|
|
117
|
+
) -> APIRouter:
|
|
118
|
+
"""Create the team router with comprehensive OpenAPI documentation."""
|
|
119
|
+
router = APIRouter(
|
|
120
|
+
dependencies=[Depends(get_authentication_dependency(settings))],
|
|
121
|
+
responses={
|
|
122
|
+
400: {"description": "Bad Request", "model": BadRequestResponse},
|
|
123
|
+
401: {"description": "Unauthorized", "model": UnauthenticatedResponse},
|
|
124
|
+
404: {"description": "Not Found", "model": NotFoundResponse},
|
|
125
|
+
422: {"description": "Validation Error", "model": ValidationErrorResponse},
|
|
126
|
+
500: {"description": "Internal Server Error", "model": InternalServerErrorResponse},
|
|
127
|
+
},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@router.post(
|
|
131
|
+
"/teams/{team_id}/runs",
|
|
132
|
+
tags=["Teams"],
|
|
133
|
+
operation_id="create_team_run",
|
|
134
|
+
response_model_exclude_none=True,
|
|
135
|
+
summary="Create Team Run",
|
|
136
|
+
description=(
|
|
137
|
+
"Execute a team collaboration with multiple agents working together on a task.\n\n"
|
|
138
|
+
"**Features:**\n"
|
|
139
|
+
"- Text message input with optional session management\n"
|
|
140
|
+
"- Multi-media support: images (PNG, JPEG, WebP), audio (WAV, MP3), video (MP4, WebM, etc.)\n"
|
|
141
|
+
"- Document processing: PDF, CSV, DOCX, TXT, JSON\n"
|
|
142
|
+
"- Real-time streaming responses with Server-Sent Events (SSE)\n"
|
|
143
|
+
"- User and session context preservation\n\n"
|
|
144
|
+
"**Streaming Response:**\n"
|
|
145
|
+
"When `stream=true`, returns SSE events with `event` and `data` fields."
|
|
146
|
+
),
|
|
147
|
+
responses={
|
|
148
|
+
200: {
|
|
149
|
+
"description": "Team run executed successfully",
|
|
150
|
+
"content": {
|
|
151
|
+
"text/event-stream": {
|
|
152
|
+
"example": 'event: RunStarted\ndata: {"content": "Hello!", "run_id": "123..."}\n\n'
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
400: {"description": "Invalid request or unsupported file type", "model": BadRequestResponse},
|
|
157
|
+
404: {"description": "Team not found", "model": NotFoundResponse},
|
|
158
|
+
},
|
|
159
|
+
dependencies=[Depends(require_resource_access("teams", "run", "team_id"))],
|
|
160
|
+
)
|
|
161
|
+
async def create_team_run(
|
|
162
|
+
team_id: str,
|
|
163
|
+
request: Request,
|
|
164
|
+
background_tasks: BackgroundTasks,
|
|
165
|
+
message: str = Form(...),
|
|
166
|
+
stream: bool = Form(True),
|
|
167
|
+
monitor: bool = Form(True),
|
|
168
|
+
session_id: Optional[str] = Form(None),
|
|
169
|
+
user_id: Optional[str] = Form(None),
|
|
170
|
+
files: Optional[List[UploadFile]] = File(None),
|
|
171
|
+
version: Optional[int] = Form(None),
|
|
172
|
+
):
|
|
173
|
+
kwargs = await get_request_kwargs(request, create_team_run)
|
|
174
|
+
|
|
175
|
+
if hasattr(request.state, "user_id") and request.state.user_id is not None:
|
|
176
|
+
if user_id and user_id != request.state.user_id:
|
|
177
|
+
log_warning("User ID parameter passed in both request state and kwargs, using request state")
|
|
178
|
+
user_id = request.state.user_id
|
|
179
|
+
if hasattr(request.state, "session_id") and request.state.session_id is not None:
|
|
180
|
+
if session_id and session_id != request.state.session_id:
|
|
181
|
+
log_warning("Session ID parameter passed in both request state and kwargs, using request state")
|
|
182
|
+
session_id = request.state.session_id
|
|
183
|
+
if hasattr(request.state, "session_state") and request.state.session_state is not None:
|
|
184
|
+
session_state = request.state.session_state
|
|
185
|
+
if "session_state" in kwargs:
|
|
186
|
+
log_warning("Session state parameter passed in both request state and kwargs, using request state")
|
|
187
|
+
kwargs["session_state"] = session_state
|
|
188
|
+
if hasattr(request.state, "dependencies") and request.state.dependencies is not None:
|
|
189
|
+
dependencies = request.state.dependencies
|
|
190
|
+
if "dependencies" in kwargs:
|
|
191
|
+
log_warning("Dependencies parameter passed in both request state and kwargs, using request state")
|
|
192
|
+
kwargs["dependencies"] = dependencies
|
|
193
|
+
if hasattr(request.state, "metadata") and request.state.metadata is not None:
|
|
194
|
+
metadata = request.state.metadata
|
|
195
|
+
if "metadata" in kwargs:
|
|
196
|
+
log_warning("Metadata parameter passed in both request state and kwargs, using request state")
|
|
197
|
+
kwargs["metadata"] = metadata
|
|
198
|
+
|
|
199
|
+
logger.debug(f"Creating team run: {message=} {session_id=} {monitor=} {user_id=} {team_id=} {files=} {kwargs=}")
|
|
200
|
+
|
|
201
|
+
team = get_team_by_id(
|
|
202
|
+
team_id=team_id, teams=os.teams, db=os.db, version=version, registry=registry, create_fresh=True
|
|
203
|
+
)
|
|
204
|
+
if team is None:
|
|
205
|
+
raise HTTPException(status_code=404, detail="Team not found")
|
|
206
|
+
|
|
207
|
+
if session_id is not None and session_id != "":
|
|
208
|
+
logger.debug(f"Continuing session: {session_id}")
|
|
209
|
+
else:
|
|
210
|
+
logger.debug("Creating new session")
|
|
211
|
+
session_id = str(uuid4())
|
|
212
|
+
|
|
213
|
+
base64_images: List[Image] = []
|
|
214
|
+
base64_audios: List[Audio] = []
|
|
215
|
+
base64_videos: List[Video] = []
|
|
216
|
+
document_files: List[FileMedia] = []
|
|
217
|
+
|
|
218
|
+
if files:
|
|
219
|
+
for file in files:
|
|
220
|
+
if file.content_type in ["image/png", "image/jpeg", "image/jpg", "image/webp"]:
|
|
221
|
+
try:
|
|
222
|
+
base64_image = process_image(file)
|
|
223
|
+
base64_images.append(base64_image)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.error(f"Error processing image {file.filename}: {e}")
|
|
226
|
+
continue
|
|
227
|
+
elif file.content_type in ["audio/wav", "audio/mp3", "audio/mpeg"]:
|
|
228
|
+
try:
|
|
229
|
+
base64_audio = process_audio(file)
|
|
230
|
+
base64_audios.append(base64_audio)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logger.error(f"Error processing audio {file.filename}: {e}")
|
|
233
|
+
continue
|
|
234
|
+
elif file.content_type in [
|
|
235
|
+
"video/x-flv",
|
|
236
|
+
"video/quicktime",
|
|
237
|
+
"video/mpeg",
|
|
238
|
+
"video/mpegs",
|
|
239
|
+
"video/mpgs",
|
|
240
|
+
"video/mpg",
|
|
241
|
+
"video/mpg",
|
|
242
|
+
"video/mp4",
|
|
243
|
+
"video/webm",
|
|
244
|
+
"video/wmv",
|
|
245
|
+
"video/3gpp",
|
|
246
|
+
]:
|
|
247
|
+
try:
|
|
248
|
+
base64_video = process_video(file)
|
|
249
|
+
base64_videos.append(base64_video)
|
|
250
|
+
except Exception as e:
|
|
251
|
+
logger.error(f"Error processing video {file.filename}: {e}")
|
|
252
|
+
continue
|
|
253
|
+
elif file.content_type in [
|
|
254
|
+
"application/pdf",
|
|
255
|
+
"text/csv",
|
|
256
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
257
|
+
"text/plain",
|
|
258
|
+
"application/json",
|
|
259
|
+
]:
|
|
260
|
+
document_file = process_document(file)
|
|
261
|
+
if document_file is not None:
|
|
262
|
+
document_files.append(document_file)
|
|
263
|
+
else:
|
|
264
|
+
raise HTTPException(status_code=400, detail="Unsupported file type")
|
|
265
|
+
|
|
266
|
+
# Extract auth token for remote teams
|
|
267
|
+
auth_token = get_auth_token_from_request(request)
|
|
268
|
+
|
|
269
|
+
if stream:
|
|
270
|
+
return StreamingResponse(
|
|
271
|
+
team_response_streamer(
|
|
272
|
+
team,
|
|
273
|
+
message,
|
|
274
|
+
session_id=session_id,
|
|
275
|
+
user_id=user_id,
|
|
276
|
+
images=base64_images if base64_images else None,
|
|
277
|
+
audio=base64_audios if base64_audios else None,
|
|
278
|
+
videos=base64_videos if base64_videos else None,
|
|
279
|
+
files=document_files if document_files else None,
|
|
280
|
+
background_tasks=background_tasks,
|
|
281
|
+
auth_token=auth_token,
|
|
282
|
+
**kwargs,
|
|
283
|
+
),
|
|
284
|
+
media_type="text/event-stream",
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
# Pass auth_token for remote teams
|
|
288
|
+
if auth_token and isinstance(team, RemoteTeam):
|
|
289
|
+
kwargs["auth_token"] = auth_token
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
run_response = await team.arun(
|
|
293
|
+
input=message,
|
|
294
|
+
session_id=session_id,
|
|
295
|
+
user_id=user_id,
|
|
296
|
+
images=base64_images if base64_images else None,
|
|
297
|
+
audio=base64_audios if base64_audios else None,
|
|
298
|
+
videos=base64_videos if base64_videos else None,
|
|
299
|
+
files=document_files if document_files else None,
|
|
300
|
+
stream=False,
|
|
301
|
+
background_tasks=background_tasks,
|
|
302
|
+
**kwargs,
|
|
303
|
+
)
|
|
304
|
+
return run_response.to_dict()
|
|
305
|
+
|
|
306
|
+
except InputCheckError as e:
|
|
307
|
+
raise HTTPException(status_code=400, detail=str(e))
|
|
308
|
+
|
|
309
|
+
@router.post(
|
|
310
|
+
"/teams/{team_id}/runs/{run_id}/cancel",
|
|
311
|
+
tags=["Teams"],
|
|
312
|
+
operation_id="cancel_team_run",
|
|
313
|
+
response_model_exclude_none=True,
|
|
314
|
+
summary="Cancel Team Run",
|
|
315
|
+
description=(
|
|
316
|
+
"Cancel a currently executing team run. This will attempt to stop the team's execution gracefully.\n\n"
|
|
317
|
+
"**Note:** Cancellation may not be immediate for all operations."
|
|
318
|
+
),
|
|
319
|
+
responses={
|
|
320
|
+
200: {},
|
|
321
|
+
404: {"description": "Team not found", "model": NotFoundResponse},
|
|
322
|
+
500: {"description": "Failed to cancel team run", "model": InternalServerErrorResponse},
|
|
323
|
+
},
|
|
324
|
+
dependencies=[Depends(require_resource_access("teams", "run", "team_id"))],
|
|
325
|
+
)
|
|
326
|
+
async def cancel_team_run(
|
|
327
|
+
team_id: str,
|
|
328
|
+
run_id: str,
|
|
329
|
+
):
|
|
330
|
+
team = get_team_by_id(team_id=team_id, teams=os.teams, db=os.db, registry=registry, create_fresh=True)
|
|
331
|
+
if team is None:
|
|
332
|
+
raise HTTPException(status_code=404, detail="Team not found")
|
|
333
|
+
|
|
334
|
+
cancelled = await team.acancel_run(run_id=run_id)
|
|
335
|
+
if not cancelled:
|
|
336
|
+
raise HTTPException(status_code=500, detail="Failed to cancel run - run not found or already completed")
|
|
337
|
+
|
|
338
|
+
return JSONResponse(content={}, status_code=200)
|
|
339
|
+
|
|
340
|
+
@router.get(
|
|
341
|
+
"/teams",
|
|
342
|
+
response_model=List[TeamResponse],
|
|
343
|
+
response_model_exclude_none=True,
|
|
344
|
+
tags=["Teams"],
|
|
345
|
+
operation_id="get_teams",
|
|
346
|
+
summary="List All Teams",
|
|
347
|
+
description=(
|
|
348
|
+
"Retrieve a comprehensive list of all teams configured in this OS instance.\n\n"
|
|
349
|
+
"**Returns team information including:**\n"
|
|
350
|
+
"- Team metadata (ID, name, description, execution mode)\n"
|
|
351
|
+
"- Model configuration for team coordination\n"
|
|
352
|
+
"- Team member roster with roles and capabilities\n"
|
|
353
|
+
"- Knowledge sharing and memory configurations"
|
|
354
|
+
),
|
|
355
|
+
responses={
|
|
356
|
+
200: {
|
|
357
|
+
"description": "List of teams retrieved successfully",
|
|
358
|
+
"content": {
|
|
359
|
+
"application/json": {
|
|
360
|
+
"example": [
|
|
361
|
+
{
|
|
362
|
+
"team_id": "basic-team",
|
|
363
|
+
"name": "Basic Team",
|
|
364
|
+
"mode": "coordinate",
|
|
365
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
366
|
+
"tools": [
|
|
367
|
+
{
|
|
368
|
+
"name": "transfer_task_to_member",
|
|
369
|
+
"description": "Use this function to transfer a task to the selected team member.\nYou must provide a clear and concise description of the task the member should achieve AND the expected output.",
|
|
370
|
+
"parameters": {
|
|
371
|
+
"type": "object",
|
|
372
|
+
"properties": {
|
|
373
|
+
"member_id": {
|
|
374
|
+
"type": "string",
|
|
375
|
+
"description": "(str) The ID of the member to transfer the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.",
|
|
376
|
+
},
|
|
377
|
+
"task_description": {
|
|
378
|
+
"type": "string",
|
|
379
|
+
"description": "(str) A clear and concise description of the task the member should achieve.",
|
|
380
|
+
},
|
|
381
|
+
"expected_output": {
|
|
382
|
+
"type": "string",
|
|
383
|
+
"description": "(str) The expected output from the member (optional).",
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
"additionalProperties": False,
|
|
387
|
+
"required": ["member_id", "task_description"],
|
|
388
|
+
},
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
"members": [
|
|
392
|
+
{
|
|
393
|
+
"agent_id": "basic-agent",
|
|
394
|
+
"name": "Basic Agent",
|
|
395
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI gpt-4o"},
|
|
396
|
+
"memory": {
|
|
397
|
+
"app_name": "Memory",
|
|
398
|
+
"app_url": None,
|
|
399
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
400
|
+
},
|
|
401
|
+
"session_table": "agno_sessions",
|
|
402
|
+
"memory_table": "agno_memories",
|
|
403
|
+
}
|
|
404
|
+
],
|
|
405
|
+
"enable_agentic_context": False,
|
|
406
|
+
"memory": {
|
|
407
|
+
"app_name": "agno_memories",
|
|
408
|
+
"app_url": "/memory/1",
|
|
409
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
410
|
+
},
|
|
411
|
+
"async_mode": False,
|
|
412
|
+
"session_table": "agno_sessions",
|
|
413
|
+
"memory_table": "agno_memories",
|
|
414
|
+
}
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
}
|
|
419
|
+
},
|
|
420
|
+
)
|
|
421
|
+
async def get_teams(request: Request) -> List[TeamResponse]:
|
|
422
|
+
"""Return the list of all Teams present in the contextual OS"""
|
|
423
|
+
# Filter teams based on user's scopes (only if authorization is enabled)
|
|
424
|
+
if getattr(request.state, "authorization_enabled", False):
|
|
425
|
+
from agno.os.auth import filter_resources_by_access, get_accessible_resources
|
|
426
|
+
|
|
427
|
+
# Check if user has any team scopes at all
|
|
428
|
+
accessible_ids = get_accessible_resources(request, "teams")
|
|
429
|
+
if not accessible_ids:
|
|
430
|
+
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
|
431
|
+
|
|
432
|
+
accessible_teams = filter_resources_by_access(request, os.teams or [], "teams")
|
|
433
|
+
else:
|
|
434
|
+
accessible_teams = os.teams or []
|
|
435
|
+
|
|
436
|
+
teams = []
|
|
437
|
+
for team in accessible_teams:
|
|
438
|
+
if isinstance(team, RemoteTeam):
|
|
439
|
+
teams.append(await team.get_team_config())
|
|
440
|
+
else:
|
|
441
|
+
team_response = await TeamResponse.from_team(team=team)
|
|
442
|
+
teams.append(team_response)
|
|
443
|
+
|
|
444
|
+
# Also load teams from database
|
|
445
|
+
if os.db and isinstance(os.db, BaseDb):
|
|
446
|
+
from agno.team.team import get_teams
|
|
447
|
+
|
|
448
|
+
db_teams = get_teams(db=os.db, registry=registry)
|
|
449
|
+
for db_team in db_teams:
|
|
450
|
+
team_response = await TeamResponse.from_team(team=db_team)
|
|
451
|
+
teams.append(team_response)
|
|
452
|
+
|
|
453
|
+
return teams
|
|
454
|
+
|
|
455
|
+
@router.get(
|
|
456
|
+
"/teams/{team_id}",
|
|
457
|
+
response_model=TeamResponse,
|
|
458
|
+
response_model_exclude_none=True,
|
|
459
|
+
tags=["Teams"],
|
|
460
|
+
operation_id="get_team",
|
|
461
|
+
summary="Get Team Details",
|
|
462
|
+
description=("Retrieve detailed configuration and member information for a specific team."),
|
|
463
|
+
responses={
|
|
464
|
+
200: {
|
|
465
|
+
"description": "Team details retrieved successfully",
|
|
466
|
+
"content": {
|
|
467
|
+
"application/json": {
|
|
468
|
+
"example": {
|
|
469
|
+
"team_id": "basic-team",
|
|
470
|
+
"name": "Basic Team",
|
|
471
|
+
"description": None,
|
|
472
|
+
"mode": "coordinate",
|
|
473
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
474
|
+
"tools": [
|
|
475
|
+
{
|
|
476
|
+
"name": "transfer_task_to_member",
|
|
477
|
+
"description": "Use this function to transfer a task to the selected team member.\nYou must provide a clear and concise description of the task the member should achieve AND the expected output.",
|
|
478
|
+
"parameters": {
|
|
479
|
+
"type": "object",
|
|
480
|
+
"properties": {
|
|
481
|
+
"member_id": {
|
|
482
|
+
"type": "string",
|
|
483
|
+
"description": "(str) The ID of the member to transfer the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.",
|
|
484
|
+
},
|
|
485
|
+
"task_description": {
|
|
486
|
+
"type": "string",
|
|
487
|
+
"description": "(str) A clear and concise description of the task the member should achieve.",
|
|
488
|
+
},
|
|
489
|
+
"expected_output": {
|
|
490
|
+
"type": "string",
|
|
491
|
+
"description": "(str) The expected output from the member (optional).",
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
"additionalProperties": False,
|
|
495
|
+
"required": ["member_id", "task_description"],
|
|
496
|
+
},
|
|
497
|
+
}
|
|
498
|
+
],
|
|
499
|
+
"instructions": None,
|
|
500
|
+
"members": [
|
|
501
|
+
{
|
|
502
|
+
"agent_id": "basic-agent",
|
|
503
|
+
"name": "Basic Agent",
|
|
504
|
+
"description": None,
|
|
505
|
+
"instructions": None,
|
|
506
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI gpt-4o"},
|
|
507
|
+
"tools": None,
|
|
508
|
+
"memory": {
|
|
509
|
+
"app_name": "Memory",
|
|
510
|
+
"app_url": None,
|
|
511
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
512
|
+
},
|
|
513
|
+
"knowledge": None,
|
|
514
|
+
"session_table": "agno_sessions",
|
|
515
|
+
"memory_table": "agno_memories",
|
|
516
|
+
"knowledge_table": None,
|
|
517
|
+
}
|
|
518
|
+
],
|
|
519
|
+
"expected_output": None,
|
|
520
|
+
"dependencies": None,
|
|
521
|
+
"enable_agentic_context": False,
|
|
522
|
+
"memory": {
|
|
523
|
+
"app_name": "Memory",
|
|
524
|
+
"app_url": None,
|
|
525
|
+
"model": {"name": "OpenAIChat", "model": "gpt-4o", "provider": "OpenAI"},
|
|
526
|
+
},
|
|
527
|
+
"knowledge": None,
|
|
528
|
+
"async_mode": False,
|
|
529
|
+
"session_table": "agno_sessions",
|
|
530
|
+
"memory_table": "agno_memories",
|
|
531
|
+
"knowledge_table": None,
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
},
|
|
536
|
+
404: {"description": "Team not found", "model": NotFoundResponse},
|
|
537
|
+
},
|
|
538
|
+
dependencies=[Depends(require_resource_access("teams", "read", "team_id"))],
|
|
539
|
+
)
|
|
540
|
+
async def get_team(team_id: str, request: Request) -> TeamResponse:
|
|
541
|
+
team = get_team_by_id(team_id=team_id, teams=os.teams, db=os.db, registry=registry, create_fresh=True)
|
|
542
|
+
if team is None:
|
|
543
|
+
raise HTTPException(status_code=404, detail="Team not found")
|
|
544
|
+
|
|
545
|
+
if isinstance(team, RemoteTeam):
|
|
546
|
+
return await team.get_team_config()
|
|
547
|
+
else:
|
|
548
|
+
return await TeamResponse.from_team(team=team)
|
|
549
|
+
|
|
550
|
+
return router
|