agno 1.8.1__py3-none-any.whl → 2.0.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/__init__.py +8 -0
- agno/agent/__init__.py +19 -27
- agno/agent/agent.py +3143 -4170
- agno/api/agent.py +11 -67
- agno/api/api.py +5 -46
- agno/api/evals.py +8 -19
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -41
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +5 -21
- agno/api/schemas/evals.py +7 -16
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +5 -21
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +11 -7
- agno/api/settings.py +53 -0
- agno/api/team.py +11 -66
- 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/db/__init__.py +24 -0
- agno/db/base.py +245 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +1743 -0
- agno/db/dynamo/schemas.py +278 -0
- agno/db/dynamo/utils.py +684 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1432 -0
- agno/db/firestore/schemas.py +130 -0
- agno/db/firestore/utils.py +278 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1001 -0
- agno/db/gcs_json/utils.py +194 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +882 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1045 -0
- agno/db/json/utils.py +196 -0
- agno/db/migrations/v1_to_v2.py +162 -0
- agno/db/mongo/__init__.py +3 -0
- agno/db/mongo/mongo.py +1416 -0
- agno/db/mongo/schemas.py +77 -0
- agno/db/mongo/utils.py +204 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +1719 -0
- agno/db/mysql/schemas.py +124 -0
- agno/db/mysql/utils.py +297 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1710 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +280 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1367 -0
- agno/db/redis/schemas.py +109 -0
- agno/db/redis/utils.py +288 -0
- agno/db/schemas/__init__.py +3 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +116 -0
- agno/db/singlestore/singlestore.py +1712 -0
- agno/db/singlestore/utils.py +326 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1676 -0
- agno/db/sqlite/utils.py +268 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +154 -48
- agno/eval/performance.py +88 -23
- agno/eval/reliability.py +73 -20
- agno/eval/utils.py +23 -13
- agno/integrations/discord/__init__.py +3 -0
- agno/{app → integrations}/discord/client.py +15 -11
- agno/knowledge/__init__.py +2 -2
- agno/{document → knowledge}/chunking/agentic.py +2 -2
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +3 -3
- agno/{document → knowledge}/chunking/markdown.py +2 -2
- agno/{document → knowledge}/chunking/recursive.py +2 -2
- agno/{document → knowledge}/chunking/row.py +2 -2
- agno/knowledge/chunking/semantic.py +59 -0
- agno/knowledge/chunking/strategy.py +121 -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/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
- agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
- agno/{embedder → knowledge/embedder}/base.py +6 -0
- agno/{embedder → knowledge/embedder}/cohere.py +72 -1
- agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/{embedder → knowledge/embedder}/google.py +74 -1
- agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
- agno/{embedder → knowledge/embedder}/jina.py +48 -2
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +139 -0
- agno/{embedder → knowledge/embedder}/nebius.py +1 -1
- agno/{embedder → knowledge/embedder}/ollama.py +54 -3
- agno/knowledge/embedder/openai.py +223 -0
- agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
- agno/knowledge/knowledge.py +1551 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
- agno/knowledge/reader/base.py +88 -0
- agno/{document → knowledge}/reader/csv_reader.py +47 -65
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +58 -9
- agno/{document → knowledge}/reader/pdf_reader.py +71 -126
- agno/knowledge/reader/reader_factory.py +268 -0
- agno/knowledge/reader/s3_reader.py +101 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +128 -0
- agno/knowledge/reader/web_search_reader.py +366 -0
- agno/{document → knowledge}/reader/website_reader.py +37 -10
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/{reranker → knowledge/reranker}/infinity.py +2 -2
- agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
- agno/knowledge/types.py +30 -0
- agno/knowledge/utils.py +169 -0
- agno/media.py +269 -268
- agno/memory/__init__.py +2 -10
- agno/memory/manager.py +1003 -148
- agno/models/aimlapi/__init__.py +2 -2
- agno/models/aimlapi/aimlapi.py +6 -6
- agno/models/anthropic/claude.py +131 -131
- agno/models/aws/bedrock.py +110 -182
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +346 -290
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +105 -46
- agno/models/groq/groq.py +97 -35
- agno/models/huggingface/huggingface.py +92 -27
- agno/models/ibm/watsonx.py +72 -13
- agno/models/litellm/chat.py +85 -13
- agno/models/message.py +46 -151
- agno/models/meta/llama.py +85 -49
- agno/models/metrics.py +120 -0
- agno/models/mistral/mistral.py +90 -21
- agno/models/ollama/__init__.py +0 -2
- agno/models/ollama/chat.py +85 -47
- agno/models/openai/chat.py +154 -37
- agno/models/openai/responses.py +178 -105
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +15 -9
- agno/models/utils.py +20 -0
- agno/models/vercel/__init__.py +2 -2
- agno/models/vercel/v0.py +1 -1
- agno/models/vllm/__init__.py +2 -2
- agno/models/vllm/vllm.py +3 -3
- agno/models/xai/xai.py +10 -10
- agno/os/__init__.py +3 -0
- agno/os/app.py +497 -0
- agno/os/auth.py +47 -0
- agno/os/config.py +103 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +31 -0
- agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
- agno/{app → os/interfaces}/agui/utils.py +77 -33
- agno/os/interfaces/base.py +21 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
- agno/os/interfaces/slack/slack.py +32 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
- agno/os/interfaces/whatsapp/whatsapp.py +29 -0
- agno/os/mcp.py +235 -0
- agno/os/router.py +1400 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +393 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +161 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +850 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +410 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +178 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +536 -0
- agno/os/schema.py +945 -0
- agno/{app/playground → os}/settings.py +7 -15
- agno/os/utils.py +270 -0
- agno/reasoning/azure_ai_foundry.py +4 -4
- agno/reasoning/deepseek.py +4 -4
- agno/reasoning/default.py +6 -11
- agno/reasoning/groq.py +4 -4
- agno/reasoning/helpers.py +4 -6
- agno/reasoning/ollama.py +4 -4
- agno/reasoning/openai.py +4 -4
- agno/run/agent.py +633 -0
- agno/run/base.py +53 -77
- agno/run/cancel.py +81 -0
- agno/run/team.py +243 -96
- agno/run/workflow.py +550 -12
- agno/session/__init__.py +10 -0
- agno/session/agent.py +244 -0
- agno/session/summary.py +225 -0
- agno/session/team.py +262 -0
- agno/{storage/session/v2 → session}/workflow.py +47 -24
- agno/team/__init__.py +15 -16
- agno/team/team.py +3260 -4824
- agno/tools/agentql.py +14 -5
- agno/tools/airflow.py +9 -4
- agno/tools/api.py +7 -3
- agno/tools/apify.py +2 -46
- agno/tools/arxiv.py +8 -3
- agno/tools/aws_lambda.py +7 -5
- agno/tools/aws_ses.py +7 -1
- agno/tools/baidusearch.py +4 -1
- agno/tools/bitbucket.py +4 -4
- agno/tools/brandfetch.py +14 -11
- agno/tools/bravesearch.py +4 -1
- agno/tools/brightdata.py +43 -23
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +20 -17
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +8 -8
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +22 -12
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +17 -8
- agno/tools/discord.py +11 -8
- agno/tools/docker.py +30 -42
- agno/tools/duckdb.py +34 -53
- agno/tools/duckduckgo.py +8 -7
- agno/tools/e2b.py +62 -62
- agno/tools/eleven_labs.py +36 -29
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +30 -30
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +127 -18
- agno/tools/giphy.py +23 -11
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +45 -61
- agno/tools/google_bigquery.py +7 -6
- agno/tools/google_maps.py +11 -26
- agno/tools/googlesearch.py +7 -2
- agno/tools/googlesheets.py +21 -17
- agno/tools/hackernews.py +9 -5
- agno/tools/jina.py +5 -4
- agno/tools/jira.py +18 -9
- agno/tools/knowledge.py +31 -32
- agno/tools/linear.py +19 -34
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +32 -20
- agno/tools/mcp.py +1 -2
- agno/tools/mem0.py +18 -12
- agno/tools/memori.py +14 -10
- agno/tools/mlx_transcribe.py +3 -2
- agno/tools/models/azure_openai.py +33 -15
- agno/tools/models/gemini.py +59 -32
- agno/tools/models/groq.py +30 -23
- agno/tools/models/nebius.py +28 -12
- agno/tools/models_labs.py +40 -16
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +10 -8
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +58 -32
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +63 -47
- agno/tools/openweather.py +14 -12
- agno/tools/pandas.py +11 -3
- agno/tools/postgres.py +4 -12
- agno/tools/pubmed.py +4 -1
- agno/tools/python.py +9 -22
- agno/tools/reasoning.py +35 -27
- agno/tools/reddit.py +11 -26
- agno/tools/replicate.py +55 -42
- agno/tools/resend.py +4 -1
- agno/tools/scrapegraph.py +15 -14
- agno/tools/searxng.py +10 -23
- agno/tools/serpapi.py +6 -3
- agno/tools/serper.py +13 -4
- agno/tools/shell.py +9 -2
- agno/tools/slack.py +12 -11
- agno/tools/sleep.py +3 -2
- agno/tools/spider.py +24 -4
- agno/tools/sql.py +7 -6
- agno/tools/tavily.py +6 -4
- agno/tools/telegram.py +12 -4
- agno/tools/todoist.py +11 -31
- agno/tools/toolkit.py +1 -1
- agno/tools/trafilatura.py +22 -6
- agno/tools/trello.py +9 -22
- agno/tools/twilio.py +10 -3
- agno/tools/user_control_flow.py +6 -1
- agno/tools/valyu.py +34 -5
- agno/tools/visualization.py +19 -28
- agno/tools/webbrowser.py +4 -3
- agno/tools/webex.py +11 -7
- agno/tools/website.py +15 -46
- agno/tools/webtools.py +12 -4
- agno/tools/whatsapp.py +5 -9
- agno/tools/wikipedia.py +20 -13
- agno/tools/x.py +14 -13
- agno/tools/yfinance.py +13 -40
- agno/tools/youtube.py +26 -20
- agno/tools/zendesk.py +7 -2
- agno/tools/zep.py +10 -7
- agno/tools/zoom.py +10 -9
- agno/utils/common.py +1 -19
- agno/utils/events.py +100 -123
- agno/utils/gemini.py +32 -2
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +54 -4
- agno/utils/mcp.py +68 -10
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/aws_claude.py +1 -1
- agno/utils/models/claude.py +47 -4
- agno/utils/models/cohere.py +1 -1
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/models/watsonx.py +1 -1
- agno/utils/openai.py +1 -1
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1669 -0
- agno/utils/print_response/workflow.py +1451 -0
- agno/utils/prompts.py +14 -14
- agno/utils/reasoning.py +87 -0
- agno/utils/response.py +42 -42
- agno/utils/streamlit.py +481 -0
- agno/utils/string.py +8 -22
- agno/utils/team.py +50 -0
- agno/utils/timer.py +2 -2
- agno/vectordb/base.py +33 -21
- agno/vectordb/cassandra/cassandra.py +287 -23
- agno/vectordb/chroma/chromadb.py +482 -59
- agno/vectordb/clickhouse/clickhousedb.py +270 -63
- agno/vectordb/couchbase/couchbase.py +309 -29
- agno/vectordb/lancedb/lance_db.py +360 -21
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +145 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +374 -0
- agno/vectordb/llamaindex/llamaindexdb.py +127 -0
- agno/vectordb/milvus/milvus.py +242 -32
- agno/vectordb/mongodb/mongodb.py +200 -24
- agno/vectordb/pgvector/pgvector.py +319 -37
- agno/vectordb/pineconedb/pineconedb.py +221 -27
- agno/vectordb/qdrant/qdrant.py +334 -14
- agno/vectordb/singlestore/singlestore.py +286 -29
- agno/vectordb/surrealdb/surrealdb.py +187 -7
- agno/vectordb/upstashdb/upstashdb.py +342 -26
- agno/vectordb/weaviate/weaviate.py +227 -165
- agno/workflow/__init__.py +17 -13
- agno/workflow/{v2/condition.py → condition.py} +135 -32
- agno/workflow/{v2/loop.py → loop.py} +115 -28
- agno/workflow/{v2/parallel.py → parallel.py} +138 -108
- agno/workflow/{v2/router.py → router.py} +133 -32
- agno/workflow/{v2/step.py → step.py} +207 -49
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2410 -696
- agno-2.0.0.dist-info/METADATA +494 -0
- agno-2.0.0.dist-info/RECORD +515 -0
- agno-2.0.0.dist-info/licenses/LICENSE +201 -0
- agno/agent/metrics.py +0 -107
- agno/api/app.py +0 -35
- agno/api/playground.py +0 -92
- agno/api/schemas/app.py +0 -12
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -35
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workflows.py +0 -33
- agno/api/workspace.py +0 -175
- agno/app/agui/__init__.py +0 -3
- agno/app/agui/app.py +0 -17
- agno/app/agui/sync_router.py +0 -120
- agno/app/base.py +0 -186
- agno/app/discord/__init__.py +0 -3
- agno/app/fastapi/__init__.py +0 -3
- agno/app/fastapi/app.py +0 -107
- agno/app/fastapi/async_router.py +0 -457
- agno/app/fastapi/sync_router.py +0 -448
- agno/app/playground/app.py +0 -228
- agno/app/playground/async_router.py +0 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- agno/app/playground/utils.py +0 -46
- agno/app/settings.py +0 -15
- agno/app/slack/__init__.py +0 -3
- agno/app/slack/app.py +0 -19
- agno/app/slack/sync_router.py +0 -92
- agno/app/utils.py +0 -54
- agno/app/whatsapp/__init__.py +0 -3
- agno/app/whatsapp/app.py +0 -15
- agno/app/whatsapp/sync_router.py +0 -197
- agno/cli/auth_server.py +0 -249
- agno/cli/config.py +0 -274
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -357
- agno/cli/settings.py +0 -96
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -5
- agno/document/chunking/semantic.py +0 -45
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -5
- agno/document/reader/base.py +0 -47
- agno/document/reader/docx_reader.py +0 -60
- agno/document/reader/gcs/pdf_reader.py +0 -44
- agno/document/reader/s3/pdf_reader.py +0 -59
- agno/document/reader/s3/text_reader.py +0 -63
- agno/document/reader/url_reader.py +0 -59
- agno/document/reader/youtube_reader.py +0 -58
- agno/embedder/__init__.py +0 -5
- agno/embedder/langdb.py +0 -80
- agno/embedder/mistral.py +0 -82
- agno/embedder/openai.py +0 -78
- agno/file/__init__.py +0 -5
- 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 -702
- agno/knowledge/arxiv.py +0 -33
- agno/knowledge/combined.py +0 -36
- agno/knowledge/csv.py +0 -144
- agno/knowledge/csv_url.py +0 -124
- agno/knowledge/document.py +0 -223
- agno/knowledge/docx.py +0 -137
- agno/knowledge/firecrawl.py +0 -34
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +0 -39
- agno/knowledge/gcs/pdf.py +0 -125
- agno/knowledge/json.py +0 -137
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/light_rag.py +0 -273
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/markdown.py +0 -154
- agno/knowledge/pdf.py +0 -164
- agno/knowledge/pdf_bytes.py +0 -42
- agno/knowledge/pdf_url.py +0 -148
- agno/knowledge/s3/__init__.py +0 -0
- agno/knowledge/s3/base.py +0 -64
- agno/knowledge/s3/pdf.py +0 -33
- agno/knowledge/s3/text.py +0 -34
- agno/knowledge/text.py +0 -141
- agno/knowledge/url.py +0 -46
- agno/knowledge/website.py +0 -179
- agno/knowledge/wikipedia.py +0 -32
- agno/knowledge/youtube.py +0 -35
- agno/memory/agent.py +0 -423
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -5
- 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 -22
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -201
- agno/memory/summary.py +0 -19
- agno/memory/team.py +0 -415
- agno/memory/v2/__init__.py +0 -2
- agno/memory/v2/db/__init__.py +0 -1
- agno/memory/v2/db/base.py +0 -42
- agno/memory/v2/db/firestore.py +0 -339
- agno/memory/v2/db/mongodb.py +0 -196
- agno/memory/v2/db/postgres.py +0 -214
- agno/memory/v2/db/redis.py +0 -187
- agno/memory/v2/db/schema.py +0 -54
- agno/memory/v2/db/sqlite.py +0 -209
- agno/memory/v2/manager.py +0 -437
- agno/memory/v2/memory.py +0 -1097
- agno/memory/v2/schema.py +0 -55
- agno/memory/v2/summarizer.py +0 -215
- agno/memory/workflow.py +0 -38
- agno/models/ollama/tools.py +0 -430
- agno/models/qwen/__init__.py +0 -5
- agno/playground/__init__.py +0 -10
- agno/playground/deploy.py +0 -3
- agno/playground/playground.py +0 -3
- agno/playground/serve.py +0 -3
- agno/playground/settings.py +0 -3
- agno/reranker/__init__.py +0 -0
- agno/run/response.py +0 -467
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +0 -567
- agno/storage/__init__.py +0 -0
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/dynamodb.py +0 -1
- agno/storage/agent/json.py +0 -1
- agno/storage/agent/mongodb.py +0 -1
- agno/storage/agent/postgres.py +0 -1
- agno/storage/agent/singlestore.py +0 -1
- agno/storage/agent/sqlite.py +0 -1
- agno/storage/agent/yaml.py +0 -1
- agno/storage/base.py +0 -60
- agno/storage/dynamodb.py +0 -673
- agno/storage/firestore.py +0 -297
- agno/storage/gcs_json.py +0 -261
- agno/storage/in_memory.py +0 -234
- agno/storage/json.py +0 -237
- agno/storage/mongodb.py +0 -328
- agno/storage/mysql.py +0 -685
- agno/storage/postgres.py +0 -682
- agno/storage/redis.py +0 -336
- agno/storage/session/__init__.py +0 -16
- agno/storage/session/agent.py +0 -64
- agno/storage/session/team.py +0 -63
- agno/storage/session/v2/__init__.py +0 -5
- agno/storage/session/workflow.py +0 -61
- agno/storage/singlestore.py +0 -606
- agno/storage/sqlite.py +0 -646
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/mongodb.py +0 -1
- agno/storage/workflow/postgres.py +0 -1
- agno/storage/workflow/sqlite.py +0 -1
- agno/storage/yaml.py +0 -241
- agno/tools/thinking.py +0 -73
- 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/workflow/v2/__init__.py +0 -21
- agno/workflow/v2/types.py +0 -357
- agno/workflow/v2/workflow.py +0 -3312
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -52
- agno/workspace/operator.py +0 -757
- agno/workspace/settings.py +0 -158
- agno-1.8.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- agno-1.8.1.dist-info/licenses/LICENSE +0 -375
- /agno/{app → db/migrations}/__init__.py +0 -0
- /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{cli → integrations}/__init__.py +0 -0
- /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
- /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
- /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
- /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
- /agno/{app → os/interfaces}/slack/security.py +0 -0
- /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
- /agno/{file/local → utils/print_response}/__init__.py +0 -0
- /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/models/base.py
CHANGED
|
@@ -5,7 +5,6 @@ from dataclasses import dataclass, field
|
|
|
5
5
|
from types import AsyncGeneratorType, GeneratorType
|
|
6
6
|
from typing import (
|
|
7
7
|
Any,
|
|
8
|
-
AsyncGenerator,
|
|
9
8
|
AsyncIterator,
|
|
10
9
|
Dict,
|
|
11
10
|
Iterator,
|
|
@@ -22,12 +21,13 @@ from uuid import uuid4
|
|
|
22
21
|
from pydantic import BaseModel
|
|
23
22
|
|
|
24
23
|
from agno.exceptions import AgentRunException
|
|
25
|
-
from agno.media import
|
|
26
|
-
from agno.models.message import Citations, Message
|
|
24
|
+
from agno.media import Audio, Image, Video
|
|
25
|
+
from agno.models.message import Citations, Message
|
|
26
|
+
from agno.models.metrics import Metrics
|
|
27
27
|
from agno.models.response import ModelResponse, ModelResponseEvent, ToolExecution
|
|
28
|
-
from agno.run.
|
|
29
|
-
from agno.run.team import
|
|
30
|
-
from agno.run.team import
|
|
28
|
+
from agno.run.agent import CustomEvent, RunContentEvent, RunOutput, RunOutputEvent
|
|
29
|
+
from agno.run.team import RunContentEvent as TeamRunContentEvent
|
|
30
|
+
from agno.run.team import TeamRunOutputEvent
|
|
31
31
|
from agno.tools.function import Function, FunctionCall, FunctionExecutionResult, UserInputField
|
|
32
32
|
from agno.utils.log import log_debug, log_error, log_warning
|
|
33
33
|
from agno.utils.timer import Timer
|
|
@@ -38,13 +38,14 @@ from agno.utils.tools import get_function_call_for_tool_call, get_function_call_
|
|
|
38
38
|
class MessageData:
|
|
39
39
|
response_role: Optional[Literal["system", "user", "assistant", "tool"]] = None
|
|
40
40
|
response_content: Any = ""
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
response_reasoning_content: Any = ""
|
|
42
|
+
response_redacted_reasoning_content: Any = ""
|
|
43
43
|
response_citations: Optional[Citations] = None
|
|
44
44
|
response_tool_calls: List[Dict[str, Any]] = field(default_factory=list)
|
|
45
45
|
|
|
46
|
-
response_audio: Optional[
|
|
47
|
-
response_image: Optional[
|
|
46
|
+
response_audio: Optional[Audio] = None
|
|
47
|
+
response_image: Optional[Image] = None
|
|
48
|
+
response_video: Optional[Video] = None
|
|
48
49
|
|
|
49
50
|
# Data from the provider that we might need on subsequent messages
|
|
50
51
|
response_provider_data: Optional[Dict[str, Any]] = None
|
|
@@ -61,143 +62,17 @@ def _log_messages(messages: List[Message]) -> None:
|
|
|
61
62
|
m.log(metrics=False)
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
def
|
|
65
|
-
"""
|
|
66
|
-
Add usage metrics from the model provider to the assistant message.
|
|
67
|
-
|
|
68
|
-
Args:
|
|
69
|
-
assistant_message: Message to update with metrics
|
|
70
|
-
response_usage: Usage data from model provider
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
# Standard token metrics
|
|
74
|
-
if isinstance(response_usage, dict):
|
|
75
|
-
if "input_tokens" in response_usage and response_usage.get("input_tokens") is not None:
|
|
76
|
-
assistant_message.metrics.input_tokens = response_usage.get("input_tokens", 0)
|
|
77
|
-
if "output_tokens" in response_usage and response_usage.get("output_tokens") is not None:
|
|
78
|
-
assistant_message.metrics.output_tokens = response_usage.get("output_tokens", 0)
|
|
79
|
-
if "prompt_tokens" in response_usage and response_usage.get("prompt_tokens") is not None:
|
|
80
|
-
assistant_message.metrics.input_tokens = response_usage.get("prompt_tokens", 0)
|
|
81
|
-
if "completion_tokens" in response_usage and response_usage.get("completion_tokens") is not None:
|
|
82
|
-
assistant_message.metrics.output_tokens = response_usage.get("completion_tokens", 0)
|
|
83
|
-
if "cached_tokens" in response_usage and response_usage.get("cached_tokens") is not None:
|
|
84
|
-
assistant_message.metrics.cached_tokens = response_usage.get("cached_tokens", 0)
|
|
85
|
-
if "cache_write_tokens" in response_usage and response_usage.get("cache_write_tokens") is not None:
|
|
86
|
-
assistant_message.metrics.cache_write_tokens = response_usage.get("cache_write_tokens", 0)
|
|
87
|
-
if "total_tokens" in response_usage and response_usage.get("total_tokens") is not None:
|
|
88
|
-
assistant_message.metrics.total_tokens = response_usage.get("total_tokens", 0)
|
|
89
|
-
else:
|
|
90
|
-
assistant_message.metrics.total_tokens = (
|
|
91
|
-
assistant_message.metrics.input_tokens + assistant_message.metrics.output_tokens
|
|
92
|
-
)
|
|
93
|
-
else:
|
|
94
|
-
if hasattr(response_usage, "input_tokens") and response_usage.input_tokens:
|
|
95
|
-
assistant_message.metrics.input_tokens = response_usage.input_tokens
|
|
96
|
-
if hasattr(response_usage, "output_tokens") and response_usage.output_tokens:
|
|
97
|
-
assistant_message.metrics.output_tokens = response_usage.output_tokens
|
|
98
|
-
if hasattr(response_usage, "prompt_tokens") and response_usage.prompt_tokens is not None:
|
|
99
|
-
assistant_message.metrics.input_tokens = response_usage.prompt_tokens
|
|
100
|
-
assistant_message.metrics.prompt_tokens = response_usage.prompt_tokens
|
|
101
|
-
if hasattr(response_usage, "completion_tokens") and response_usage.completion_tokens is not None:
|
|
102
|
-
assistant_message.metrics.output_tokens = response_usage.completion_tokens
|
|
103
|
-
assistant_message.metrics.completion_tokens = response_usage.completion_tokens
|
|
104
|
-
if hasattr(response_usage, "total_tokens") and response_usage.total_tokens is not None:
|
|
105
|
-
assistant_message.metrics.total_tokens = response_usage.total_tokens
|
|
106
|
-
if hasattr(response_usage, "cached_tokens") and response_usage.cached_tokens is not None:
|
|
107
|
-
assistant_message.metrics.cached_tokens = response_usage.cached_tokens
|
|
108
|
-
if hasattr(response_usage, "cache_write_tokens") and response_usage.cache_write_tokens is not None:
|
|
109
|
-
assistant_message.metrics.cache_write_tokens = response_usage.cache_write_tokens
|
|
110
|
-
|
|
111
|
-
# If you didn't capture any total tokens
|
|
112
|
-
if not assistant_message.metrics.total_tokens:
|
|
113
|
-
if assistant_message.metrics.input_tokens is None:
|
|
114
|
-
assistant_message.metrics.input_tokens = 0
|
|
115
|
-
if assistant_message.metrics.output_tokens is None:
|
|
116
|
-
assistant_message.metrics.output_tokens = 0
|
|
117
|
-
|
|
118
|
-
assistant_message.metrics.total_tokens = (
|
|
119
|
-
assistant_message.metrics.input_tokens + assistant_message.metrics.output_tokens
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
# Additional metrics (e.g., from Groq, Ollama)
|
|
123
|
-
if isinstance(response_usage, dict) and "additional_metrics" in response_usage:
|
|
124
|
-
assistant_message.metrics.additional_metrics = response_usage["additional_metrics"]
|
|
125
|
-
|
|
126
|
-
# Token details (e.g., from OpenAI)
|
|
127
|
-
if hasattr(response_usage, "prompt_tokens_details"):
|
|
128
|
-
if isinstance(response_usage.prompt_tokens_details, dict):
|
|
129
|
-
assistant_message.metrics.prompt_tokens_details = response_usage.prompt_tokens_details
|
|
130
|
-
if (
|
|
131
|
-
"audio_tokens" in response_usage.prompt_tokens_details
|
|
132
|
-
and response_usage.prompt_tokens_details["audio_tokens"] is not None
|
|
133
|
-
):
|
|
134
|
-
assistant_message.metrics.input_audio_tokens = response_usage.prompt_tokens_details["audio_tokens"]
|
|
135
|
-
if (
|
|
136
|
-
"cached_tokens" in response_usage.prompt_tokens_details
|
|
137
|
-
and response_usage.prompt_tokens_details["cached_tokens"] is not None
|
|
138
|
-
):
|
|
139
|
-
assistant_message.metrics.cached_tokens = response_usage.prompt_tokens_details["cached_tokens"]
|
|
140
|
-
elif hasattr(response_usage.prompt_tokens_details, "model_dump"):
|
|
141
|
-
assistant_message.metrics.prompt_tokens_details = response_usage.prompt_tokens_details.model_dump(
|
|
142
|
-
exclude_none=True
|
|
143
|
-
)
|
|
144
|
-
if (
|
|
145
|
-
hasattr(response_usage.prompt_tokens_details, "audio_tokens")
|
|
146
|
-
and response_usage.prompt_tokens_details.audio_tokens is not None
|
|
147
|
-
):
|
|
148
|
-
assistant_message.metrics.input_audio_tokens = response_usage.prompt_tokens_details.audio_tokens
|
|
149
|
-
if (
|
|
150
|
-
hasattr(response_usage.prompt_tokens_details, "cached_tokens")
|
|
151
|
-
and response_usage.prompt_tokens_details.cached_tokens is not None
|
|
152
|
-
):
|
|
153
|
-
assistant_message.metrics.cached_tokens = response_usage.prompt_tokens_details.cached_tokens
|
|
154
|
-
|
|
155
|
-
if hasattr(response_usage, "completion_tokens_details"):
|
|
156
|
-
if isinstance(response_usage.completion_tokens_details, dict):
|
|
157
|
-
assistant_message.metrics.completion_tokens_details = response_usage.completion_tokens_details
|
|
158
|
-
if (
|
|
159
|
-
"audio_tokens" in response_usage.completion_tokens_details
|
|
160
|
-
and response_usage.completion_tokens_details["audio_tokens"] is not None
|
|
161
|
-
):
|
|
162
|
-
assistant_message.metrics.output_audio_tokens = response_usage.completion_tokens_details["audio_tokens"]
|
|
163
|
-
if (
|
|
164
|
-
"reasoning_tokens" in response_usage.completion_tokens_details
|
|
165
|
-
and response_usage.completion_tokens_details["reasoning_tokens"] is not None
|
|
166
|
-
):
|
|
167
|
-
assistant_message.metrics.reasoning_tokens = response_usage.completion_tokens_details[
|
|
168
|
-
"reasoning_tokens"
|
|
169
|
-
]
|
|
170
|
-
elif hasattr(response_usage.completion_tokens_details, "model_dump"):
|
|
171
|
-
assistant_message.metrics.completion_tokens_details = response_usage.completion_tokens_details.model_dump(
|
|
172
|
-
exclude_none=True
|
|
173
|
-
)
|
|
174
|
-
if (
|
|
175
|
-
hasattr(response_usage.completion_tokens_details, "audio_tokens")
|
|
176
|
-
and response_usage.completion_tokens_details.audio_tokens is not None
|
|
177
|
-
):
|
|
178
|
-
assistant_message.metrics.output_audio_tokens = response_usage.completion_tokens_details.audio_tokens
|
|
179
|
-
if (
|
|
180
|
-
hasattr(response_usage.completion_tokens_details, "reasoning_tokens")
|
|
181
|
-
and response_usage.completion_tokens_details.reasoning_tokens is not None
|
|
182
|
-
):
|
|
183
|
-
assistant_message.metrics.reasoning_tokens = response_usage.completion_tokens_details.reasoning_tokens
|
|
184
|
-
|
|
185
|
-
assistant_message.metrics.audio_tokens = (
|
|
186
|
-
assistant_message.metrics.input_audio_tokens + assistant_message.metrics.output_audio_tokens
|
|
187
|
-
)
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
def _handle_agent_exception(a_exc: AgentRunException, additional_messages: Optional[List[Message]] = None) -> None:
|
|
65
|
+
def _handle_agent_exception(a_exc: AgentRunException, additional_input: Optional[List[Message]] = None) -> None:
|
|
191
66
|
"""Handle AgentRunException and collect additional messages."""
|
|
192
|
-
if
|
|
193
|
-
|
|
67
|
+
if additional_input is None:
|
|
68
|
+
additional_input = []
|
|
194
69
|
if a_exc.user_message is not None:
|
|
195
70
|
msg = (
|
|
196
71
|
Message(role="user", content=a_exc.user_message)
|
|
197
72
|
if isinstance(a_exc.user_message, str)
|
|
198
73
|
else a_exc.user_message
|
|
199
74
|
)
|
|
200
|
-
|
|
75
|
+
additional_input.append(msg)
|
|
201
76
|
|
|
202
77
|
if a_exc.agent_message is not None:
|
|
203
78
|
msg = (
|
|
@@ -205,20 +80,20 @@ def _handle_agent_exception(a_exc: AgentRunException, additional_messages: Optio
|
|
|
205
80
|
if isinstance(a_exc.agent_message, str)
|
|
206
81
|
else a_exc.agent_message
|
|
207
82
|
)
|
|
208
|
-
|
|
83
|
+
additional_input.append(msg)
|
|
209
84
|
|
|
210
85
|
if a_exc.messages:
|
|
211
86
|
for m in a_exc.messages:
|
|
212
87
|
if isinstance(m, Message):
|
|
213
|
-
|
|
88
|
+
additional_input.append(m)
|
|
214
89
|
elif isinstance(m, dict):
|
|
215
90
|
try:
|
|
216
|
-
|
|
91
|
+
additional_input.append(Message(**m))
|
|
217
92
|
except Exception as e:
|
|
218
93
|
log_warning(f"Failed to convert dict to Message: {e}")
|
|
219
94
|
|
|
220
95
|
if a_exc.stop_execution:
|
|
221
|
-
for m in
|
|
96
|
+
for m in additional_input:
|
|
222
97
|
m.stop_after_tool_call = True
|
|
223
98
|
|
|
224
99
|
|
|
@@ -270,23 +145,23 @@ class Model(ABC):
|
|
|
270
145
|
return self.provider or self.name or self.__class__.__name__
|
|
271
146
|
|
|
272
147
|
@abstractmethod
|
|
273
|
-
def invoke(self, *args, **kwargs) ->
|
|
148
|
+
def invoke(self, *args, **kwargs) -> ModelResponse:
|
|
274
149
|
pass
|
|
275
150
|
|
|
276
151
|
@abstractmethod
|
|
277
|
-
async def ainvoke(self, *args, **kwargs) ->
|
|
152
|
+
async def ainvoke(self, *args, **kwargs) -> ModelResponse:
|
|
278
153
|
pass
|
|
279
154
|
|
|
280
155
|
@abstractmethod
|
|
281
|
-
def invoke_stream(self, *args, **kwargs) -> Iterator[
|
|
156
|
+
def invoke_stream(self, *args, **kwargs) -> Iterator[ModelResponse]:
|
|
282
157
|
pass
|
|
283
158
|
|
|
284
159
|
@abstractmethod
|
|
285
|
-
|
|
160
|
+
def ainvoke_stream(self, *args, **kwargs) -> AsyncIterator[ModelResponse]:
|
|
286
161
|
pass
|
|
287
162
|
|
|
288
163
|
@abstractmethod
|
|
289
|
-
def
|
|
164
|
+
def _parse_provider_response(self, response: Any, **kwargs) -> ModelResponse:
|
|
290
165
|
"""
|
|
291
166
|
Parse the raw response from the model provider into a ModelResponse.
|
|
292
167
|
|
|
@@ -299,7 +174,7 @@ class Model(ABC):
|
|
|
299
174
|
pass
|
|
300
175
|
|
|
301
176
|
@abstractmethod
|
|
302
|
-
def
|
|
177
|
+
def _parse_provider_response_delta(self, response: Any) -> ModelResponse:
|
|
303
178
|
"""
|
|
304
179
|
Parse the streaming response from the model provider into ModelResponse objects.
|
|
305
180
|
|
|
@@ -319,6 +194,7 @@ class Model(ABC):
|
|
|
319
194
|
functions: Optional[Dict[str, Function]] = None,
|
|
320
195
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
321
196
|
tool_call_limit: Optional[int] = None,
|
|
197
|
+
run_response: Optional[RunOutput] = None,
|
|
322
198
|
) -> ModelResponse:
|
|
323
199
|
"""
|
|
324
200
|
Generate a response from the model.
|
|
@@ -342,6 +218,7 @@ class Model(ABC):
|
|
|
342
218
|
response_format=response_format,
|
|
343
219
|
tools=tools,
|
|
344
220
|
tool_choice=tool_choice or self._tool_choice,
|
|
221
|
+
run_response=run_response,
|
|
345
222
|
)
|
|
346
223
|
|
|
347
224
|
# Add assistant message to messages
|
|
@@ -369,6 +246,26 @@ class Model(ABC):
|
|
|
369
246
|
function_call_limit=tool_call_limit,
|
|
370
247
|
):
|
|
371
248
|
if isinstance(function_call_response, ModelResponse):
|
|
249
|
+
# The session state is updated by the function call
|
|
250
|
+
if function_call_response.updated_session_state is not None:
|
|
251
|
+
model_response.updated_session_state = function_call_response.updated_session_state
|
|
252
|
+
|
|
253
|
+
# Media artifacts are generated by the function call
|
|
254
|
+
if function_call_response.images is not None:
|
|
255
|
+
if model_response.images is None:
|
|
256
|
+
model_response.images = []
|
|
257
|
+
model_response.images.extend(function_call_response.images)
|
|
258
|
+
|
|
259
|
+
if function_call_response.audios is not None:
|
|
260
|
+
if model_response.audios is None:
|
|
261
|
+
model_response.audios = []
|
|
262
|
+
model_response.audios.extend(function_call_response.audios)
|
|
263
|
+
|
|
264
|
+
if function_call_response.videos is not None:
|
|
265
|
+
if model_response.videos is None:
|
|
266
|
+
model_response.videos = []
|
|
267
|
+
model_response.videos.extend(function_call_response.videos)
|
|
268
|
+
|
|
372
269
|
if (
|
|
373
270
|
function_call_response.event
|
|
374
271
|
in [
|
|
@@ -395,6 +292,11 @@ class Model(ABC):
|
|
|
395
292
|
self.format_function_call_results(
|
|
396
293
|
messages=messages, function_call_results=function_call_results, **model_response.extra or {}
|
|
397
294
|
)
|
|
295
|
+
|
|
296
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
297
|
+
# Handle function call media
|
|
298
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
299
|
+
|
|
398
300
|
for function_call_result in function_call_results:
|
|
399
301
|
function_call_result.log(metrics=True)
|
|
400
302
|
|
|
@@ -480,6 +382,26 @@ class Model(ABC):
|
|
|
480
382
|
function_call_limit=tool_call_limit,
|
|
481
383
|
):
|
|
482
384
|
if isinstance(function_call_response, ModelResponse):
|
|
385
|
+
# The session state is updated by the function call
|
|
386
|
+
if function_call_response.updated_session_state is not None:
|
|
387
|
+
model_response.updated_session_state = function_call_response.updated_session_state
|
|
388
|
+
|
|
389
|
+
# Media artifacts are generated by the function call
|
|
390
|
+
if function_call_response.images is not None:
|
|
391
|
+
if model_response.images is None:
|
|
392
|
+
model_response.images = []
|
|
393
|
+
model_response.images.extend(function_call_response.images)
|
|
394
|
+
|
|
395
|
+
if function_call_response.audios is not None:
|
|
396
|
+
if model_response.audios is None:
|
|
397
|
+
model_response.audios = []
|
|
398
|
+
model_response.audios.extend(function_call_response.audios)
|
|
399
|
+
|
|
400
|
+
if function_call_response.videos is not None:
|
|
401
|
+
if model_response.videos is None:
|
|
402
|
+
model_response.videos = []
|
|
403
|
+
model_response.videos.extend(function_call_response.videos)
|
|
404
|
+
|
|
483
405
|
if (
|
|
484
406
|
function_call_response.event
|
|
485
407
|
in [
|
|
@@ -505,6 +427,11 @@ class Model(ABC):
|
|
|
505
427
|
self.format_function_call_results(
|
|
506
428
|
messages=messages, function_call_results=function_call_results, **model_response.extra or {}
|
|
507
429
|
)
|
|
430
|
+
|
|
431
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
432
|
+
# Handle function call media
|
|
433
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
434
|
+
|
|
508
435
|
for function_call_result in function_call_results:
|
|
509
436
|
function_call_result.log(metrics=True)
|
|
510
437
|
|
|
@@ -541,6 +468,7 @@ class Model(ABC):
|
|
|
541
468
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
542
469
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
543
470
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
471
|
+
run_response: Optional[RunOutput] = None,
|
|
544
472
|
) -> None:
|
|
545
473
|
"""
|
|
546
474
|
Process a single model response and return the assistant message and whether to continue.
|
|
@@ -549,21 +477,14 @@ class Model(ABC):
|
|
|
549
477
|
Tuple[Message, bool]: (assistant_message, should_continue)
|
|
550
478
|
"""
|
|
551
479
|
# Generate response
|
|
552
|
-
|
|
553
|
-
|
|
480
|
+
provider_response = self.invoke(
|
|
481
|
+
assistant_message=assistant_message,
|
|
554
482
|
messages=messages,
|
|
555
483
|
response_format=response_format,
|
|
556
484
|
tools=tools,
|
|
557
485
|
tool_choice=tool_choice or self._tool_choice,
|
|
486
|
+
run_response=run_response,
|
|
558
487
|
)
|
|
559
|
-
assistant_message.metrics.stop_timer()
|
|
560
|
-
|
|
561
|
-
# Parse provider response
|
|
562
|
-
provider_response: ModelResponse = self.parse_provider_response(response, response_format=response_format)
|
|
563
|
-
|
|
564
|
-
# Add parsed data to model response
|
|
565
|
-
if provider_response.parsed is not None:
|
|
566
|
-
model_response.parsed = provider_response.parsed
|
|
567
488
|
|
|
568
489
|
# Populate the assistant message
|
|
569
490
|
self._populate_assistant_message(assistant_message=assistant_message, provider_response=provider_response)
|
|
@@ -574,16 +495,19 @@ class Model(ABC):
|
|
|
574
495
|
model_response.content = assistant_message.get_content_string()
|
|
575
496
|
else:
|
|
576
497
|
model_response.content += assistant_message.get_content_string()
|
|
577
|
-
if assistant_message.
|
|
578
|
-
model_response.
|
|
579
|
-
if assistant_message.
|
|
580
|
-
model_response.
|
|
498
|
+
if assistant_message.reasoning_content is not None:
|
|
499
|
+
model_response.reasoning_content = assistant_message.reasoning_content
|
|
500
|
+
if assistant_message.redacted_reasoning_content is not None:
|
|
501
|
+
model_response.redacted_reasoning_content = assistant_message.redacted_reasoning_content
|
|
581
502
|
if assistant_message.citations is not None:
|
|
582
503
|
model_response.citations = assistant_message.citations
|
|
583
504
|
if assistant_message.audio_output is not None:
|
|
584
|
-
|
|
505
|
+
if isinstance(assistant_message.audio_output, Audio):
|
|
506
|
+
model_response.audio = assistant_message.audio_output
|
|
585
507
|
if assistant_message.image_output is not None:
|
|
586
|
-
model_response.
|
|
508
|
+
model_response.images = [assistant_message.image_output]
|
|
509
|
+
if assistant_message.video_output is not None:
|
|
510
|
+
model_response.videos = [assistant_message.video_output]
|
|
587
511
|
if provider_response.extra is not None:
|
|
588
512
|
if model_response.extra is None:
|
|
589
513
|
model_response.extra = {}
|
|
@@ -597,6 +521,7 @@ class Model(ABC):
|
|
|
597
521
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
598
522
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
599
523
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
524
|
+
run_response: Optional[RunOutput] = None,
|
|
600
525
|
) -> None:
|
|
601
526
|
"""
|
|
602
527
|
Process a single async model response and return the assistant message and whether to continue.
|
|
@@ -605,21 +530,14 @@ class Model(ABC):
|
|
|
605
530
|
Tuple[Message, bool]: (assistant_message, should_continue)
|
|
606
531
|
"""
|
|
607
532
|
# Generate response
|
|
608
|
-
|
|
609
|
-
response = await self.ainvoke(
|
|
533
|
+
provider_response = await self.ainvoke(
|
|
610
534
|
messages=messages,
|
|
611
535
|
response_format=response_format,
|
|
612
536
|
tools=tools,
|
|
613
537
|
tool_choice=tool_choice or self._tool_choice,
|
|
538
|
+
assistant_message=assistant_message,
|
|
539
|
+
run_response=run_response,
|
|
614
540
|
)
|
|
615
|
-
assistant_message.metrics.stop_timer()
|
|
616
|
-
|
|
617
|
-
# Parse provider response
|
|
618
|
-
provider_response: ModelResponse = self.parse_provider_response(response, response_format=response_format)
|
|
619
|
-
|
|
620
|
-
# Add parsed data to model response
|
|
621
|
-
if provider_response.parsed is not None:
|
|
622
|
-
model_response.parsed = provider_response.parsed
|
|
623
541
|
|
|
624
542
|
# Populate the assistant message
|
|
625
543
|
self._populate_assistant_message(assistant_message=assistant_message, provider_response=provider_response)
|
|
@@ -630,16 +548,19 @@ class Model(ABC):
|
|
|
630
548
|
model_response.content = assistant_message.get_content_string()
|
|
631
549
|
else:
|
|
632
550
|
model_response.content += assistant_message.get_content_string()
|
|
633
|
-
if assistant_message.
|
|
634
|
-
model_response.
|
|
635
|
-
if assistant_message.
|
|
636
|
-
model_response.
|
|
551
|
+
if assistant_message.reasoning_content is not None:
|
|
552
|
+
model_response.reasoning_content = assistant_message.reasoning_content
|
|
553
|
+
if assistant_message.redacted_reasoning_content is not None:
|
|
554
|
+
model_response.redacted_reasoning_content = assistant_message.redacted_reasoning_content
|
|
637
555
|
if assistant_message.citations is not None:
|
|
638
556
|
model_response.citations = assistant_message.citations
|
|
639
557
|
if assistant_message.audio_output is not None:
|
|
640
|
-
|
|
558
|
+
if isinstance(assistant_message.audio_output, Audio):
|
|
559
|
+
model_response.audio = assistant_message.audio_output
|
|
641
560
|
if assistant_message.image_output is not None:
|
|
642
|
-
model_response.
|
|
561
|
+
model_response.images = [assistant_message.image_output]
|
|
562
|
+
if assistant_message.video_output is not None:
|
|
563
|
+
model_response.videos = [assistant_message.video_output]
|
|
643
564
|
if provider_response.extra is not None:
|
|
644
565
|
if model_response.extra is None:
|
|
645
566
|
model_response.extra = {}
|
|
@@ -677,16 +598,22 @@ class Model(ABC):
|
|
|
677
598
|
assistant_message.audio_output = provider_response.audio
|
|
678
599
|
|
|
679
600
|
# Add image to assistant message
|
|
680
|
-
if provider_response.
|
|
681
|
-
|
|
601
|
+
if provider_response.images is not None:
|
|
602
|
+
if provider_response.images:
|
|
603
|
+
assistant_message.image_output = provider_response.images[-1] # Taking last (most recent) image
|
|
604
|
+
|
|
605
|
+
# Add video to assistant message
|
|
606
|
+
if provider_response.videos is not None:
|
|
607
|
+
if provider_response.videos:
|
|
608
|
+
assistant_message.video_output = provider_response.videos[-1] # Taking last (most recent) video
|
|
682
609
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
610
|
+
if provider_response.audios is not None:
|
|
611
|
+
if provider_response.audios:
|
|
612
|
+
assistant_message.audio_output = provider_response.audios[-1] # Taking last (most recent) audio
|
|
686
613
|
|
|
687
614
|
# Add redacted thinking content to assistant message
|
|
688
|
-
if provider_response.
|
|
689
|
-
assistant_message.
|
|
615
|
+
if provider_response.redacted_reasoning_content is not None:
|
|
616
|
+
assistant_message.redacted_reasoning_content = provider_response.redacted_reasoning_content
|
|
690
617
|
|
|
691
618
|
# Add reasoning content to assistant message
|
|
692
619
|
if provider_response.reasoning_content is not None:
|
|
@@ -702,9 +629,7 @@ class Model(ABC):
|
|
|
702
629
|
|
|
703
630
|
# Add usage metrics if provided
|
|
704
631
|
if provider_response.response_usage is not None:
|
|
705
|
-
|
|
706
|
-
assistant_message=assistant_message, response_usage=provider_response.response_usage
|
|
707
|
-
)
|
|
632
|
+
assistant_message.metrics += provider_response.response_usage
|
|
708
633
|
|
|
709
634
|
return assistant_message
|
|
710
635
|
|
|
@@ -716,22 +641,28 @@ class Model(ABC):
|
|
|
716
641
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
717
642
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
718
643
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
644
|
+
run_response: Optional[RunOutput] = None,
|
|
719
645
|
) -> Iterator[ModelResponse]:
|
|
720
646
|
"""
|
|
721
647
|
Process a streaming response from the model.
|
|
722
648
|
"""
|
|
723
|
-
|
|
649
|
+
|
|
724
650
|
for response_delta in self.invoke_stream(
|
|
725
651
|
messages=messages,
|
|
652
|
+
assistant_message=assistant_message,
|
|
726
653
|
response_format=response_format,
|
|
727
654
|
tools=tools,
|
|
728
655
|
tool_choice=tool_choice or self._tool_choice,
|
|
656
|
+
run_response=run_response,
|
|
729
657
|
):
|
|
730
|
-
model_response_delta = self.parse_provider_response_delta(response_delta)
|
|
731
658
|
yield from self._populate_stream_data_and_assistant_message(
|
|
732
|
-
stream_data=stream_data,
|
|
659
|
+
stream_data=stream_data,
|
|
660
|
+
assistant_message=assistant_message,
|
|
661
|
+
model_response_delta=response_delta,
|
|
733
662
|
)
|
|
734
|
-
|
|
663
|
+
|
|
664
|
+
# Add final metrics to assistant message
|
|
665
|
+
self._populate_assistant_message(assistant_message=assistant_message, provider_response=response_delta)
|
|
735
666
|
|
|
736
667
|
def response_stream(
|
|
737
668
|
self,
|
|
@@ -742,7 +673,8 @@ class Model(ABC):
|
|
|
742
673
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
743
674
|
tool_call_limit: Optional[int] = None,
|
|
744
675
|
stream_model_response: bool = True,
|
|
745
|
-
|
|
676
|
+
run_response: Optional[RunOutput] = None,
|
|
677
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
746
678
|
"""
|
|
747
679
|
Generate a streaming response from the model.
|
|
748
680
|
"""
|
|
@@ -766,15 +698,16 @@ class Model(ABC):
|
|
|
766
698
|
response_format=response_format,
|
|
767
699
|
tools=tools,
|
|
768
700
|
tool_choice=tool_choice or self._tool_choice,
|
|
701
|
+
run_response=run_response,
|
|
769
702
|
)
|
|
770
703
|
|
|
771
704
|
# Populate assistant message from stream data
|
|
772
705
|
if stream_data.response_content:
|
|
773
706
|
assistant_message.content = stream_data.response_content
|
|
774
|
-
if stream_data.
|
|
775
|
-
assistant_message.
|
|
776
|
-
if stream_data.
|
|
777
|
-
assistant_message.
|
|
707
|
+
if stream_data.response_reasoning_content:
|
|
708
|
+
assistant_message.reasoning_content = stream_data.response_reasoning_content
|
|
709
|
+
if stream_data.response_redacted_reasoning_content:
|
|
710
|
+
assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
|
|
778
711
|
if stream_data.response_provider_data:
|
|
779
712
|
assistant_message.provider_data = stream_data.response_provider_data
|
|
780
713
|
if stream_data.response_citations:
|
|
@@ -828,6 +761,10 @@ class Model(ABC):
|
|
|
828
761
|
else:
|
|
829
762
|
self.format_function_call_results(messages=messages, function_call_results=function_call_results)
|
|
830
763
|
|
|
764
|
+
# Handle function call media
|
|
765
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
766
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
767
|
+
|
|
831
768
|
for function_call_result in function_call_results:
|
|
832
769
|
function_call_result.log(metrics=True)
|
|
833
770
|
|
|
@@ -863,23 +800,28 @@ class Model(ABC):
|
|
|
863
800
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
864
801
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
865
802
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
803
|
+
run_response: Optional[RunOutput] = None,
|
|
866
804
|
) -> AsyncIterator[ModelResponse]:
|
|
867
805
|
"""
|
|
868
806
|
Process a streaming response from the model.
|
|
869
807
|
"""
|
|
870
|
-
assistant_message.metrics.start_timer()
|
|
871
808
|
async for response_delta in self.ainvoke_stream(
|
|
872
809
|
messages=messages,
|
|
810
|
+
assistant_message=assistant_message,
|
|
873
811
|
response_format=response_format,
|
|
874
812
|
tools=tools,
|
|
875
813
|
tool_choice=tool_choice or self._tool_choice,
|
|
814
|
+
run_response=run_response,
|
|
876
815
|
): # type: ignore
|
|
877
|
-
model_response_delta = self.parse_provider_response_delta(response_delta)
|
|
878
816
|
for model_response in self._populate_stream_data_and_assistant_message(
|
|
879
|
-
stream_data=stream_data,
|
|
817
|
+
stream_data=stream_data,
|
|
818
|
+
assistant_message=assistant_message,
|
|
819
|
+
model_response_delta=response_delta,
|
|
880
820
|
):
|
|
881
821
|
yield model_response
|
|
882
|
-
|
|
822
|
+
|
|
823
|
+
# Populate the assistant message
|
|
824
|
+
self._populate_assistant_message(assistant_message=assistant_message, provider_response=model_response)
|
|
883
825
|
|
|
884
826
|
async def aresponse_stream(
|
|
885
827
|
self,
|
|
@@ -890,7 +832,8 @@ class Model(ABC):
|
|
|
890
832
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
891
833
|
tool_call_limit: Optional[int] = None,
|
|
892
834
|
stream_model_response: bool = True,
|
|
893
|
-
|
|
835
|
+
run_response: Optional[RunOutput] = None,
|
|
836
|
+
) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
894
837
|
"""
|
|
895
838
|
Generate an asynchronous streaming response from the model.
|
|
896
839
|
"""
|
|
@@ -914,16 +857,17 @@ class Model(ABC):
|
|
|
914
857
|
response_format=response_format,
|
|
915
858
|
tools=tools,
|
|
916
859
|
tool_choice=tool_choice or self._tool_choice,
|
|
860
|
+
run_response=run_response,
|
|
917
861
|
):
|
|
918
862
|
yield response
|
|
919
863
|
|
|
920
864
|
# Populate assistant message from stream data
|
|
921
865
|
if stream_data.response_content:
|
|
922
866
|
assistant_message.content = stream_data.response_content
|
|
923
|
-
if stream_data.
|
|
924
|
-
assistant_message.
|
|
925
|
-
if stream_data.
|
|
926
|
-
assistant_message.
|
|
867
|
+
if stream_data.response_reasoning_content:
|
|
868
|
+
assistant_message.reasoning_content = stream_data.response_reasoning_content
|
|
869
|
+
if stream_data.response_redacted_reasoning_content:
|
|
870
|
+
assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
|
|
927
871
|
if stream_data.response_provider_data:
|
|
928
872
|
assistant_message.provider_data = stream_data.response_provider_data
|
|
929
873
|
if stream_data.response_audio:
|
|
@@ -940,6 +884,7 @@ class Model(ABC):
|
|
|
940
884
|
response_format=response_format,
|
|
941
885
|
tools=tools,
|
|
942
886
|
tool_choice=tool_choice or self._tool_choice,
|
|
887
|
+
run_response=run_response,
|
|
943
888
|
)
|
|
944
889
|
yield model_response
|
|
945
890
|
|
|
@@ -975,6 +920,10 @@ class Model(ABC):
|
|
|
975
920
|
else:
|
|
976
921
|
self.format_function_call_results(messages=messages, function_call_results=function_call_results)
|
|
977
922
|
|
|
923
|
+
# Handle function call media
|
|
924
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
925
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
926
|
+
|
|
978
927
|
for function_call_result in function_call_results:
|
|
979
928
|
function_call_result.log(metrics=True)
|
|
980
929
|
|
|
@@ -1006,11 +955,6 @@ class Model(ABC):
|
|
|
1006
955
|
self, stream_data: MessageData, assistant_message: Message, model_response_delta: ModelResponse
|
|
1007
956
|
) -> Iterator[ModelResponse]:
|
|
1008
957
|
"""Update the stream data and assistant message with the model response."""
|
|
1009
|
-
|
|
1010
|
-
# Update metrics
|
|
1011
|
-
if not assistant_message.metrics.time_to_first_token:
|
|
1012
|
-
assistant_message.metrics.set_time_to_first_token()
|
|
1013
|
-
|
|
1014
958
|
# Add role to assistant message
|
|
1015
959
|
if model_response_delta.role is not None:
|
|
1016
960
|
assistant_message.role = model_response_delta.role
|
|
@@ -1021,16 +965,12 @@ class Model(ABC):
|
|
|
1021
965
|
stream_data.response_content += model_response_delta.content
|
|
1022
966
|
should_yield = True
|
|
1023
967
|
|
|
1024
|
-
if model_response_delta.thinking is not None:
|
|
1025
|
-
stream_data.response_thinking += model_response_delta.thinking
|
|
1026
|
-
should_yield = True
|
|
1027
|
-
|
|
1028
968
|
if model_response_delta.reasoning_content is not None:
|
|
1029
|
-
stream_data.
|
|
969
|
+
stream_data.response_reasoning_content += model_response_delta.reasoning_content
|
|
1030
970
|
should_yield = True
|
|
1031
971
|
|
|
1032
|
-
if model_response_delta.
|
|
1033
|
-
stream_data.
|
|
972
|
+
if model_response_delta.redacted_reasoning_content is not None:
|
|
973
|
+
stream_data.response_redacted_reasoning_content += model_response_delta.redacted_reasoning_content
|
|
1034
974
|
should_yield = True
|
|
1035
975
|
|
|
1036
976
|
if model_response_delta.citations is not None:
|
|
@@ -1049,40 +989,45 @@ class Model(ABC):
|
|
|
1049
989
|
stream_data.response_tool_calls.extend(model_response_delta.tool_calls)
|
|
1050
990
|
should_yield = True
|
|
1051
991
|
|
|
1052
|
-
if model_response_delta.audio is not None:
|
|
992
|
+
if model_response_delta.audio is not None and isinstance(model_response_delta.audio, Audio):
|
|
1053
993
|
if stream_data.response_audio is None:
|
|
1054
|
-
stream_data.response_audio =
|
|
994
|
+
stream_data.response_audio = Audio(id=str(uuid4()), content="", transcript="")
|
|
995
|
+
|
|
996
|
+
from typing import cast
|
|
997
|
+
|
|
998
|
+
audio_response = cast(Audio, model_response_delta.audio)
|
|
1055
999
|
|
|
1056
1000
|
# Update the stream data with audio information
|
|
1057
|
-
if
|
|
1058
|
-
stream_data.response_audio.id =
|
|
1059
|
-
if
|
|
1060
|
-
stream_data.response_audio.content +=
|
|
1061
|
-
if
|
|
1062
|
-
stream_data.response_audio.transcript +=
|
|
1063
|
-
if
|
|
1064
|
-
stream_data.response_audio.expires_at =
|
|
1065
|
-
if
|
|
1066
|
-
stream_data.response_audio.mime_type =
|
|
1067
|
-
stream_data.response_audio.sample_rate =
|
|
1068
|
-
stream_data.response_audio.channels =
|
|
1001
|
+
if audio_response.id is not None:
|
|
1002
|
+
stream_data.response_audio.id = audio_response.id # type: ignore
|
|
1003
|
+
if audio_response.content is not None:
|
|
1004
|
+
stream_data.response_audio.content += audio_response.content # type: ignore
|
|
1005
|
+
if audio_response.transcript is not None:
|
|
1006
|
+
stream_data.response_audio.transcript += audio_response.transcript # type: ignore
|
|
1007
|
+
if audio_response.expires_at is not None:
|
|
1008
|
+
stream_data.response_audio.expires_at = audio_response.expires_at
|
|
1009
|
+
if audio_response.mime_type is not None:
|
|
1010
|
+
stream_data.response_audio.mime_type = audio_response.mime_type
|
|
1011
|
+
stream_data.response_audio.sample_rate = audio_response.sample_rate
|
|
1012
|
+
stream_data.response_audio.channels = audio_response.channels
|
|
1069
1013
|
|
|
1070
1014
|
should_yield = True
|
|
1071
1015
|
|
|
1072
|
-
if model_response_delta.
|
|
1016
|
+
if model_response_delta.images:
|
|
1073
1017
|
if stream_data.response_image is None:
|
|
1074
|
-
stream_data.response_image = model_response_delta.
|
|
1018
|
+
stream_data.response_image = model_response_delta.images[-1]
|
|
1019
|
+
should_yield = True
|
|
1020
|
+
|
|
1021
|
+
if model_response_delta.videos:
|
|
1022
|
+
if stream_data.response_video is None:
|
|
1023
|
+
stream_data.response_video = model_response_delta.videos[-1]
|
|
1024
|
+
should_yield = True
|
|
1075
1025
|
|
|
1076
1026
|
if model_response_delta.extra is not None:
|
|
1077
1027
|
if stream_data.extra is None:
|
|
1078
1028
|
stream_data.extra = {}
|
|
1079
1029
|
stream_data.extra.update(model_response_delta.extra)
|
|
1080
1030
|
|
|
1081
|
-
if model_response_delta.response_usage is not None:
|
|
1082
|
-
_add_usage_metrics_to_assistant_message(
|
|
1083
|
-
assistant_message=assistant_message, response_usage=model_response_delta.response_usage
|
|
1084
|
-
)
|
|
1085
|
-
|
|
1086
1031
|
if should_yield:
|
|
1087
1032
|
yield model_response_delta
|
|
1088
1033
|
|
|
@@ -1142,11 +1087,24 @@ class Model(ABC):
|
|
|
1142
1087
|
success: bool,
|
|
1143
1088
|
output: Optional[Union[List[Any], str]] = None,
|
|
1144
1089
|
timer: Optional[Timer] = None,
|
|
1090
|
+
function_execution_result: Optional[FunctionExecutionResult] = None,
|
|
1145
1091
|
) -> Message:
|
|
1146
1092
|
"""Create a function call result message."""
|
|
1147
1093
|
kwargs = {}
|
|
1148
1094
|
if timer is not None:
|
|
1149
|
-
kwargs["metrics"] =
|
|
1095
|
+
kwargs["metrics"] = Metrics(duration=timer.elapsed)
|
|
1096
|
+
|
|
1097
|
+
# Include media artifacts from function execution result in the tool message
|
|
1098
|
+
images = None
|
|
1099
|
+
videos = None
|
|
1100
|
+
audios = None
|
|
1101
|
+
|
|
1102
|
+
if success and function_execution_result:
|
|
1103
|
+
# With unified classes, no conversion needed - use directly
|
|
1104
|
+
images = function_execution_result.images
|
|
1105
|
+
videos = function_execution_result.videos
|
|
1106
|
+
audios = function_execution_result.audios
|
|
1107
|
+
|
|
1150
1108
|
return Message(
|
|
1151
1109
|
role=self.tool_message_role,
|
|
1152
1110
|
content=output if success else function_call.error,
|
|
@@ -1155,7 +1113,10 @@ class Model(ABC):
|
|
|
1155
1113
|
tool_args=function_call.arguments,
|
|
1156
1114
|
tool_call_error=not success,
|
|
1157
1115
|
stop_after_tool_call=function_call.function.stop_after_tool_call,
|
|
1158
|
-
|
|
1116
|
+
images=images,
|
|
1117
|
+
videos=videos,
|
|
1118
|
+
audio=audios,
|
|
1119
|
+
**kwargs, # type: ignore
|
|
1159
1120
|
)
|
|
1160
1121
|
|
|
1161
1122
|
def create_tool_call_limit_error_result(self, function_call: FunctionCall) -> Message:
|
|
@@ -1172,8 +1133,8 @@ class Model(ABC):
|
|
|
1172
1133
|
self,
|
|
1173
1134
|
function_call: FunctionCall,
|
|
1174
1135
|
function_call_results: List[Message],
|
|
1175
|
-
|
|
1176
|
-
) -> Iterator[Union[ModelResponse,
|
|
1136
|
+
additional_input: Optional[List[Message]] = None,
|
|
1137
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1177
1138
|
# Start function call
|
|
1178
1139
|
function_call_timer = Timer()
|
|
1179
1140
|
function_call_timer.start()
|
|
@@ -1196,7 +1157,7 @@ class Model(ABC):
|
|
|
1196
1157
|
function_execution_result = function_call.execute()
|
|
1197
1158
|
except AgentRunException as a_exc:
|
|
1198
1159
|
# Update additional messages from function call
|
|
1199
|
-
_handle_agent_exception(a_exc,
|
|
1160
|
+
_handle_agent_exception(a_exc, additional_input)
|
|
1200
1161
|
# Set function call success to False if an exception occurred
|
|
1201
1162
|
except Exception as e:
|
|
1202
1163
|
log_error(f"Error executing function {function_call.function.name}: {e}")
|
|
@@ -1210,14 +1171,14 @@ class Model(ABC):
|
|
|
1210
1171
|
# Process function call output
|
|
1211
1172
|
function_call_output: str = ""
|
|
1212
1173
|
|
|
1213
|
-
if isinstance(
|
|
1214
|
-
for item in
|
|
1174
|
+
if isinstance(function_execution_result.result, (GeneratorType, collections.abc.Iterator)):
|
|
1175
|
+
for item in function_execution_result.result:
|
|
1215
1176
|
# This function yields agent/team run events
|
|
1216
|
-
if isinstance(item, tuple(get_args(
|
|
1217
|
-
item, tuple(get_args(
|
|
1177
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1178
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1218
1179
|
):
|
|
1219
1180
|
# We only capture content events
|
|
1220
|
-
if isinstance(item,
|
|
1181
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1221
1182
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1222
1183
|
function_call_output += item.content.model_dump_json()
|
|
1223
1184
|
else:
|
|
@@ -1227,6 +1188,9 @@ class Model(ABC):
|
|
|
1227
1188
|
if function_call.function.show_result:
|
|
1228
1189
|
yield ModelResponse(content=item.content)
|
|
1229
1190
|
|
|
1191
|
+
if isinstance(item, CustomEvent):
|
|
1192
|
+
function_call_output += str(item)
|
|
1193
|
+
|
|
1230
1194
|
# Yield the event itself to bubble it up
|
|
1231
1195
|
yield item
|
|
1232
1196
|
|
|
@@ -1235,13 +1199,33 @@ class Model(ABC):
|
|
|
1235
1199
|
if function_call.function.show_result:
|
|
1236
1200
|
yield ModelResponse(content=str(item))
|
|
1237
1201
|
else:
|
|
1238
|
-
|
|
1202
|
+
from agno.tools.function import ToolResult
|
|
1203
|
+
|
|
1204
|
+
if isinstance(function_execution_result.result, ToolResult):
|
|
1205
|
+
# Extract content and media from ToolResult
|
|
1206
|
+
tool_result = function_execution_result.result
|
|
1207
|
+
function_call_output = tool_result.content
|
|
1208
|
+
|
|
1209
|
+
# Transfer media from ToolResult to FunctionExecutionResult
|
|
1210
|
+
if tool_result.images:
|
|
1211
|
+
function_execution_result.images = tool_result.images
|
|
1212
|
+
if tool_result.videos:
|
|
1213
|
+
function_execution_result.videos = tool_result.videos
|
|
1214
|
+
if tool_result.audios:
|
|
1215
|
+
function_execution_result.audios = tool_result.audios
|
|
1216
|
+
else:
|
|
1217
|
+
function_call_output = str(function_execution_result.result) if function_execution_result.result else ""
|
|
1218
|
+
|
|
1239
1219
|
if function_call.function.show_result:
|
|
1240
1220
|
yield ModelResponse(content=function_call_output)
|
|
1241
1221
|
|
|
1242
1222
|
# Create and yield function call result
|
|
1243
1223
|
function_call_result = self.create_function_call_result(
|
|
1244
|
-
function_call,
|
|
1224
|
+
function_call,
|
|
1225
|
+
success=function_call_success,
|
|
1226
|
+
output=function_call_output,
|
|
1227
|
+
timer=function_call_timer,
|
|
1228
|
+
function_execution_result=function_execution_result,
|
|
1245
1229
|
)
|
|
1246
1230
|
yield ModelResponse(
|
|
1247
1231
|
content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
|
|
@@ -1257,6 +1241,11 @@ class Model(ABC):
|
|
|
1257
1241
|
)
|
|
1258
1242
|
],
|
|
1259
1243
|
event=ModelResponseEvent.tool_call_completed.value,
|
|
1244
|
+
updated_session_state=function_execution_result.updated_session_state,
|
|
1245
|
+
# Add media artifacts from function execution
|
|
1246
|
+
images=function_execution_result.images,
|
|
1247
|
+
videos=function_execution_result.videos,
|
|
1248
|
+
audios=function_execution_result.audios,
|
|
1260
1249
|
)
|
|
1261
1250
|
|
|
1262
1251
|
# Add function call to function call results
|
|
@@ -1266,13 +1255,13 @@ class Model(ABC):
|
|
|
1266
1255
|
self,
|
|
1267
1256
|
function_calls: List[FunctionCall],
|
|
1268
1257
|
function_call_results: List[Message],
|
|
1269
|
-
|
|
1258
|
+
additional_input: Optional[List[Message]] = None,
|
|
1270
1259
|
current_function_call_count: int = 0,
|
|
1271
1260
|
function_call_limit: Optional[int] = None,
|
|
1272
|
-
) -> Iterator[Union[ModelResponse,
|
|
1261
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1273
1262
|
# Additional messages from function calls that will be added to the function call results
|
|
1274
|
-
if
|
|
1275
|
-
|
|
1263
|
+
if additional_input is None:
|
|
1264
|
+
additional_input = []
|
|
1276
1265
|
|
|
1277
1266
|
for fc in function_calls:
|
|
1278
1267
|
if function_call_limit is not None:
|
|
@@ -1358,17 +1347,17 @@ class Model(ABC):
|
|
|
1358
1347
|
continue
|
|
1359
1348
|
|
|
1360
1349
|
yield from self.run_function_call(
|
|
1361
|
-
function_call=fc, function_call_results=function_call_results,
|
|
1350
|
+
function_call=fc, function_call_results=function_call_results, additional_input=additional_input
|
|
1362
1351
|
)
|
|
1363
1352
|
|
|
1364
1353
|
# Add any additional messages at the end
|
|
1365
|
-
if
|
|
1366
|
-
function_call_results.extend(
|
|
1354
|
+
if additional_input:
|
|
1355
|
+
function_call_results.extend(additional_input)
|
|
1367
1356
|
|
|
1368
1357
|
async def arun_function_call(
|
|
1369
1358
|
self,
|
|
1370
1359
|
function_call: FunctionCall,
|
|
1371
|
-
) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall]:
|
|
1360
|
+
) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall, FunctionExecutionResult]:
|
|
1372
1361
|
"""Run a single function call and return its success status, timer, and the FunctionCall object."""
|
|
1373
1362
|
from inspect import isasyncgenfunction, iscoroutine, iscoroutinefunction
|
|
1374
1363
|
|
|
@@ -1402,20 +1391,20 @@ class Model(ABC):
|
|
|
1402
1391
|
raise e
|
|
1403
1392
|
|
|
1404
1393
|
function_call_timer.stop()
|
|
1405
|
-
return success, function_call_timer, function_call
|
|
1394
|
+
return success, function_call_timer, function_call, result
|
|
1406
1395
|
|
|
1407
1396
|
async def arun_function_calls(
|
|
1408
1397
|
self,
|
|
1409
1398
|
function_calls: List[FunctionCall],
|
|
1410
1399
|
function_call_results: List[Message],
|
|
1411
|
-
|
|
1400
|
+
additional_input: Optional[List[Message]] = None,
|
|
1412
1401
|
current_function_call_count: int = 0,
|
|
1413
1402
|
function_call_limit: Optional[int] = None,
|
|
1414
1403
|
skip_pause_check: bool = False,
|
|
1415
|
-
) -> AsyncIterator[Union[ModelResponse,
|
|
1404
|
+
) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1416
1405
|
# Additional messages from function calls that will be added to the function call results
|
|
1417
|
-
if
|
|
1418
|
-
|
|
1406
|
+
if additional_input is None:
|
|
1407
|
+
additional_input = []
|
|
1419
1408
|
|
|
1420
1409
|
function_calls_to_run = []
|
|
1421
1410
|
for fc in function_calls:
|
|
@@ -1548,33 +1537,35 @@ class Model(ABC):
|
|
|
1548
1537
|
raise result
|
|
1549
1538
|
|
|
1550
1539
|
# Unpack result
|
|
1551
|
-
function_call_success, function_call_timer,
|
|
1540
|
+
function_call_success, function_call_timer, function_call, function_execution_result = result
|
|
1541
|
+
|
|
1542
|
+
updated_session_state = function_execution_result.updated_session_state
|
|
1552
1543
|
|
|
1553
1544
|
# Handle AgentRunException
|
|
1554
1545
|
if isinstance(function_call_success, AgentRunException):
|
|
1555
1546
|
a_exc = function_call_success
|
|
1556
1547
|
# Update additional messages from function call
|
|
1557
|
-
_handle_agent_exception(a_exc,
|
|
1548
|
+
_handle_agent_exception(a_exc, additional_input)
|
|
1558
1549
|
# Set function call success to False if an exception occurred
|
|
1559
1550
|
function_call_success = False
|
|
1560
1551
|
|
|
1561
1552
|
# Process function call output
|
|
1562
1553
|
function_call_output: str = ""
|
|
1563
|
-
if isinstance(
|
|
1564
|
-
for item in
|
|
1554
|
+
if isinstance(function_call.result, (GeneratorType, collections.abc.Iterator)):
|
|
1555
|
+
for item in function_call.result:
|
|
1565
1556
|
# This function yields agent/team run events
|
|
1566
|
-
if isinstance(item, tuple(get_args(
|
|
1567
|
-
item, tuple(get_args(
|
|
1557
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1558
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1568
1559
|
):
|
|
1569
1560
|
# We only capture content events
|
|
1570
|
-
if isinstance(item,
|
|
1561
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1571
1562
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1572
1563
|
function_call_output += item.content.model_dump_json()
|
|
1573
1564
|
else:
|
|
1574
1565
|
# Capture output
|
|
1575
1566
|
function_call_output += item.content or ""
|
|
1576
1567
|
|
|
1577
|
-
if
|
|
1568
|
+
if function_call.function.show_result:
|
|
1578
1569
|
yield ModelResponse(content=item.content)
|
|
1579
1570
|
continue
|
|
1580
1571
|
|
|
@@ -1582,43 +1573,66 @@ class Model(ABC):
|
|
|
1582
1573
|
yield item
|
|
1583
1574
|
else:
|
|
1584
1575
|
function_call_output += str(item)
|
|
1585
|
-
if
|
|
1576
|
+
if function_call.function.show_result:
|
|
1586
1577
|
yield ModelResponse(content=str(item))
|
|
1587
|
-
elif isinstance(
|
|
1588
|
-
async for item in
|
|
1578
|
+
elif isinstance(function_call.result, (AsyncGeneratorType, collections.abc.AsyncIterator)):
|
|
1579
|
+
async for item in function_call.result:
|
|
1589
1580
|
# This function yields agent/team run events
|
|
1590
|
-
if isinstance(item, tuple(get_args(
|
|
1591
|
-
item, tuple(get_args(
|
|
1581
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1582
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1592
1583
|
):
|
|
1593
1584
|
# We only capture content events
|
|
1594
|
-
if isinstance(item,
|
|
1585
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1595
1586
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1596
1587
|
function_call_output += item.content.model_dump_json()
|
|
1597
1588
|
else:
|
|
1598
1589
|
# Capture output
|
|
1599
1590
|
function_call_output += item.content or ""
|
|
1600
1591
|
|
|
1601
|
-
if
|
|
1592
|
+
if function_call.function.show_result:
|
|
1602
1593
|
yield ModelResponse(content=item.content)
|
|
1603
1594
|
continue
|
|
1604
1595
|
|
|
1596
|
+
if isinstance(item, CustomEvent):
|
|
1597
|
+
function_call_output += str(item)
|
|
1598
|
+
|
|
1605
1599
|
# Yield the event itself to bubble it up
|
|
1606
1600
|
yield item
|
|
1601
|
+
|
|
1602
|
+
# Yield custom events emitted by the tool
|
|
1607
1603
|
else:
|
|
1608
1604
|
function_call_output += str(item)
|
|
1609
|
-
if
|
|
1605
|
+
if function_call.function.show_result:
|
|
1610
1606
|
yield ModelResponse(content=str(item))
|
|
1611
1607
|
else:
|
|
1612
|
-
|
|
1613
|
-
|
|
1608
|
+
from agno.tools.function import ToolResult
|
|
1609
|
+
|
|
1610
|
+
if isinstance(function_execution_result.result, ToolResult):
|
|
1611
|
+
tool_result = function_execution_result.result
|
|
1612
|
+
function_call_output = tool_result.content
|
|
1613
|
+
|
|
1614
|
+
if tool_result.images:
|
|
1615
|
+
function_execution_result.images = tool_result.images
|
|
1616
|
+
if tool_result.videos:
|
|
1617
|
+
function_execution_result.videos = tool_result.videos
|
|
1618
|
+
if tool_result.audios:
|
|
1619
|
+
function_execution_result.audios = tool_result.audios
|
|
1620
|
+
else:
|
|
1621
|
+
function_call_output = str(function_call.result)
|
|
1622
|
+
|
|
1623
|
+
if function_call.function.show_result:
|
|
1614
1624
|
yield ModelResponse(content=function_call_output)
|
|
1615
1625
|
|
|
1616
1626
|
# Create and yield function call result
|
|
1617
1627
|
function_call_result = self.create_function_call_result(
|
|
1618
|
-
|
|
1628
|
+
function_call,
|
|
1629
|
+
success=function_call_success,
|
|
1630
|
+
output=function_call_output,
|
|
1631
|
+
timer=function_call_timer,
|
|
1632
|
+
function_execution_result=function_execution_result,
|
|
1619
1633
|
)
|
|
1620
1634
|
yield ModelResponse(
|
|
1621
|
-
content=f"{
|
|
1635
|
+
content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
|
|
1622
1636
|
tool_executions=[
|
|
1623
1637
|
ToolExecution(
|
|
1624
1638
|
tool_call_id=function_call_result.tool_call_id,
|
|
@@ -1631,14 +1645,18 @@ class Model(ABC):
|
|
|
1631
1645
|
)
|
|
1632
1646
|
],
|
|
1633
1647
|
event=ModelResponseEvent.tool_call_completed.value,
|
|
1648
|
+
updated_session_state=updated_session_state,
|
|
1649
|
+
images=function_execution_result.images,
|
|
1650
|
+
videos=function_execution_result.videos,
|
|
1651
|
+
audios=function_execution_result.audios,
|
|
1634
1652
|
)
|
|
1635
1653
|
|
|
1636
1654
|
# Add function call result to function call results
|
|
1637
1655
|
function_call_results.append(function_call_result)
|
|
1638
1656
|
|
|
1639
1657
|
# Add any additional messages at the end
|
|
1640
|
-
if
|
|
1641
|
-
function_call_results.extend(
|
|
1658
|
+
if additional_input:
|
|
1659
|
+
function_call_results.extend(additional_input)
|
|
1642
1660
|
|
|
1643
1661
|
def _prepare_function_calls(
|
|
1644
1662
|
self,
|
|
@@ -1669,6 +1687,44 @@ class Model(ABC):
|
|
|
1669
1687
|
if len(function_call_results) > 0:
|
|
1670
1688
|
messages.extend(function_call_results)
|
|
1671
1689
|
|
|
1690
|
+
def _handle_function_call_media(self, messages: List[Message], function_call_results: List[Message]) -> None:
|
|
1691
|
+
"""
|
|
1692
|
+
Handle media artifacts from function calls by adding follow-up user messages for generated media if needed.
|
|
1693
|
+
"""
|
|
1694
|
+
if not function_call_results:
|
|
1695
|
+
return
|
|
1696
|
+
|
|
1697
|
+
# Collect all media artifacts from function calls
|
|
1698
|
+
all_images: List[Image] = []
|
|
1699
|
+
all_videos: List[Video] = []
|
|
1700
|
+
all_audio: List[Audio] = []
|
|
1701
|
+
|
|
1702
|
+
for result_message in function_call_results:
|
|
1703
|
+
if result_message.images:
|
|
1704
|
+
all_images.extend(result_message.images)
|
|
1705
|
+
# Remove images from tool message to avoid errors on the LLMs
|
|
1706
|
+
result_message.images = None
|
|
1707
|
+
|
|
1708
|
+
if result_message.videos:
|
|
1709
|
+
all_videos.extend(result_message.videos)
|
|
1710
|
+
result_message.videos = None
|
|
1711
|
+
|
|
1712
|
+
if result_message.audio:
|
|
1713
|
+
all_audio.extend(result_message.audio)
|
|
1714
|
+
result_message.audio = None
|
|
1715
|
+
|
|
1716
|
+
# If we have media artifacts, add a follow-up "user" message instead of a "tool"
|
|
1717
|
+
# message with the media artifacts which throws error for some models
|
|
1718
|
+
if all_images or all_videos or all_audio:
|
|
1719
|
+
media_message = Message(
|
|
1720
|
+
role="user",
|
|
1721
|
+
content="Take note of the following content",
|
|
1722
|
+
images=all_images if all_images else None,
|
|
1723
|
+
videos=all_videos if all_videos else None,
|
|
1724
|
+
audio=all_audio if all_audio else None,
|
|
1725
|
+
)
|
|
1726
|
+
messages.append(media_message)
|
|
1727
|
+
|
|
1672
1728
|
def get_system_message_for_model(self, tools: Optional[List[Any]] = None) -> Optional[str]:
|
|
1673
1729
|
return self.system_prompt
|
|
1674
1730
|
|