agno 0.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/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- 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/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- 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 +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -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/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- 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/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -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 +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- 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/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- 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 +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- 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 +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- 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/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
"""Logic used by the AG-UI router."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import uuid
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from dataclasses import asdict, dataclass, is_dataclass
|
|
7
|
+
from typing import Any, AsyncIterator, Dict, List, Optional, Set, Tuple, Union
|
|
8
|
+
|
|
9
|
+
from ag_ui.core import (
|
|
10
|
+
BaseEvent,
|
|
11
|
+
CustomEvent,
|
|
12
|
+
EventType,
|
|
13
|
+
RunFinishedEvent,
|
|
14
|
+
StepFinishedEvent,
|
|
15
|
+
StepStartedEvent,
|
|
16
|
+
TextMessageContentEvent,
|
|
17
|
+
TextMessageEndEvent,
|
|
18
|
+
TextMessageStartEvent,
|
|
19
|
+
ToolCallArgsEvent,
|
|
20
|
+
ToolCallEndEvent,
|
|
21
|
+
ToolCallResultEvent,
|
|
22
|
+
ToolCallStartEvent,
|
|
23
|
+
)
|
|
24
|
+
from ag_ui.core.types import Message as AGUIMessage
|
|
25
|
+
from pydantic import BaseModel
|
|
26
|
+
|
|
27
|
+
from agno.models.message import Message
|
|
28
|
+
from agno.run.agent import RunContentEvent, RunEvent, RunOutputEvent, RunPausedEvent
|
|
29
|
+
from agno.run.team import RunContentEvent as TeamRunContentEvent
|
|
30
|
+
from agno.run.team import TeamRunEvent, TeamRunOutputEvent
|
|
31
|
+
from agno.utils.log import log_debug, log_warning
|
|
32
|
+
from agno.utils.message import get_text_from_message
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def validate_agui_state(state: Any, thread_id: str) -> Optional[Dict[str, Any]]:
|
|
36
|
+
"""Validate the given AGUI state is of the expected type (dict)."""
|
|
37
|
+
if state is None:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
if isinstance(state, dict):
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
if isinstance(state, BaseModel):
|
|
44
|
+
try:
|
|
45
|
+
return state.model_dump()
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
if is_dataclass(state):
|
|
50
|
+
try:
|
|
51
|
+
return asdict(state) # type: ignore
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
if hasattr(state, "to_dict") and callable(getattr(state, "to_dict")):
|
|
56
|
+
try:
|
|
57
|
+
result = state.to_dict() # type: ignore
|
|
58
|
+
if isinstance(result, dict):
|
|
59
|
+
return result
|
|
60
|
+
except Exception:
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
log_warning(f"AGUI state must be a dict, got {type(state).__name__}. State will be ignored. Thread: {thread_id}")
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class EventBuffer:
|
|
69
|
+
"""Buffer to manage event ordering constraints, relevant when mapping Agno responses to AG-UI events."""
|
|
70
|
+
|
|
71
|
+
active_tool_call_ids: Set[str] # All currently active tool calls
|
|
72
|
+
ended_tool_call_ids: Set[str] # All tool calls that have ended
|
|
73
|
+
current_text_message_id: str = "" # ID of the current text message context (for tool call parenting)
|
|
74
|
+
next_text_message_id: str = "" # Pre-generated ID for the next text message
|
|
75
|
+
pending_tool_calls_parent_id: str = "" # Parent message ID for pending tool calls
|
|
76
|
+
|
|
77
|
+
def __init__(self):
|
|
78
|
+
self.active_tool_call_ids = set()
|
|
79
|
+
self.ended_tool_call_ids = set()
|
|
80
|
+
self.current_text_message_id = ""
|
|
81
|
+
self.next_text_message_id = str(uuid.uuid4())
|
|
82
|
+
self.pending_tool_calls_parent_id = ""
|
|
83
|
+
|
|
84
|
+
def start_tool_call(self, tool_call_id: str) -> None:
|
|
85
|
+
"""Start a new tool call."""
|
|
86
|
+
self.active_tool_call_ids.add(tool_call_id)
|
|
87
|
+
|
|
88
|
+
def end_tool_call(self, tool_call_id: str) -> None:
|
|
89
|
+
"""End a tool call."""
|
|
90
|
+
self.active_tool_call_ids.discard(tool_call_id)
|
|
91
|
+
self.ended_tool_call_ids.add(tool_call_id)
|
|
92
|
+
|
|
93
|
+
def start_text_message(self) -> str:
|
|
94
|
+
"""Start a new text message and return its ID."""
|
|
95
|
+
# Use the pre-generated next ID as current, and generate a new next ID
|
|
96
|
+
self.current_text_message_id = self.next_text_message_id
|
|
97
|
+
self.next_text_message_id = str(uuid.uuid4())
|
|
98
|
+
return self.current_text_message_id
|
|
99
|
+
|
|
100
|
+
def get_parent_message_id_for_tool_call(self) -> str:
|
|
101
|
+
"""Get the message ID to use as parent for tool calls."""
|
|
102
|
+
# If we have a pending parent ID set (from text message end), use that
|
|
103
|
+
if self.pending_tool_calls_parent_id:
|
|
104
|
+
return self.pending_tool_calls_parent_id
|
|
105
|
+
# Otherwise use current text message ID
|
|
106
|
+
return self.current_text_message_id
|
|
107
|
+
|
|
108
|
+
def set_pending_tool_calls_parent_id(self, parent_id: str) -> None:
|
|
109
|
+
"""Set the parent message ID for upcoming tool calls."""
|
|
110
|
+
self.pending_tool_calls_parent_id = parent_id
|
|
111
|
+
|
|
112
|
+
def clear_pending_tool_calls_parent_id(self) -> None:
|
|
113
|
+
"""Clear the pending parent ID when a new text message starts."""
|
|
114
|
+
self.pending_tool_calls_parent_id = ""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def convert_agui_messages_to_agno_messages(messages: List[AGUIMessage]) -> List[Message]:
|
|
118
|
+
"""Convert AG-UI messages to Agno messages."""
|
|
119
|
+
# First pass: collect all tool_call_ids that have results
|
|
120
|
+
tool_call_ids_with_results: Set[str] = set()
|
|
121
|
+
for msg in messages:
|
|
122
|
+
if msg.role == "tool" and msg.tool_call_id:
|
|
123
|
+
tool_call_ids_with_results.add(msg.tool_call_id)
|
|
124
|
+
|
|
125
|
+
# Second pass: convert messages
|
|
126
|
+
result: List[Message] = []
|
|
127
|
+
seen_tool_call_ids: Set[str] = set()
|
|
128
|
+
|
|
129
|
+
for msg in messages:
|
|
130
|
+
if msg.role == "tool":
|
|
131
|
+
# Deduplicate tool results - keep only first occurrence
|
|
132
|
+
if msg.tool_call_id in seen_tool_call_ids:
|
|
133
|
+
log_debug(f"Skipping duplicate AGUI tool result: {msg.tool_call_id}")
|
|
134
|
+
continue
|
|
135
|
+
seen_tool_call_ids.add(msg.tool_call_id)
|
|
136
|
+
result.append(Message(role="tool", tool_call_id=msg.tool_call_id, content=msg.content))
|
|
137
|
+
|
|
138
|
+
elif msg.role == "assistant":
|
|
139
|
+
tool_calls = None
|
|
140
|
+
if msg.tool_calls:
|
|
141
|
+
# Filter tool_calls to only those with results in this message sequence
|
|
142
|
+
filtered_calls = [call for call in msg.tool_calls if call.id in tool_call_ids_with_results]
|
|
143
|
+
if filtered_calls:
|
|
144
|
+
tool_calls = [call.model_dump() for call in filtered_calls]
|
|
145
|
+
result.append(Message(role="assistant", content=msg.content, tool_calls=tool_calls))
|
|
146
|
+
|
|
147
|
+
elif msg.role == "user":
|
|
148
|
+
result.append(Message(role="user", content=msg.content))
|
|
149
|
+
|
|
150
|
+
elif msg.role == "system":
|
|
151
|
+
pass # Skip - agent builds its own system message from configuration
|
|
152
|
+
|
|
153
|
+
else:
|
|
154
|
+
log_warning(f"Unknown AGUI message role: {msg.role}")
|
|
155
|
+
|
|
156
|
+
return result
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def extract_team_response_chunk_content(response: TeamRunContentEvent) -> str:
|
|
160
|
+
"""Given a response stream chunk, find and extract the content."""
|
|
161
|
+
|
|
162
|
+
# Handle Team members' responses
|
|
163
|
+
members_content = []
|
|
164
|
+
if hasattr(response, "member_responses") and response.member_responses: # type: ignore
|
|
165
|
+
for member_resp in response.member_responses: # type: ignore
|
|
166
|
+
if isinstance(member_resp, RunContentEvent):
|
|
167
|
+
member_content = extract_response_chunk_content(member_resp)
|
|
168
|
+
if member_content:
|
|
169
|
+
members_content.append(f"Team member: {member_content}")
|
|
170
|
+
elif isinstance(member_resp, TeamRunContentEvent):
|
|
171
|
+
member_content = extract_team_response_chunk_content(member_resp)
|
|
172
|
+
if member_content:
|
|
173
|
+
members_content.append(f"Team member: {member_content}")
|
|
174
|
+
members_response = "\n".join(members_content) if members_content else ""
|
|
175
|
+
|
|
176
|
+
# Handle structured outputs
|
|
177
|
+
main_content = get_text_from_message(response.content) if response.content is not None else ""
|
|
178
|
+
|
|
179
|
+
return main_content + members_response
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def extract_response_chunk_content(response: RunContentEvent) -> str:
|
|
183
|
+
"""Given a response stream chunk, find and extract the content."""
|
|
184
|
+
|
|
185
|
+
if hasattr(response, "messages") and response.messages: # type: ignore
|
|
186
|
+
for msg in reversed(response.messages): # type: ignore
|
|
187
|
+
if hasattr(msg, "role") and msg.role == "assistant" and hasattr(msg, "content") and msg.content:
|
|
188
|
+
# Handle structured outputs from messages
|
|
189
|
+
return get_text_from_message(msg.content)
|
|
190
|
+
|
|
191
|
+
# Handle structured outputs
|
|
192
|
+
return get_text_from_message(response.content) if response.content is not None else ""
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _create_events_from_chunk(
|
|
196
|
+
chunk: Union[RunOutputEvent, TeamRunOutputEvent],
|
|
197
|
+
message_id: str,
|
|
198
|
+
message_started: bool,
|
|
199
|
+
event_buffer: EventBuffer,
|
|
200
|
+
) -> Tuple[List[BaseEvent], bool, str]:
|
|
201
|
+
"""
|
|
202
|
+
Process a single chunk and return events to emit + updated message_started state.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
chunk: The event chunk to process
|
|
206
|
+
message_id: Current message identifier
|
|
207
|
+
message_started: Whether a message is currently active
|
|
208
|
+
event_buffer: Event buffer for tracking tool call state
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
Tuple of (events_to_emit, new_message_started_state, message_id)
|
|
212
|
+
"""
|
|
213
|
+
events_to_emit: List[BaseEvent] = []
|
|
214
|
+
|
|
215
|
+
# Extract content if the contextual event is a content event
|
|
216
|
+
if chunk.event == RunEvent.run_content:
|
|
217
|
+
content = extract_response_chunk_content(chunk) # type: ignore
|
|
218
|
+
elif chunk.event == TeamRunEvent.run_content:
|
|
219
|
+
content = extract_team_response_chunk_content(chunk) # type: ignore
|
|
220
|
+
else:
|
|
221
|
+
content = None
|
|
222
|
+
|
|
223
|
+
# Handle text responses
|
|
224
|
+
if content is not None:
|
|
225
|
+
# Handle the message start event, emitted once per message
|
|
226
|
+
if not message_started:
|
|
227
|
+
message_started = True
|
|
228
|
+
message_id = event_buffer.start_text_message()
|
|
229
|
+
|
|
230
|
+
# Clear pending tool calls parent ID when starting new text message
|
|
231
|
+
event_buffer.clear_pending_tool_calls_parent_id()
|
|
232
|
+
|
|
233
|
+
start_event = TextMessageStartEvent(
|
|
234
|
+
type=EventType.TEXT_MESSAGE_START,
|
|
235
|
+
message_id=message_id,
|
|
236
|
+
role="assistant",
|
|
237
|
+
)
|
|
238
|
+
events_to_emit.append(start_event)
|
|
239
|
+
|
|
240
|
+
# Handle the text content event, emitted once per text chunk
|
|
241
|
+
if content is not None and content != "":
|
|
242
|
+
content_event = TextMessageContentEvent(
|
|
243
|
+
type=EventType.TEXT_MESSAGE_CONTENT,
|
|
244
|
+
message_id=message_id,
|
|
245
|
+
delta=content,
|
|
246
|
+
)
|
|
247
|
+
events_to_emit.append(content_event) # type: ignore
|
|
248
|
+
|
|
249
|
+
# Handle starting a new tool
|
|
250
|
+
elif chunk.event == RunEvent.tool_call_started or chunk.event == TeamRunEvent.tool_call_started:
|
|
251
|
+
if chunk.tool is not None: # type: ignore
|
|
252
|
+
tool_call = chunk.tool # type: ignore
|
|
253
|
+
|
|
254
|
+
# End current text message and handle for tool calls
|
|
255
|
+
current_message_id = message_id
|
|
256
|
+
if message_started:
|
|
257
|
+
# End the current text message
|
|
258
|
+
end_message_event = TextMessageEndEvent(type=EventType.TEXT_MESSAGE_END, message_id=current_message_id)
|
|
259
|
+
events_to_emit.append(end_message_event)
|
|
260
|
+
|
|
261
|
+
# Set this message as the parent for any upcoming tool calls
|
|
262
|
+
# This ensures multiple sequential tool calls all use the same parent
|
|
263
|
+
event_buffer.set_pending_tool_calls_parent_id(current_message_id)
|
|
264
|
+
|
|
265
|
+
# Reset message started state and generate new message_id for future messages
|
|
266
|
+
message_started = False
|
|
267
|
+
message_id = str(uuid.uuid4())
|
|
268
|
+
|
|
269
|
+
# Get the parent message ID - this will use pending parent if set, ensuring multiple tool calls in sequence have the same parent
|
|
270
|
+
parent_message_id = event_buffer.get_parent_message_id_for_tool_call()
|
|
271
|
+
|
|
272
|
+
if not parent_message_id:
|
|
273
|
+
# Create parent message for tool calls without preceding assistant message
|
|
274
|
+
parent_message_id = str(uuid.uuid4())
|
|
275
|
+
|
|
276
|
+
# Emit a text message to serve as the parent
|
|
277
|
+
text_start = TextMessageStartEvent(
|
|
278
|
+
type=EventType.TEXT_MESSAGE_START,
|
|
279
|
+
message_id=parent_message_id,
|
|
280
|
+
role="assistant",
|
|
281
|
+
)
|
|
282
|
+
events_to_emit.append(text_start)
|
|
283
|
+
|
|
284
|
+
text_end = TextMessageEndEvent(
|
|
285
|
+
type=EventType.TEXT_MESSAGE_END,
|
|
286
|
+
message_id=parent_message_id,
|
|
287
|
+
)
|
|
288
|
+
events_to_emit.append(text_end)
|
|
289
|
+
|
|
290
|
+
# Set this as the pending parent for subsequent tool calls in this batch
|
|
291
|
+
event_buffer.set_pending_tool_calls_parent_id(parent_message_id)
|
|
292
|
+
|
|
293
|
+
start_event = ToolCallStartEvent(
|
|
294
|
+
type=EventType.TOOL_CALL_START,
|
|
295
|
+
tool_call_id=tool_call.tool_call_id, # type: ignore
|
|
296
|
+
tool_call_name=tool_call.tool_name, # type: ignore
|
|
297
|
+
parent_message_id=parent_message_id,
|
|
298
|
+
)
|
|
299
|
+
events_to_emit.append(start_event)
|
|
300
|
+
|
|
301
|
+
args_event = ToolCallArgsEvent(
|
|
302
|
+
type=EventType.TOOL_CALL_ARGS,
|
|
303
|
+
tool_call_id=tool_call.tool_call_id, # type: ignore
|
|
304
|
+
delta=json.dumps(tool_call.tool_args),
|
|
305
|
+
)
|
|
306
|
+
events_to_emit.append(args_event) # type: ignore
|
|
307
|
+
|
|
308
|
+
# Handle tool call completion
|
|
309
|
+
elif chunk.event == RunEvent.tool_call_completed or chunk.event == TeamRunEvent.tool_call_completed:
|
|
310
|
+
if chunk.tool is not None: # type: ignore
|
|
311
|
+
tool_call = chunk.tool # type: ignore
|
|
312
|
+
if tool_call.tool_call_id not in event_buffer.ended_tool_call_ids:
|
|
313
|
+
end_event = ToolCallEndEvent(
|
|
314
|
+
type=EventType.TOOL_CALL_END,
|
|
315
|
+
tool_call_id=tool_call.tool_call_id, # type: ignore
|
|
316
|
+
)
|
|
317
|
+
events_to_emit.append(end_event)
|
|
318
|
+
|
|
319
|
+
if tool_call.result is not None:
|
|
320
|
+
result_event = ToolCallResultEvent(
|
|
321
|
+
type=EventType.TOOL_CALL_RESULT,
|
|
322
|
+
tool_call_id=tool_call.tool_call_id, # type: ignore
|
|
323
|
+
content=str(tool_call.result),
|
|
324
|
+
role="tool",
|
|
325
|
+
message_id=str(uuid.uuid4()),
|
|
326
|
+
)
|
|
327
|
+
events_to_emit.append(result_event)
|
|
328
|
+
|
|
329
|
+
# Handle reasoning
|
|
330
|
+
elif chunk.event == RunEvent.reasoning_started:
|
|
331
|
+
step_started_event = StepStartedEvent(type=EventType.STEP_STARTED, step_name="reasoning")
|
|
332
|
+
events_to_emit.append(step_started_event)
|
|
333
|
+
elif chunk.event == RunEvent.reasoning_completed:
|
|
334
|
+
step_finished_event = StepFinishedEvent(type=EventType.STEP_FINISHED, step_name="reasoning")
|
|
335
|
+
events_to_emit.append(step_finished_event)
|
|
336
|
+
|
|
337
|
+
# Handle custom events
|
|
338
|
+
elif chunk.event == RunEvent.custom_event:
|
|
339
|
+
# Use the name of the event class if available, otherwise default to the CustomEvent
|
|
340
|
+
try:
|
|
341
|
+
custom_event_name = chunk.__class__.__name__
|
|
342
|
+
except Exception:
|
|
343
|
+
custom_event_name = chunk.event
|
|
344
|
+
|
|
345
|
+
# Use the complete Agno event as value if parsing it works, else the event content field
|
|
346
|
+
try:
|
|
347
|
+
custom_event_value = chunk.to_dict()
|
|
348
|
+
except Exception:
|
|
349
|
+
custom_event_value = chunk.content # type: ignore
|
|
350
|
+
|
|
351
|
+
custom_event = CustomEvent(name=custom_event_name, value=custom_event_value)
|
|
352
|
+
events_to_emit.append(custom_event)
|
|
353
|
+
|
|
354
|
+
return events_to_emit, message_started, message_id
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _create_completion_events(
|
|
358
|
+
chunk: Union[RunOutputEvent, TeamRunOutputEvent],
|
|
359
|
+
event_buffer: EventBuffer,
|
|
360
|
+
message_started: bool,
|
|
361
|
+
message_id: str,
|
|
362
|
+
thread_id: str,
|
|
363
|
+
run_id: str,
|
|
364
|
+
) -> List[BaseEvent]:
|
|
365
|
+
"""Create events for run completion."""
|
|
366
|
+
events_to_emit: List[BaseEvent] = []
|
|
367
|
+
|
|
368
|
+
# End remaining active tool calls if needed
|
|
369
|
+
for tool_call_id in list(event_buffer.active_tool_call_ids):
|
|
370
|
+
if tool_call_id not in event_buffer.ended_tool_call_ids:
|
|
371
|
+
end_event = ToolCallEndEvent(
|
|
372
|
+
type=EventType.TOOL_CALL_END,
|
|
373
|
+
tool_call_id=tool_call_id,
|
|
374
|
+
)
|
|
375
|
+
events_to_emit.append(end_event)
|
|
376
|
+
|
|
377
|
+
# End the message and run, denoting the end of the session
|
|
378
|
+
if message_started:
|
|
379
|
+
end_message_event = TextMessageEndEvent(type=EventType.TEXT_MESSAGE_END, message_id=message_id)
|
|
380
|
+
events_to_emit.append(end_message_event)
|
|
381
|
+
|
|
382
|
+
# Emit external execution tools
|
|
383
|
+
if isinstance(chunk, RunPausedEvent):
|
|
384
|
+
external_tools = chunk.tools_awaiting_external_execution
|
|
385
|
+
if external_tools:
|
|
386
|
+
# First, emit an assistant message for external tool calls
|
|
387
|
+
assistant_message_id = str(uuid.uuid4())
|
|
388
|
+
assistant_start_event = TextMessageStartEvent(
|
|
389
|
+
type=EventType.TEXT_MESSAGE_START,
|
|
390
|
+
message_id=assistant_message_id,
|
|
391
|
+
role="assistant",
|
|
392
|
+
)
|
|
393
|
+
events_to_emit.append(assistant_start_event)
|
|
394
|
+
|
|
395
|
+
# Add any text content if present for the assistant message
|
|
396
|
+
if chunk.content:
|
|
397
|
+
content_event = TextMessageContentEvent(
|
|
398
|
+
type=EventType.TEXT_MESSAGE_CONTENT,
|
|
399
|
+
message_id=assistant_message_id,
|
|
400
|
+
delta=str(chunk.content),
|
|
401
|
+
)
|
|
402
|
+
events_to_emit.append(content_event)
|
|
403
|
+
|
|
404
|
+
# End the assistant message
|
|
405
|
+
assistant_end_event = TextMessageEndEvent(
|
|
406
|
+
type=EventType.TEXT_MESSAGE_END,
|
|
407
|
+
message_id=assistant_message_id,
|
|
408
|
+
)
|
|
409
|
+
events_to_emit.append(assistant_end_event)
|
|
410
|
+
|
|
411
|
+
# Emit tool call events for external execution
|
|
412
|
+
for tool in external_tools:
|
|
413
|
+
if tool.tool_call_id is None or tool.tool_name is None:
|
|
414
|
+
continue
|
|
415
|
+
|
|
416
|
+
start_event = ToolCallStartEvent(
|
|
417
|
+
type=EventType.TOOL_CALL_START,
|
|
418
|
+
tool_call_id=tool.tool_call_id,
|
|
419
|
+
tool_call_name=tool.tool_name,
|
|
420
|
+
parent_message_id=assistant_message_id, # Use the assistant message as parent
|
|
421
|
+
)
|
|
422
|
+
events_to_emit.append(start_event)
|
|
423
|
+
|
|
424
|
+
args_event = ToolCallArgsEvent(
|
|
425
|
+
type=EventType.TOOL_CALL_ARGS,
|
|
426
|
+
tool_call_id=tool.tool_call_id,
|
|
427
|
+
delta=json.dumps(tool.tool_args),
|
|
428
|
+
)
|
|
429
|
+
events_to_emit.append(args_event)
|
|
430
|
+
|
|
431
|
+
end_event = ToolCallEndEvent(
|
|
432
|
+
type=EventType.TOOL_CALL_END,
|
|
433
|
+
tool_call_id=tool.tool_call_id,
|
|
434
|
+
)
|
|
435
|
+
events_to_emit.append(end_event)
|
|
436
|
+
|
|
437
|
+
run_finished_event = RunFinishedEvent(type=EventType.RUN_FINISHED, thread_id=thread_id, run_id=run_id)
|
|
438
|
+
events_to_emit.append(run_finished_event)
|
|
439
|
+
|
|
440
|
+
return events_to_emit
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _emit_event_logic(event: BaseEvent, event_buffer: EventBuffer) -> List[BaseEvent]:
|
|
444
|
+
"""Process an event and return events to actually emit."""
|
|
445
|
+
events_to_emit: List[BaseEvent] = [event]
|
|
446
|
+
|
|
447
|
+
# Update the event buffer state for tracking purposes
|
|
448
|
+
if event.type == EventType.TOOL_CALL_START:
|
|
449
|
+
tool_call_id = getattr(event, "tool_call_id", None)
|
|
450
|
+
if tool_call_id:
|
|
451
|
+
event_buffer.start_tool_call(tool_call_id)
|
|
452
|
+
elif event.type == EventType.TOOL_CALL_END:
|
|
453
|
+
tool_call_id = getattr(event, "tool_call_id", None)
|
|
454
|
+
if tool_call_id:
|
|
455
|
+
event_buffer.end_tool_call(tool_call_id)
|
|
456
|
+
|
|
457
|
+
return events_to_emit
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def stream_agno_response_as_agui_events(
|
|
461
|
+
response_stream: Iterator[Union[RunOutputEvent, TeamRunOutputEvent]], thread_id: str, run_id: str
|
|
462
|
+
) -> Iterator[BaseEvent]:
|
|
463
|
+
"""Map the Agno response stream to AG-UI format, handling event ordering constraints."""
|
|
464
|
+
message_id = "" # Will be set by EventBuffer when text message starts
|
|
465
|
+
message_started = False
|
|
466
|
+
event_buffer = EventBuffer()
|
|
467
|
+
stream_completed = False
|
|
468
|
+
|
|
469
|
+
completion_chunk = None
|
|
470
|
+
|
|
471
|
+
for chunk in response_stream:
|
|
472
|
+
# Check if this is a completion event
|
|
473
|
+
if (
|
|
474
|
+
chunk.event == RunEvent.run_completed
|
|
475
|
+
or chunk.event == TeamRunEvent.run_completed
|
|
476
|
+
or chunk.event == RunEvent.run_paused
|
|
477
|
+
):
|
|
478
|
+
# Store completion chunk but don't process it yet
|
|
479
|
+
completion_chunk = chunk
|
|
480
|
+
stream_completed = True
|
|
481
|
+
else:
|
|
482
|
+
# Process regular chunk immediately
|
|
483
|
+
events_from_chunk, message_started, message_id = _create_events_from_chunk(
|
|
484
|
+
chunk, message_id, message_started, event_buffer
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
for event in events_from_chunk:
|
|
488
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
489
|
+
for emit_event in events_to_emit:
|
|
490
|
+
yield emit_event
|
|
491
|
+
|
|
492
|
+
# Process ONLY completion cleanup events, not content from completion chunk
|
|
493
|
+
if completion_chunk:
|
|
494
|
+
completion_events = _create_completion_events(
|
|
495
|
+
completion_chunk, event_buffer, message_started, message_id, thread_id, run_id
|
|
496
|
+
)
|
|
497
|
+
for event in completion_events:
|
|
498
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
499
|
+
for emit_event in events_to_emit:
|
|
500
|
+
yield emit_event
|
|
501
|
+
|
|
502
|
+
# Ensure completion events are always emitted even when stream ends naturally
|
|
503
|
+
if not stream_completed:
|
|
504
|
+
# Create a synthetic completion event to ensure proper cleanup
|
|
505
|
+
from agno.run.agent import RunCompletedEvent
|
|
506
|
+
|
|
507
|
+
synthetic_completion = RunCompletedEvent()
|
|
508
|
+
completion_events = _create_completion_events(
|
|
509
|
+
synthetic_completion, event_buffer, message_started, message_id, thread_id, run_id
|
|
510
|
+
)
|
|
511
|
+
for event in completion_events:
|
|
512
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
513
|
+
for emit_event in events_to_emit:
|
|
514
|
+
yield emit_event
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# Async version - thin wrapper
|
|
518
|
+
async def async_stream_agno_response_as_agui_events(
|
|
519
|
+
response_stream: AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]],
|
|
520
|
+
thread_id: str,
|
|
521
|
+
run_id: str,
|
|
522
|
+
) -> AsyncIterator[BaseEvent]:
|
|
523
|
+
"""Map the Agno response stream to AG-UI format, handling event ordering constraints."""
|
|
524
|
+
message_id = "" # Will be set by EventBuffer when text message starts
|
|
525
|
+
message_started = False
|
|
526
|
+
event_buffer = EventBuffer()
|
|
527
|
+
stream_completed = False
|
|
528
|
+
|
|
529
|
+
completion_chunk = None
|
|
530
|
+
|
|
531
|
+
async for chunk in response_stream:
|
|
532
|
+
# Check if this is a completion event
|
|
533
|
+
if (
|
|
534
|
+
chunk.event == RunEvent.run_completed
|
|
535
|
+
or chunk.event == TeamRunEvent.run_completed
|
|
536
|
+
or chunk.event == RunEvent.run_paused
|
|
537
|
+
):
|
|
538
|
+
# Store completion chunk but don't process it yet
|
|
539
|
+
completion_chunk = chunk
|
|
540
|
+
stream_completed = True
|
|
541
|
+
else:
|
|
542
|
+
# Process regular chunk immediately
|
|
543
|
+
events_from_chunk, message_started, message_id = _create_events_from_chunk(
|
|
544
|
+
chunk, message_id, message_started, event_buffer
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
for event in events_from_chunk:
|
|
548
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
549
|
+
for emit_event in events_to_emit:
|
|
550
|
+
yield emit_event
|
|
551
|
+
|
|
552
|
+
# Process ONLY completion cleanup events, not content from completion chunk
|
|
553
|
+
if completion_chunk:
|
|
554
|
+
completion_events = _create_completion_events(
|
|
555
|
+
completion_chunk, event_buffer, message_started, message_id, thread_id, run_id
|
|
556
|
+
)
|
|
557
|
+
for event in completion_events:
|
|
558
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
559
|
+
for emit_event in events_to_emit:
|
|
560
|
+
yield emit_event
|
|
561
|
+
|
|
562
|
+
# Ensure completion events are always emitted even when stream ends naturally
|
|
563
|
+
if not stream_completed:
|
|
564
|
+
# Create a synthetic completion event to ensure proper cleanup
|
|
565
|
+
from agno.run.agent import RunCompletedEvent
|
|
566
|
+
|
|
567
|
+
synthetic_completion = RunCompletedEvent()
|
|
568
|
+
completion_events = _create_completion_events(
|
|
569
|
+
synthetic_completion, event_buffer, message_started, message_id, thread_id, run_id
|
|
570
|
+
)
|
|
571
|
+
for event in completion_events:
|
|
572
|
+
events_to_emit = _emit_event_logic(event_buffer=event_buffer, event=event)
|
|
573
|
+
for emit_event in events_to_emit:
|
|
574
|
+
yield emit_event
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter
|
|
5
|
+
|
|
6
|
+
from agno.agent import Agent
|
|
7
|
+
from agno.team import Team
|
|
8
|
+
from agno.workflow.workflow import Workflow
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseInterface(ABC):
|
|
12
|
+
type: str
|
|
13
|
+
version: str = "1.0"
|
|
14
|
+
agent: Optional[Agent] = None
|
|
15
|
+
team: Optional[Team] = None
|
|
16
|
+
workflow: Optional[Workflow] = None
|
|
17
|
+
|
|
18
|
+
prefix: str
|
|
19
|
+
tags: List[str]
|
|
20
|
+
|
|
21
|
+
router: APIRouter
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_router(self, use_async: bool = True, **kwargs) -> APIRouter:
|
|
25
|
+
pass
|