agno 2.0.0rc2__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 +6009 -2874
- 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 +595 -187
- 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 +3 -0
- agno/knowledge/types.py +9 -0
- agno/knowledge/utils.py +20 -0
- agno/media.py +339 -266
- 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 +1011 -566
- 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 +110 -37
- 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 +143 -4
- 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 +60 -6
- agno/models/openai/chat.py +102 -43
- 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 +81 -5
- 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 -175
- 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 +266 -112
- agno/run/base.py +53 -24
- agno/run/team.py +252 -111
- agno/run/workflow.py +156 -45
- 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 -1692
- agno/tools/brightdata.py +3 -3
- agno/tools/cartesia.py +3 -5
- agno/tools/dalle.py +9 -8
- agno/tools/decorator.py +4 -2
- agno/tools/desi_vocal.py +2 -2
- agno/tools/duckduckgo.py +15 -11
- agno/tools/e2b.py +20 -13
- agno/tools/eleven_labs.py +26 -28
- agno/tools/exa.py +21 -16
- agno/tools/fal.py +4 -4
- agno/tools/file.py +153 -23
- agno/tools/file_generation.py +350 -0
- agno/tools/firecrawl.py +4 -4
- agno/tools/function.py +257 -37
- agno/tools/giphy.py +2 -2
- 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/lumalab.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/azure_openai.py +2 -2
- agno/tools/models/gemini.py +3 -3
- agno/tools/models/groq.py +3 -5
- agno/tools/models/nebius.py +7 -7
- agno/tools/models_labs.py +25 -15
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +4 -9
- agno/tools/opencv.py +3 -3
- agno/tools/parallel.py +314 -0
- agno/tools/replicate.py +7 -7
- 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 +222 -7
- agno/utils/gemini.py +181 -23
- 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 +95 -5
- 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/models/cohere.py +1 -1
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- 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 +183 -135
- 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 +645 -136
- agno/workflow/steps.py +65 -6
- agno/workflow/types.py +71 -33
- agno/workflow/workflow.py +2113 -300
- agno-2.3.0.dist-info/METADATA +618 -0
- agno-2.3.0.dist-info/RECORD +577 -0
- agno-2.3.0.dist-info/licenses/LICENSE +201 -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.0rc2.dist-info/METADATA +0 -355
- agno-2.0.0rc2.dist-info/RECORD +0 -515
- agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
- {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/os/utils.py
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
from typing import Any, Callable, Dict, List, Optional, Union
|
|
2
|
-
from uuid import uuid4
|
|
1
|
+
from typing import Any, Callable, Dict, List, Optional, Set, Union
|
|
3
2
|
|
|
4
|
-
from fastapi import HTTPException, UploadFile
|
|
3
|
+
from fastapi import FastAPI, HTTPException, UploadFile
|
|
4
|
+
from fastapi.routing import APIRoute, APIRouter
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
5
7
|
|
|
6
8
|
from agno.agent.agent import Agent
|
|
7
|
-
from agno.db.base import BaseDb
|
|
9
|
+
from agno.db.base import AsyncBaseDb, BaseDb
|
|
8
10
|
from agno.knowledge.knowledge import Knowledge
|
|
9
11
|
from agno.media import Audio, Image, Video
|
|
10
12
|
from agno.media import File as FileMedia
|
|
13
|
+
from agno.models.message import Message
|
|
14
|
+
from agno.os.config import AgentOSConfig
|
|
11
15
|
from agno.team.team import Team
|
|
12
16
|
from agno.tools import Toolkit
|
|
13
17
|
from agno.tools.function import Function
|
|
@@ -15,23 +19,69 @@ from agno.utils.log import logger
|
|
|
15
19
|
from agno.workflow.workflow import Workflow
|
|
16
20
|
|
|
17
21
|
|
|
18
|
-
def get_db(
|
|
19
|
-
|
|
22
|
+
async def get_db(
|
|
23
|
+
dbs: dict[str, list[Union[BaseDb, AsyncBaseDb]]], db_id: Optional[str] = None, table: Optional[str] = None
|
|
24
|
+
) -> Union[BaseDb, AsyncBaseDb]:
|
|
25
|
+
"""Return the database with the given ID and/or table, or the first database if no ID/table is provided."""
|
|
26
|
+
|
|
27
|
+
if table and not db_id:
|
|
28
|
+
raise HTTPException(status_code=400, detail="The db_id query parameter is required when passing a table")
|
|
29
|
+
|
|
30
|
+
async def _has_table(db: Union[BaseDb, AsyncBaseDb], table_name: str) -> bool:
|
|
31
|
+
"""Check if this database has the specified table (configured and actually exists)."""
|
|
32
|
+
# First check if table name is configured
|
|
33
|
+
is_configured = (
|
|
34
|
+
hasattr(db, "session_table_name")
|
|
35
|
+
and db.session_table_name == table_name
|
|
36
|
+
or hasattr(db, "memory_table_name")
|
|
37
|
+
and db.memory_table_name == table_name
|
|
38
|
+
or hasattr(db, "metrics_table_name")
|
|
39
|
+
and db.metrics_table_name == table_name
|
|
40
|
+
or hasattr(db, "eval_table_name")
|
|
41
|
+
and db.eval_table_name == table_name
|
|
42
|
+
or hasattr(db, "knowledge_table_name")
|
|
43
|
+
and db.knowledge_table_name == table_name
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if not is_configured:
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
# Then check if table actually exists in the database
|
|
50
|
+
try:
|
|
51
|
+
if isinstance(db, AsyncBaseDb):
|
|
52
|
+
# For async databases, await the check
|
|
53
|
+
return await db.table_exists(table_name)
|
|
54
|
+
else:
|
|
55
|
+
# For sync databases, call directly
|
|
56
|
+
return db.table_exists(table_name)
|
|
57
|
+
except (NotImplementedError, AttributeError):
|
|
58
|
+
# If table_exists not implemented, fall back to configuration check
|
|
59
|
+
return is_configured
|
|
60
|
+
|
|
61
|
+
# If db_id is provided, first find the database with that ID
|
|
62
|
+
if db_id:
|
|
63
|
+
target_db_list = dbs.get(db_id)
|
|
64
|
+
if not target_db_list:
|
|
65
|
+
raise HTTPException(status_code=404, detail=f"No database found with id '{db_id}'")
|
|
66
|
+
|
|
67
|
+
# If table is also specified, search through all databases with this ID to find one with the table
|
|
68
|
+
if table:
|
|
69
|
+
for db in target_db_list:
|
|
70
|
+
if await _has_table(db, table):
|
|
71
|
+
return db
|
|
72
|
+
raise HTTPException(status_code=404, detail=f"No database with id '{db_id}' has table '{table}'")
|
|
73
|
+
|
|
74
|
+
# If no table specified, return the first database with this ID
|
|
75
|
+
return target_db_list[0]
|
|
20
76
|
|
|
21
77
|
# Raise if multiple databases are provided but no db_id is provided
|
|
22
|
-
if
|
|
78
|
+
if len(dbs) > 1:
|
|
23
79
|
raise HTTPException(
|
|
24
80
|
status_code=400, detail="The db_id query parameter is required when using multiple databases"
|
|
25
81
|
)
|
|
26
82
|
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
db = dbs.get(db_id)
|
|
30
|
-
if not db:
|
|
31
|
-
raise HTTPException(status_code=404, detail=f"Database with id '{db_id}' not found")
|
|
32
|
-
else:
|
|
33
|
-
db = next(iter(dbs.values()))
|
|
34
|
-
return db
|
|
83
|
+
# Return the first (and only) database
|
|
84
|
+
return next(db for dbs in dbs.values() for db in dbs)
|
|
35
85
|
|
|
36
86
|
|
|
37
87
|
def get_knowledge_instance_by_db_id(knowledge_instances: List[Knowledge], db_id: Optional[str] = None) -> Knowledge:
|
|
@@ -52,17 +102,33 @@ def get_knowledge_instance_by_db_id(knowledge_instances: List[Knowledge], db_id:
|
|
|
52
102
|
|
|
53
103
|
|
|
54
104
|
def get_run_input(run_dict: Dict[str, Any], is_workflow_run: bool = False) -> str:
|
|
55
|
-
"""Get the run input from the given run dictionary
|
|
105
|
+
"""Get the run input from the given run dictionary
|
|
106
|
+
|
|
107
|
+
Uses the RunInput/TeamRunInput object which stores the original user input.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
# For agent or team runs, use the stored input_content
|
|
111
|
+
if not is_workflow_run and run_dict.get("input") is not None:
|
|
112
|
+
input_data = run_dict.get("input")
|
|
113
|
+
if isinstance(input_data, dict) and input_data.get("input_content") is not None:
|
|
114
|
+
return stringify_input_content(input_data["input_content"])
|
|
56
115
|
|
|
57
116
|
if is_workflow_run:
|
|
117
|
+
# Check the input field directly
|
|
118
|
+
if run_dict.get("input") is not None:
|
|
119
|
+
input_value = run_dict.get("input")
|
|
120
|
+
return str(input_value)
|
|
121
|
+
|
|
122
|
+
# Check the step executor runs for fallback
|
|
58
123
|
step_executor_runs = run_dict.get("step_executor_runs", [])
|
|
59
124
|
if step_executor_runs:
|
|
60
|
-
for message in step_executor_runs[0].get("messages", []):
|
|
125
|
+
for message in reversed(step_executor_runs[0].get("messages", [])):
|
|
61
126
|
if message.get("role") == "user":
|
|
62
127
|
return message.get("content", "")
|
|
63
128
|
|
|
129
|
+
# Final fallback: scan messages
|
|
64
130
|
if run_dict.get("messages") is not None:
|
|
65
|
-
for message in run_dict["messages"]:
|
|
131
|
+
for message in reversed(run_dict["messages"]):
|
|
66
132
|
if message.get("role") == "user":
|
|
67
133
|
return message.get("content", "")
|
|
68
134
|
|
|
@@ -79,22 +145,46 @@ def get_session_name(session: Dict[str, Any]) -> str:
|
|
|
79
145
|
|
|
80
146
|
# Otherwise use the original user message
|
|
81
147
|
else:
|
|
82
|
-
runs = session.get("runs", [])
|
|
148
|
+
runs = session.get("runs", []) or []
|
|
83
149
|
|
|
84
150
|
# For teams, identify the first Team run and avoid using the first member's run
|
|
85
151
|
if session.get("session_type") == "team":
|
|
86
|
-
run =
|
|
152
|
+
run = None
|
|
153
|
+
for r in runs:
|
|
154
|
+
# If agent_id is not present, it's a team run
|
|
155
|
+
if not r.get("agent_id"):
|
|
156
|
+
run = r
|
|
157
|
+
break
|
|
158
|
+
|
|
159
|
+
# Fallback to first run if no team run found
|
|
160
|
+
if run is None and runs:
|
|
161
|
+
run = runs[0]
|
|
87
162
|
|
|
88
|
-
# For workflows, pass along the first step_executor_run
|
|
89
163
|
elif session.get("session_type") == "workflow":
|
|
90
164
|
try:
|
|
91
|
-
|
|
165
|
+
workflow_run = runs[0]
|
|
166
|
+
workflow_input = workflow_run.get("input")
|
|
167
|
+
if isinstance(workflow_input, str):
|
|
168
|
+
return workflow_input
|
|
169
|
+
elif isinstance(workflow_input, dict):
|
|
170
|
+
try:
|
|
171
|
+
import json
|
|
172
|
+
|
|
173
|
+
return json.dumps(workflow_input)
|
|
174
|
+
except (TypeError, ValueError):
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
workflow_name = session.get("workflow_data", {}).get("name")
|
|
178
|
+
return f"New {workflow_name} Session" if workflow_name else ""
|
|
92
179
|
except (KeyError, IndexError, TypeError):
|
|
93
180
|
return ""
|
|
94
181
|
|
|
95
182
|
# For agents, use the first run
|
|
96
183
|
else:
|
|
97
|
-
run = runs[0]
|
|
184
|
+
run = runs[0] if runs else None
|
|
185
|
+
|
|
186
|
+
if run is None:
|
|
187
|
+
return ""
|
|
98
188
|
|
|
99
189
|
if not isinstance(run, dict):
|
|
100
190
|
run = run.to_dict()
|
|
@@ -106,31 +196,42 @@ def get_session_name(session: Dict[str, Any]) -> str:
|
|
|
106
196
|
return ""
|
|
107
197
|
|
|
108
198
|
|
|
199
|
+
def extract_input_media(run_dict: Dict[str, Any]) -> Dict[str, Any]:
|
|
200
|
+
input_media: Dict[str, List[Any]] = {
|
|
201
|
+
"images": [],
|
|
202
|
+
"videos": [],
|
|
203
|
+
"audios": [],
|
|
204
|
+
"files": [],
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
input = run_dict.get("input", {})
|
|
208
|
+
input_media["images"].extend(input.get("images", []))
|
|
209
|
+
input_media["videos"].extend(input.get("videos", []))
|
|
210
|
+
input_media["audios"].extend(input.get("audios", []))
|
|
211
|
+
input_media["files"].extend(input.get("files", []))
|
|
212
|
+
|
|
213
|
+
return input_media
|
|
214
|
+
|
|
215
|
+
|
|
109
216
|
def process_image(file: UploadFile) -> Image:
|
|
110
217
|
content = file.file.read()
|
|
111
218
|
if not content:
|
|
112
219
|
raise HTTPException(status_code=400, detail="Empty file")
|
|
113
|
-
return Image(content=content)
|
|
220
|
+
return Image(content=content, format=extract_format(file), mime_type=file.content_type)
|
|
114
221
|
|
|
115
222
|
|
|
116
223
|
def process_audio(file: UploadFile) -> Audio:
|
|
117
224
|
content = file.file.read()
|
|
118
225
|
if not content:
|
|
119
226
|
raise HTTPException(status_code=400, detail="Empty file")
|
|
120
|
-
format =
|
|
121
|
-
if file.filename and "." in file.filename:
|
|
122
|
-
format = file.filename.split(".")[-1].lower()
|
|
123
|
-
elif file.content_type:
|
|
124
|
-
format = file.content_type.split("/")[-1]
|
|
125
|
-
|
|
126
|
-
return Audio(content=content, format=format)
|
|
227
|
+
return Audio(content=content, format=extract_format(file), mime_type=file.content_type)
|
|
127
228
|
|
|
128
229
|
|
|
129
230
|
def process_video(file: UploadFile) -> Video:
|
|
130
231
|
content = file.file.read()
|
|
131
232
|
if not content:
|
|
132
233
|
raise HTTPException(status_code=400, detail="Empty file")
|
|
133
|
-
return Video(content=content, format=file.content_type)
|
|
234
|
+
return Video(content=content, format=extract_format(file), mime_type=file.content_type)
|
|
134
235
|
|
|
135
236
|
|
|
136
237
|
def process_document(file: UploadFile) -> Optional[FileMedia]:
|
|
@@ -138,15 +239,29 @@ def process_document(file: UploadFile) -> Optional[FileMedia]:
|
|
|
138
239
|
content = file.file.read()
|
|
139
240
|
if not content:
|
|
140
241
|
raise HTTPException(status_code=400, detail="Empty file")
|
|
141
|
-
|
|
142
|
-
|
|
242
|
+
return FileMedia(
|
|
243
|
+
content=content, filename=file.filename, format=extract_format(file), mime_type=file.content_type
|
|
244
|
+
)
|
|
143
245
|
except Exception as e:
|
|
144
246
|
logger.error(f"Error processing document {file.filename}: {e}")
|
|
145
247
|
return None
|
|
146
248
|
|
|
147
249
|
|
|
250
|
+
def extract_format(file: UploadFile) -> Optional[str]:
|
|
251
|
+
"""Extract the File format from file name or content_type."""
|
|
252
|
+
# Get the format from the filename
|
|
253
|
+
if file.filename and "." in file.filename:
|
|
254
|
+
return file.filename.split(".")[-1].lower()
|
|
255
|
+
|
|
256
|
+
# Fallback to the file content_type
|
|
257
|
+
if file.content_type:
|
|
258
|
+
return file.content_type.strip().split("/")[-1]
|
|
259
|
+
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
|
|
148
263
|
def format_tools(agent_tools: List[Union[Dict[str, Any], Toolkit, Function, Callable]]):
|
|
149
|
-
formatted_tools = []
|
|
264
|
+
formatted_tools: List[Dict] = []
|
|
150
265
|
if agent_tools is not None:
|
|
151
266
|
for tool in agent_tools:
|
|
152
267
|
if isinstance(tool, dict):
|
|
@@ -164,8 +279,15 @@ def format_tools(agent_tools: List[Union[Dict[str, Any], Toolkit, Function, Call
|
|
|
164
279
|
return formatted_tools
|
|
165
280
|
|
|
166
281
|
|
|
167
|
-
def format_team_tools(team_tools: List[Function]):
|
|
168
|
-
|
|
282
|
+
def format_team_tools(team_tools: List[Union[Function, dict]]):
|
|
283
|
+
formatted_tools: List[Dict] = []
|
|
284
|
+
if team_tools is not None:
|
|
285
|
+
for tool in team_tools:
|
|
286
|
+
if isinstance(tool, dict):
|
|
287
|
+
formatted_tools.append(tool)
|
|
288
|
+
elif isinstance(tool, Function):
|
|
289
|
+
formatted_tools.append(tool.to_dict())
|
|
290
|
+
return formatted_tools
|
|
169
291
|
|
|
170
292
|
|
|
171
293
|
def get_agent_by_id(agent_id: str, agents: Optional[List[Agent]] = None) -> Optional[Agent]:
|
|
@@ -198,6 +320,33 @@ def get_workflow_by_id(workflow_id: str, workflows: Optional[List[Workflow]] = N
|
|
|
198
320
|
return None
|
|
199
321
|
|
|
200
322
|
|
|
323
|
+
# INPUT SCHEMA VALIDATIONS
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def get_agent_input_schema_dict(agent: Agent) -> Optional[Dict[str, Any]]:
|
|
327
|
+
"""Get input schema as dictionary for API responses"""
|
|
328
|
+
|
|
329
|
+
if agent.input_schema is not None:
|
|
330
|
+
try:
|
|
331
|
+
return agent.input_schema.model_json_schema()
|
|
332
|
+
except Exception:
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
return None
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def get_team_input_schema_dict(team: Team) -> Optional[Dict[str, Any]]:
|
|
339
|
+
"""Get input schema as dictionary for API responses"""
|
|
340
|
+
|
|
341
|
+
if team.input_schema is not None:
|
|
342
|
+
try:
|
|
343
|
+
return team.input_schema.model_json_schema()
|
|
344
|
+
except Exception:
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
|
|
201
350
|
def get_workflow_input_schema_dict(workflow: Workflow) -> Optional[Dict[str, Any]]:
|
|
202
351
|
"""Get input schema as dictionary for API responses"""
|
|
203
352
|
|
|
@@ -263,8 +412,219 @@ def _generate_schema_from_params(params: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
263
412
|
return schema
|
|
264
413
|
|
|
265
414
|
|
|
266
|
-
def
|
|
267
|
-
|
|
268
|
-
|
|
415
|
+
def update_cors_middleware(app: FastAPI, new_origins: list):
|
|
416
|
+
existing_origins: List[str] = []
|
|
417
|
+
|
|
418
|
+
# TODO: Allow more options where CORS is properly merged and user can disable this behaviour
|
|
419
|
+
|
|
420
|
+
# Extract existing origins from current CORS middleware
|
|
421
|
+
for middleware in app.user_middleware:
|
|
422
|
+
if middleware.cls == CORSMiddleware:
|
|
423
|
+
if hasattr(middleware, "kwargs"):
|
|
424
|
+
origins_value = middleware.kwargs.get("allow_origins", [])
|
|
425
|
+
if isinstance(origins_value, list):
|
|
426
|
+
existing_origins = origins_value
|
|
427
|
+
else:
|
|
428
|
+
existing_origins = []
|
|
429
|
+
break
|
|
430
|
+
# Merge origins
|
|
431
|
+
merged_origins = list(set(new_origins + existing_origins))
|
|
432
|
+
final_origins = [origin for origin in merged_origins if origin != "*"]
|
|
433
|
+
|
|
434
|
+
# Remove existing CORS
|
|
435
|
+
app.user_middleware = [m for m in app.user_middleware if m.cls != CORSMiddleware]
|
|
436
|
+
app.middleware_stack = None
|
|
437
|
+
|
|
438
|
+
# Add updated CORS
|
|
439
|
+
app.add_middleware(
|
|
440
|
+
CORSMiddleware, # type: ignore
|
|
441
|
+
allow_origins=final_origins,
|
|
442
|
+
allow_credentials=True,
|
|
443
|
+
allow_methods=["*"],
|
|
444
|
+
allow_headers=["*"],
|
|
445
|
+
expose_headers=["*"],
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def get_existing_route_paths(fastapi_app: FastAPI) -> Dict[str, List[str]]:
|
|
450
|
+
"""Get all existing route paths and methods from the FastAPI app.
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
Dict[str, List[str]]: Dictionary mapping paths to list of HTTP methods
|
|
454
|
+
"""
|
|
455
|
+
existing_paths: Dict[str, Any] = {}
|
|
456
|
+
for route in fastapi_app.routes:
|
|
457
|
+
if isinstance(route, APIRoute):
|
|
458
|
+
path = route.path
|
|
459
|
+
methods = list(route.methods) if route.methods else []
|
|
460
|
+
if path in existing_paths:
|
|
461
|
+
existing_paths[path].extend(methods)
|
|
462
|
+
else:
|
|
463
|
+
existing_paths[path] = methods
|
|
464
|
+
return existing_paths
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def find_conflicting_routes(fastapi_app: FastAPI, router: APIRouter) -> List[Dict[str, Any]]:
|
|
468
|
+
"""Find conflicting routes in the FastAPI app.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
fastapi_app: The FastAPI app with all existing routes
|
|
472
|
+
router: The APIRouter to add
|
|
473
|
+
|
|
474
|
+
Returns:
|
|
475
|
+
List[Dict[str, Any]]: List of conflicting routes
|
|
476
|
+
"""
|
|
477
|
+
existing_paths = get_existing_route_paths(fastapi_app)
|
|
478
|
+
|
|
479
|
+
conflicts = []
|
|
480
|
+
|
|
481
|
+
for route in router.routes:
|
|
482
|
+
if isinstance(route, APIRoute):
|
|
483
|
+
full_path = route.path
|
|
484
|
+
route_methods = list(route.methods) if route.methods else []
|
|
485
|
+
|
|
486
|
+
if full_path in existing_paths:
|
|
487
|
+
conflicting_methods: Set[str] = set(route_methods) & set(existing_paths[full_path])
|
|
488
|
+
if conflicting_methods:
|
|
489
|
+
conflicts.append({"path": full_path, "methods": list(conflicting_methods), "route": route})
|
|
490
|
+
return conflicts
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def load_yaml_config(config_file_path: str) -> AgentOSConfig:
|
|
494
|
+
"""Load a YAML config file and return the configuration as an AgentOSConfig instance."""
|
|
495
|
+
from pathlib import Path
|
|
496
|
+
|
|
497
|
+
import yaml
|
|
498
|
+
|
|
499
|
+
# Validate that the path points to a YAML file
|
|
500
|
+
path = Path(config_file_path)
|
|
501
|
+
if path.suffix.lower() not in [".yaml", ".yml"]:
|
|
502
|
+
raise ValueError(f"Config file must have a .yaml or .yml extension, got: {config_file_path}")
|
|
503
|
+
|
|
504
|
+
# Load the YAML file
|
|
505
|
+
with open(config_file_path, "r") as f:
|
|
506
|
+
return AgentOSConfig.model_validate(yaml.safe_load(f))
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
def collect_mcp_tools_from_team(team: Team, mcp_tools: List[Any]) -> None:
|
|
510
|
+
"""Recursively collect MCP tools from a team and its members."""
|
|
511
|
+
# Check the team tools
|
|
512
|
+
if team.tools:
|
|
513
|
+
for tool in team.tools:
|
|
514
|
+
type_name = type(tool).__name__
|
|
515
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
516
|
+
if tool not in mcp_tools:
|
|
517
|
+
mcp_tools.append(tool)
|
|
518
|
+
|
|
519
|
+
# Recursively check team members
|
|
520
|
+
if team.members:
|
|
521
|
+
for member in team.members:
|
|
522
|
+
if isinstance(member, Agent):
|
|
523
|
+
if member.tools:
|
|
524
|
+
for tool in member.tools:
|
|
525
|
+
type_name = type(tool).__name__
|
|
526
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
527
|
+
if tool not in mcp_tools:
|
|
528
|
+
mcp_tools.append(tool)
|
|
529
|
+
|
|
530
|
+
elif isinstance(member, Team):
|
|
531
|
+
# Recursively check nested team
|
|
532
|
+
collect_mcp_tools_from_team(member, mcp_tools)
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def collect_mcp_tools_from_workflow(workflow: Workflow, mcp_tools: List[Any]) -> None:
|
|
536
|
+
"""Recursively collect MCP tools from a workflow and its steps."""
|
|
537
|
+
from agno.workflow.steps import Steps
|
|
538
|
+
|
|
539
|
+
# Recursively check workflow steps
|
|
540
|
+
if workflow.steps:
|
|
541
|
+
if isinstance(workflow.steps, list):
|
|
542
|
+
# Handle list of steps
|
|
543
|
+
for step in workflow.steps:
|
|
544
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
545
|
+
|
|
546
|
+
elif isinstance(workflow.steps, Steps):
|
|
547
|
+
# Handle Steps container
|
|
548
|
+
if steps := workflow.steps.steps:
|
|
549
|
+
for step in steps:
|
|
550
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
551
|
+
|
|
552
|
+
elif callable(workflow.steps):
|
|
553
|
+
pass
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
def collect_mcp_tools_from_workflow_step(step: Any, mcp_tools: List[Any]) -> None:
|
|
557
|
+
"""Collect MCP tools from a single workflow step."""
|
|
558
|
+
from agno.workflow.condition import Condition
|
|
559
|
+
from agno.workflow.loop import Loop
|
|
560
|
+
from agno.workflow.parallel import Parallel
|
|
561
|
+
from agno.workflow.router import Router
|
|
562
|
+
from agno.workflow.step import Step
|
|
563
|
+
from agno.workflow.steps import Steps
|
|
564
|
+
|
|
565
|
+
if isinstance(step, Step):
|
|
566
|
+
# Check step's agent
|
|
567
|
+
if step.agent:
|
|
568
|
+
if step.agent.tools:
|
|
569
|
+
for tool in step.agent.tools:
|
|
570
|
+
type_name = type(tool).__name__
|
|
571
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
572
|
+
if tool not in mcp_tools:
|
|
573
|
+
mcp_tools.append(tool)
|
|
574
|
+
# Check step's team
|
|
575
|
+
if step.team:
|
|
576
|
+
collect_mcp_tools_from_team(step.team, mcp_tools)
|
|
577
|
+
|
|
578
|
+
elif isinstance(step, Steps):
|
|
579
|
+
if steps := step.steps:
|
|
580
|
+
for step in steps:
|
|
581
|
+
collect_mcp_tools_from_workflow_step(step, mcp_tools)
|
|
582
|
+
|
|
583
|
+
elif isinstance(step, (Parallel, Loop, Condition, Router)):
|
|
584
|
+
# These contain other steps - recursively check them
|
|
585
|
+
if hasattr(step, "steps") and step.steps:
|
|
586
|
+
for sub_step in step.steps:
|
|
587
|
+
collect_mcp_tools_from_workflow_step(sub_step, mcp_tools)
|
|
588
|
+
|
|
589
|
+
elif isinstance(step, Agent):
|
|
590
|
+
# Direct agent in workflow steps
|
|
591
|
+
if step.tools:
|
|
592
|
+
for tool in step.tools:
|
|
593
|
+
type_name = type(tool).__name__
|
|
594
|
+
if type_name in ("MCPTools", "MultiMCPTools"):
|
|
595
|
+
if tool not in mcp_tools:
|
|
596
|
+
mcp_tools.append(tool)
|
|
597
|
+
|
|
598
|
+
elif isinstance(step, Team):
|
|
599
|
+
# Direct team in workflow steps
|
|
600
|
+
collect_mcp_tools_from_team(step, mcp_tools)
|
|
601
|
+
|
|
602
|
+
elif isinstance(step, Workflow):
|
|
603
|
+
# Nested workflow
|
|
604
|
+
collect_mcp_tools_from_workflow(step, mcp_tools)
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def stringify_input_content(input_content: Union[str, Dict[str, Any], List[Any], BaseModel]) -> str:
|
|
608
|
+
"""Convert any given input_content into its string representation.
|
|
609
|
+
|
|
610
|
+
This handles both serialized (dict) and live (object) input_content formats.
|
|
611
|
+
"""
|
|
612
|
+
import json
|
|
613
|
+
|
|
614
|
+
if isinstance(input_content, str):
|
|
615
|
+
return input_content
|
|
616
|
+
elif isinstance(input_content, Message):
|
|
617
|
+
return json.dumps(input_content.to_dict())
|
|
618
|
+
elif isinstance(input_content, dict):
|
|
619
|
+
return json.dumps(input_content, indent=2, default=str)
|
|
620
|
+
elif isinstance(input_content, list):
|
|
621
|
+
if input_content:
|
|
622
|
+
# Handle live Message objects
|
|
623
|
+
if isinstance(input_content[0], Message):
|
|
624
|
+
return json.dumps([m.to_dict() for m in input_content])
|
|
625
|
+
# Handle serialized Message dicts
|
|
626
|
+
elif isinstance(input_content[0], dict) and input_content[0].get("role") == "user":
|
|
627
|
+
return input_content[0].get("content", str(input_content))
|
|
628
|
+
return str(input_content)
|
|
269
629
|
else:
|
|
270
|
-
return str(
|
|
630
|
+
return str(input_content)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
|
+
from agno.models.base import Model
|
|
6
|
+
from agno.models.message import Message
|
|
7
|
+
from agno.utils.log import logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def is_anthropic_reasoning_model(reasoning_model: Model) -> bool:
|
|
11
|
+
"""Check if the model is an Anthropic Claude model with thinking support."""
|
|
12
|
+
is_claude = reasoning_model.__class__.__name__ == "Claude"
|
|
13
|
+
if not is_claude:
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
# Check if provider is Anthropic (not VertexAI)
|
|
17
|
+
is_anthropic_provider = hasattr(reasoning_model, "provider") and reasoning_model.provider == "Anthropic"
|
|
18
|
+
|
|
19
|
+
# Check if thinking parameter is set
|
|
20
|
+
has_thinking = hasattr(reasoning_model, "thinking") and reasoning_model.thinking is not None
|
|
21
|
+
|
|
22
|
+
return is_claude and is_anthropic_provider and has_thinking
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_anthropic_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
26
|
+
"""Get reasoning from an Anthropic Claude model."""
|
|
27
|
+
from agno.run.agent import RunOutput
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
logger.warning(f"Reasoning error: {e}")
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
reasoning_content: str = ""
|
|
36
|
+
redacted_reasoning_content: Optional[str] = None
|
|
37
|
+
|
|
38
|
+
if reasoning_agent_response.messages is not None:
|
|
39
|
+
for msg in reasoning_agent_response.messages:
|
|
40
|
+
if msg.reasoning_content is not None:
|
|
41
|
+
reasoning_content = msg.reasoning_content
|
|
42
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
43
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
44
|
+
break
|
|
45
|
+
|
|
46
|
+
return Message(
|
|
47
|
+
role="assistant",
|
|
48
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
49
|
+
reasoning_content=reasoning_content,
|
|
50
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async def aget_anthropic_reasoning(reasoning_agent: "Agent", messages: List[Message]) -> Optional[Message]: # type: ignore # noqa: F821
|
|
55
|
+
"""Get reasoning from an Anthropic Claude model asynchronously."""
|
|
56
|
+
from agno.run.agent import RunOutput
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"Reasoning error: {e}")
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
reasoning_content: str = ""
|
|
65
|
+
redacted_reasoning_content: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
if reasoning_agent_response.messages is not None:
|
|
68
|
+
for msg in reasoning_agent_response.messages:
|
|
69
|
+
if msg.reasoning_content is not None:
|
|
70
|
+
reasoning_content = msg.reasoning_content
|
|
71
|
+
if hasattr(msg, "redacted_reasoning_content") and msg.redacted_reasoning_content is not None:
|
|
72
|
+
redacted_reasoning_content = msg.redacted_reasoning_content
|
|
73
|
+
break
|
|
74
|
+
|
|
75
|
+
return Message(
|
|
76
|
+
role="assistant",
|
|
77
|
+
content=f"<thinking>\n{reasoning_content}\n</thinking>",
|
|
78
|
+
reasoning_content=reasoning_content,
|
|
79
|
+
redacted_reasoning_content=redacted_reasoning_content,
|
|
80
|
+
)
|
|
@@ -20,7 +20,7 @@ def get_ai_foundry_reasoning(reasoning_agent: "Agent", messages: List[Message])
|
|
|
20
20
|
from agno.run.agent import RunOutput
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
|
-
reasoning_agent_response: RunOutput = reasoning_agent.run(
|
|
23
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
24
24
|
except Exception as e:
|
|
25
25
|
logger.warning(f"Reasoning error: {e}")
|
|
26
26
|
return None
|
|
@@ -46,7 +46,7 @@ async def aget_ai_foundry_reasoning(reasoning_agent: "Agent", messages: List[Mes
|
|
|
46
46
|
from agno.run.agent import RunOutput
|
|
47
47
|
|
|
48
48
|
try:
|
|
49
|
-
reasoning_agent_response: RunOutput = await reasoning_agent.arun(
|
|
49
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
50
50
|
except Exception as e:
|
|
51
51
|
logger.warning(f"Reasoning error: {e}")
|
|
52
52
|
return None
|
agno/reasoning/deepseek.py
CHANGED
|
@@ -20,7 +20,7 @@ def get_deepseek_reasoning(reasoning_agent: "Agent", messages: List[Message]) ->
|
|
|
20
20
|
message.role = "system"
|
|
21
21
|
|
|
22
22
|
try:
|
|
23
|
-
reasoning_agent_response: RunOutput = reasoning_agent.run(
|
|
23
|
+
reasoning_agent_response: RunOutput = reasoning_agent.run(input=messages)
|
|
24
24
|
except Exception as e:
|
|
25
25
|
logger.warning(f"Reasoning error: {e}")
|
|
26
26
|
return None
|
|
@@ -46,7 +46,7 @@ async def aget_deepseek_reasoning(reasoning_agent: "Agent", messages: List[Messa
|
|
|
46
46
|
message.role = "system"
|
|
47
47
|
|
|
48
48
|
try:
|
|
49
|
-
reasoning_agent_response: RunOutput = await reasoning_agent.arun(
|
|
49
|
+
reasoning_agent_response: RunOutput = await reasoning_agent.arun(input=messages)
|
|
50
50
|
except Exception as e:
|
|
51
51
|
logger.warning(f"Reasoning error: {e}")
|
|
52
52
|
return None
|