agno 1.8.1__py3-none-any.whl → 2.0.0a1__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 +2778 -4123
- agno/api/agent.py +9 -65
- agno/api/api.py +5 -46
- agno/api/evals.py +6 -17
- 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 +9 -64
- 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 +1749 -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 +1438 -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 +888 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1051 -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 +1417 -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 +298 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1720 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +281 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1371 -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 +1722 -0
- agno/db/singlestore/utils.py +327 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1680 -0
- agno/db/sqlite/utils.py +269 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +142 -43
- 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 +10 -10
- 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 +1515 -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 +68 -15
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/knowledge/reader/gcs_reader.py +67 -0
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +36 -9
- agno/{document → knowledge}/reader/pdf_reader.py +79 -21
- agno/knowledge/reader/reader_factory.py +275 -0
- agno/knowledge/reader/s3_reader.py +171 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +84 -0
- agno/knowledge/reader/web_search_reader.py +389 -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/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 +129 -82
- agno/models/aws/bedrock.py +107 -175
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +347 -287
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +100 -42
- 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 +38 -144
- 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 +84 -46
- agno/models/openai/chat.py +121 -23
- 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 +14 -8
- 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 +393 -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 +65 -28
- 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 +33 -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 +30 -0
- agno/os/router.py +843 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +204 -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 +413 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +179 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +58 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +163 -0
- agno/os/schema.py +892 -0
- agno/{app/playground → os}/settings.py +8 -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/{response.py → agent.py} +144 -72
- agno/run/base.py +44 -58
- agno/run/cancel.py +83 -0
- agno/run/team.py +133 -77
- agno/run/workflow.py +537 -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 +2961 -4253
- 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 +42 -22
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +18 -13
- 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 +18 -11
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +16 -7
- 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 +61 -61
- agno/tools/eleven_labs.py +35 -28
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +29 -29
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +68 -17
- agno/tools/giphy.py +22 -10
- 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 +18 -33
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +31 -19
- 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 +32 -14
- agno/tools/models/gemini.py +58 -31
- agno/tools/models/groq.py +29 -20
- agno/tools/models/nebius.py +27 -11
- agno/tools/models_labs.py +39 -15
- 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 +57 -26
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +62 -46
- 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 +54 -41
- 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 +95 -118
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +2 -2
- agno/utils/mcp.py +11 -5
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/claude.py +6 -4
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1565 -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/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} +200 -42
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2394 -696
- agno-2.0.0a1.dist-info/METADATA +355 -0
- agno-2.0.0a1.dist-info/RECORD +514 -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/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/{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.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.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 AudioResponse, ImageArtifact
|
|
26
|
-
from agno.models.message import Citations, Message
|
|
24
|
+
from agno.media import Audio, AudioArtifact, AudioResponse, Image, ImageArtifact, Video, VideoArtifact
|
|
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 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
46
|
response_audio: Optional[AudioResponse] = None
|
|
47
47
|
response_image: Optional[ImageArtifact] = None
|
|
48
|
+
response_video: Optional[VideoArtifact] = 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,21 @@ 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, AudioArtifact):
|
|
506
|
+
model_response.audios = [assistant_message.audio_output]
|
|
507
|
+
elif isinstance(assistant_message.audio_output, AudioResponse):
|
|
508
|
+
model_response.audio = assistant_message.audio_output
|
|
585
509
|
if assistant_message.image_output is not None:
|
|
586
|
-
model_response.
|
|
510
|
+
model_response.images = [assistant_message.image_output]
|
|
511
|
+
if assistant_message.video_output is not None:
|
|
512
|
+
model_response.videos = [assistant_message.video_output]
|
|
587
513
|
if provider_response.extra is not None:
|
|
588
514
|
if model_response.extra is None:
|
|
589
515
|
model_response.extra = {}
|
|
@@ -597,6 +523,7 @@ class Model(ABC):
|
|
|
597
523
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
598
524
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
599
525
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
526
|
+
run_response: Optional[RunOutput] = None,
|
|
600
527
|
) -> None:
|
|
601
528
|
"""
|
|
602
529
|
Process a single async model response and return the assistant message and whether to continue.
|
|
@@ -605,21 +532,14 @@ class Model(ABC):
|
|
|
605
532
|
Tuple[Message, bool]: (assistant_message, should_continue)
|
|
606
533
|
"""
|
|
607
534
|
# Generate response
|
|
608
|
-
|
|
609
|
-
response = await self.ainvoke(
|
|
535
|
+
provider_response = await self.ainvoke(
|
|
610
536
|
messages=messages,
|
|
611
537
|
response_format=response_format,
|
|
612
538
|
tools=tools,
|
|
613
539
|
tool_choice=tool_choice or self._tool_choice,
|
|
540
|
+
assistant_message=assistant_message,
|
|
541
|
+
run_response=run_response,
|
|
614
542
|
)
|
|
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
543
|
|
|
624
544
|
# Populate the assistant message
|
|
625
545
|
self._populate_assistant_message(assistant_message=assistant_message, provider_response=provider_response)
|
|
@@ -630,16 +550,21 @@ class Model(ABC):
|
|
|
630
550
|
model_response.content = assistant_message.get_content_string()
|
|
631
551
|
else:
|
|
632
552
|
model_response.content += assistant_message.get_content_string()
|
|
633
|
-
if assistant_message.
|
|
634
|
-
model_response.
|
|
635
|
-
if assistant_message.
|
|
636
|
-
model_response.
|
|
553
|
+
if assistant_message.reasoning_content is not None:
|
|
554
|
+
model_response.reasoning_content = assistant_message.reasoning_content
|
|
555
|
+
if assistant_message.redacted_reasoning_content is not None:
|
|
556
|
+
model_response.redacted_reasoning_content = assistant_message.redacted_reasoning_content
|
|
637
557
|
if assistant_message.citations is not None:
|
|
638
558
|
model_response.citations = assistant_message.citations
|
|
639
559
|
if assistant_message.audio_output is not None:
|
|
640
|
-
|
|
560
|
+
if isinstance(assistant_message.audio_output, AudioArtifact):
|
|
561
|
+
model_response.audios = [assistant_message.audio_output]
|
|
562
|
+
elif isinstance(assistant_message.audio_output, AudioResponse):
|
|
563
|
+
model_response.audio = assistant_message.audio_output
|
|
641
564
|
if assistant_message.image_output is not None:
|
|
642
|
-
model_response.
|
|
565
|
+
model_response.images = [assistant_message.image_output]
|
|
566
|
+
if assistant_message.video_output is not None:
|
|
567
|
+
model_response.videos = [assistant_message.video_output]
|
|
643
568
|
if provider_response.extra is not None:
|
|
644
569
|
if model_response.extra is None:
|
|
645
570
|
model_response.extra = {}
|
|
@@ -677,16 +602,22 @@ class Model(ABC):
|
|
|
677
602
|
assistant_message.audio_output = provider_response.audio
|
|
678
603
|
|
|
679
604
|
# Add image to assistant message
|
|
680
|
-
if provider_response.
|
|
681
|
-
|
|
605
|
+
if provider_response.images is not None:
|
|
606
|
+
if provider_response.images:
|
|
607
|
+
assistant_message.image_output = provider_response.images[-1] # Taking last (most recent) image
|
|
682
608
|
|
|
683
|
-
# Add
|
|
684
|
-
if provider_response.
|
|
685
|
-
|
|
609
|
+
# Add video to assistant message
|
|
610
|
+
if provider_response.videos is not None:
|
|
611
|
+
if provider_response.videos:
|
|
612
|
+
assistant_message.video_output = provider_response.videos[-1] # Taking last (most recent) video
|
|
613
|
+
|
|
614
|
+
if provider_response.audios is not None:
|
|
615
|
+
if provider_response.audios:
|
|
616
|
+
assistant_message.audio_output = provider_response.audios[-1] # Taking last (most recent) audio
|
|
686
617
|
|
|
687
618
|
# Add redacted thinking content to assistant message
|
|
688
|
-
if provider_response.
|
|
689
|
-
assistant_message.
|
|
619
|
+
if provider_response.redacted_reasoning_content is not None:
|
|
620
|
+
assistant_message.redacted_reasoning_content = provider_response.redacted_reasoning_content
|
|
690
621
|
|
|
691
622
|
# Add reasoning content to assistant message
|
|
692
623
|
if provider_response.reasoning_content is not None:
|
|
@@ -702,9 +633,7 @@ class Model(ABC):
|
|
|
702
633
|
|
|
703
634
|
# Add usage metrics if provided
|
|
704
635
|
if provider_response.response_usage is not None:
|
|
705
|
-
|
|
706
|
-
assistant_message=assistant_message, response_usage=provider_response.response_usage
|
|
707
|
-
)
|
|
636
|
+
assistant_message.metrics += provider_response.response_usage
|
|
708
637
|
|
|
709
638
|
return assistant_message
|
|
710
639
|
|
|
@@ -716,22 +645,28 @@ class Model(ABC):
|
|
|
716
645
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
717
646
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
718
647
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
648
|
+
run_response: Optional[RunOutput] = None,
|
|
719
649
|
) -> Iterator[ModelResponse]:
|
|
720
650
|
"""
|
|
721
651
|
Process a streaming response from the model.
|
|
722
652
|
"""
|
|
723
|
-
|
|
653
|
+
|
|
724
654
|
for response_delta in self.invoke_stream(
|
|
725
655
|
messages=messages,
|
|
656
|
+
assistant_message=assistant_message,
|
|
726
657
|
response_format=response_format,
|
|
727
658
|
tools=tools,
|
|
728
659
|
tool_choice=tool_choice or self._tool_choice,
|
|
660
|
+
run_response=run_response,
|
|
729
661
|
):
|
|
730
|
-
model_response_delta = self.parse_provider_response_delta(response_delta)
|
|
731
662
|
yield from self._populate_stream_data_and_assistant_message(
|
|
732
|
-
stream_data=stream_data,
|
|
663
|
+
stream_data=stream_data,
|
|
664
|
+
assistant_message=assistant_message,
|
|
665
|
+
model_response_delta=response_delta,
|
|
733
666
|
)
|
|
734
|
-
|
|
667
|
+
|
|
668
|
+
# Add final metrics to assistant message
|
|
669
|
+
self._populate_assistant_message(assistant_message=assistant_message, provider_response=response_delta)
|
|
735
670
|
|
|
736
671
|
def response_stream(
|
|
737
672
|
self,
|
|
@@ -742,7 +677,8 @@ class Model(ABC):
|
|
|
742
677
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
743
678
|
tool_call_limit: Optional[int] = None,
|
|
744
679
|
stream_model_response: bool = True,
|
|
745
|
-
|
|
680
|
+
run_response: Optional[RunOutput] = None,
|
|
681
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
746
682
|
"""
|
|
747
683
|
Generate a streaming response from the model.
|
|
748
684
|
"""
|
|
@@ -766,15 +702,16 @@ class Model(ABC):
|
|
|
766
702
|
response_format=response_format,
|
|
767
703
|
tools=tools,
|
|
768
704
|
tool_choice=tool_choice or self._tool_choice,
|
|
705
|
+
run_response=run_response,
|
|
769
706
|
)
|
|
770
707
|
|
|
771
708
|
# Populate assistant message from stream data
|
|
772
709
|
if stream_data.response_content:
|
|
773
710
|
assistant_message.content = stream_data.response_content
|
|
774
|
-
if stream_data.
|
|
775
|
-
assistant_message.
|
|
776
|
-
if stream_data.
|
|
777
|
-
assistant_message.
|
|
711
|
+
if stream_data.response_reasoning_content:
|
|
712
|
+
assistant_message.reasoning_content = stream_data.response_reasoning_content
|
|
713
|
+
if stream_data.response_redacted_reasoning_content:
|
|
714
|
+
assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
|
|
778
715
|
if stream_data.response_provider_data:
|
|
779
716
|
assistant_message.provider_data = stream_data.response_provider_data
|
|
780
717
|
if stream_data.response_citations:
|
|
@@ -828,6 +765,10 @@ class Model(ABC):
|
|
|
828
765
|
else:
|
|
829
766
|
self.format_function_call_results(messages=messages, function_call_results=function_call_results)
|
|
830
767
|
|
|
768
|
+
# Handle function call media
|
|
769
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
770
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
771
|
+
|
|
831
772
|
for function_call_result in function_call_results:
|
|
832
773
|
function_call_result.log(metrics=True)
|
|
833
774
|
|
|
@@ -863,23 +804,28 @@ class Model(ABC):
|
|
|
863
804
|
response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
|
|
864
805
|
tools: Optional[List[Dict[str, Any]]] = None,
|
|
865
806
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
807
|
+
run_response: Optional[RunOutput] = None,
|
|
866
808
|
) -> AsyncIterator[ModelResponse]:
|
|
867
809
|
"""
|
|
868
810
|
Process a streaming response from the model.
|
|
869
811
|
"""
|
|
870
|
-
assistant_message.metrics.start_timer()
|
|
871
812
|
async for response_delta in self.ainvoke_stream(
|
|
872
813
|
messages=messages,
|
|
814
|
+
assistant_message=assistant_message,
|
|
873
815
|
response_format=response_format,
|
|
874
816
|
tools=tools,
|
|
875
817
|
tool_choice=tool_choice or self._tool_choice,
|
|
818
|
+
run_response=run_response,
|
|
876
819
|
): # type: ignore
|
|
877
|
-
model_response_delta = self.parse_provider_response_delta(response_delta)
|
|
878
820
|
for model_response in self._populate_stream_data_and_assistant_message(
|
|
879
|
-
stream_data=stream_data,
|
|
821
|
+
stream_data=stream_data,
|
|
822
|
+
assistant_message=assistant_message,
|
|
823
|
+
model_response_delta=response_delta,
|
|
880
824
|
):
|
|
881
825
|
yield model_response
|
|
882
|
-
|
|
826
|
+
|
|
827
|
+
# Populate the assistant message
|
|
828
|
+
self._populate_assistant_message(assistant_message=assistant_message, provider_response=model_response)
|
|
883
829
|
|
|
884
830
|
async def aresponse_stream(
|
|
885
831
|
self,
|
|
@@ -890,7 +836,8 @@ class Model(ABC):
|
|
|
890
836
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
891
837
|
tool_call_limit: Optional[int] = None,
|
|
892
838
|
stream_model_response: bool = True,
|
|
893
|
-
|
|
839
|
+
run_response: Optional[RunOutput] = None,
|
|
840
|
+
) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
894
841
|
"""
|
|
895
842
|
Generate an asynchronous streaming response from the model.
|
|
896
843
|
"""
|
|
@@ -914,16 +861,17 @@ class Model(ABC):
|
|
|
914
861
|
response_format=response_format,
|
|
915
862
|
tools=tools,
|
|
916
863
|
tool_choice=tool_choice or self._tool_choice,
|
|
864
|
+
run_response=run_response,
|
|
917
865
|
):
|
|
918
866
|
yield response
|
|
919
867
|
|
|
920
868
|
# Populate assistant message from stream data
|
|
921
869
|
if stream_data.response_content:
|
|
922
870
|
assistant_message.content = stream_data.response_content
|
|
923
|
-
if stream_data.
|
|
924
|
-
assistant_message.
|
|
925
|
-
if stream_data.
|
|
926
|
-
assistant_message.
|
|
871
|
+
if stream_data.response_reasoning_content:
|
|
872
|
+
assistant_message.reasoning_content = stream_data.response_reasoning_content
|
|
873
|
+
if stream_data.response_redacted_reasoning_content:
|
|
874
|
+
assistant_message.redacted_reasoning_content = stream_data.response_redacted_reasoning_content
|
|
927
875
|
if stream_data.response_provider_data:
|
|
928
876
|
assistant_message.provider_data = stream_data.response_provider_data
|
|
929
877
|
if stream_data.response_audio:
|
|
@@ -940,6 +888,7 @@ class Model(ABC):
|
|
|
940
888
|
response_format=response_format,
|
|
941
889
|
tools=tools,
|
|
942
890
|
tool_choice=tool_choice or self._tool_choice,
|
|
891
|
+
run_response=run_response,
|
|
943
892
|
)
|
|
944
893
|
yield model_response
|
|
945
894
|
|
|
@@ -975,6 +924,10 @@ class Model(ABC):
|
|
|
975
924
|
else:
|
|
976
925
|
self.format_function_call_results(messages=messages, function_call_results=function_call_results)
|
|
977
926
|
|
|
927
|
+
# Handle function call media
|
|
928
|
+
if any(msg.images or msg.videos or msg.audio for msg in function_call_results):
|
|
929
|
+
self._handle_function_call_media(messages=messages, function_call_results=function_call_results)
|
|
930
|
+
|
|
978
931
|
for function_call_result in function_call_results:
|
|
979
932
|
function_call_result.log(metrics=True)
|
|
980
933
|
|
|
@@ -1006,11 +959,6 @@ class Model(ABC):
|
|
|
1006
959
|
self, stream_data: MessageData, assistant_message: Message, model_response_delta: ModelResponse
|
|
1007
960
|
) -> Iterator[ModelResponse]:
|
|
1008
961
|
"""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
962
|
# Add role to assistant message
|
|
1015
963
|
if model_response_delta.role is not None:
|
|
1016
964
|
assistant_message.role = model_response_delta.role
|
|
@@ -1021,16 +969,12 @@ class Model(ABC):
|
|
|
1021
969
|
stream_data.response_content += model_response_delta.content
|
|
1022
970
|
should_yield = True
|
|
1023
971
|
|
|
1024
|
-
if model_response_delta.thinking is not None:
|
|
1025
|
-
stream_data.response_thinking += model_response_delta.thinking
|
|
1026
|
-
should_yield = True
|
|
1027
|
-
|
|
1028
972
|
if model_response_delta.reasoning_content is not None:
|
|
1029
|
-
stream_data.
|
|
973
|
+
stream_data.response_reasoning_content += model_response_delta.reasoning_content
|
|
1030
974
|
should_yield = True
|
|
1031
975
|
|
|
1032
|
-
if model_response_delta.
|
|
1033
|
-
stream_data.
|
|
976
|
+
if model_response_delta.redacted_reasoning_content is not None:
|
|
977
|
+
stream_data.response_redacted_reasoning_content += model_response_delta.redacted_reasoning_content
|
|
1034
978
|
should_yield = True
|
|
1035
979
|
|
|
1036
980
|
if model_response_delta.citations is not None:
|
|
@@ -1049,40 +993,45 @@ class Model(ABC):
|
|
|
1049
993
|
stream_data.response_tool_calls.extend(model_response_delta.tool_calls)
|
|
1050
994
|
should_yield = True
|
|
1051
995
|
|
|
1052
|
-
if model_response_delta.audio is not None:
|
|
996
|
+
if model_response_delta.audio is not None and isinstance(model_response_delta.audio, AudioResponse):
|
|
1053
997
|
if stream_data.response_audio is None:
|
|
1054
998
|
stream_data.response_audio = AudioResponse(id=str(uuid4()), content="", transcript="")
|
|
1055
999
|
|
|
1000
|
+
from typing import cast
|
|
1001
|
+
|
|
1002
|
+
audio_response = cast(AudioResponse, model_response_delta.audio)
|
|
1003
|
+
|
|
1056
1004
|
# 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 =
|
|
1005
|
+
if audio_response.id is not None:
|
|
1006
|
+
stream_data.response_audio.id = audio_response.id # type: ignore
|
|
1007
|
+
if audio_response.content is not None:
|
|
1008
|
+
stream_data.response_audio.content += audio_response.content # type: ignore
|
|
1009
|
+
if audio_response.transcript is not None:
|
|
1010
|
+
stream_data.response_audio.transcript += audio_response.transcript # type: ignore
|
|
1011
|
+
if audio_response.expires_at is not None:
|
|
1012
|
+
stream_data.response_audio.expires_at = audio_response.expires_at
|
|
1013
|
+
if audio_response.mime_type is not None:
|
|
1014
|
+
stream_data.response_audio.mime_type = audio_response.mime_type
|
|
1015
|
+
stream_data.response_audio.sample_rate = audio_response.sample_rate
|
|
1016
|
+
stream_data.response_audio.channels = audio_response.channels
|
|
1069
1017
|
|
|
1070
1018
|
should_yield = True
|
|
1071
1019
|
|
|
1072
|
-
if model_response_delta.
|
|
1020
|
+
if model_response_delta.images:
|
|
1073
1021
|
if stream_data.response_image is None:
|
|
1074
|
-
stream_data.response_image = model_response_delta.
|
|
1022
|
+
stream_data.response_image = model_response_delta.images[-1]
|
|
1023
|
+
should_yield = True
|
|
1024
|
+
|
|
1025
|
+
if model_response_delta.videos:
|
|
1026
|
+
if stream_data.response_video is None:
|
|
1027
|
+
stream_data.response_video = model_response_delta.videos[-1]
|
|
1028
|
+
should_yield = True
|
|
1075
1029
|
|
|
1076
1030
|
if model_response_delta.extra is not None:
|
|
1077
1031
|
if stream_data.extra is None:
|
|
1078
1032
|
stream_data.extra = {}
|
|
1079
1033
|
stream_data.extra.update(model_response_delta.extra)
|
|
1080
1034
|
|
|
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
1035
|
if should_yield:
|
|
1087
1036
|
yield model_response_delta
|
|
1088
1037
|
|
|
@@ -1142,11 +1091,55 @@ class Model(ABC):
|
|
|
1142
1091
|
success: bool,
|
|
1143
1092
|
output: Optional[Union[List[Any], str]] = None,
|
|
1144
1093
|
timer: Optional[Timer] = None,
|
|
1094
|
+
function_execution_result: Optional[FunctionExecutionResult] = None,
|
|
1145
1095
|
) -> Message:
|
|
1146
1096
|
"""Create a function call result message."""
|
|
1147
1097
|
kwargs = {}
|
|
1148
1098
|
if timer is not None:
|
|
1149
|
-
kwargs["metrics"] =
|
|
1099
|
+
kwargs["metrics"] = Metrics(duration=timer.elapsed)
|
|
1100
|
+
|
|
1101
|
+
# Include media artifacts from function execution result in the tool message
|
|
1102
|
+
images = None
|
|
1103
|
+
videos = None
|
|
1104
|
+
audios = None
|
|
1105
|
+
|
|
1106
|
+
if success and function_execution_result:
|
|
1107
|
+
# Convert ImageArtifacts to Images for message compatibility
|
|
1108
|
+
if function_execution_result.images:
|
|
1109
|
+
from agno.media import Image
|
|
1110
|
+
|
|
1111
|
+
images = []
|
|
1112
|
+
for img_artifact in function_execution_result.images:
|
|
1113
|
+
if img_artifact.url:
|
|
1114
|
+
images.append(Image(url=img_artifact.url))
|
|
1115
|
+
elif img_artifact.content:
|
|
1116
|
+
images.append(Image(content=img_artifact.content))
|
|
1117
|
+
|
|
1118
|
+
# Convert VideoArtifacts to Videos for message compatibility
|
|
1119
|
+
if function_execution_result.videos:
|
|
1120
|
+
from agno.media import Video
|
|
1121
|
+
|
|
1122
|
+
videos = []
|
|
1123
|
+
for vid_artifact in function_execution_result.videos:
|
|
1124
|
+
if vid_artifact.url:
|
|
1125
|
+
videos.append(Video(url=vid_artifact.url))
|
|
1126
|
+
elif vid_artifact.content:
|
|
1127
|
+
videos.append(Video(content=vid_artifact.content))
|
|
1128
|
+
|
|
1129
|
+
# Convert AudioArtifacts to Audio for message compatibility
|
|
1130
|
+
if function_execution_result.audios:
|
|
1131
|
+
from agno.media import Audio
|
|
1132
|
+
|
|
1133
|
+
audios = []
|
|
1134
|
+
for aud_artifact in function_execution_result.audios:
|
|
1135
|
+
if aud_artifact.url:
|
|
1136
|
+
audios.append(Audio(url=aud_artifact.url))
|
|
1137
|
+
elif aud_artifact.base64_audio:
|
|
1138
|
+
import base64
|
|
1139
|
+
|
|
1140
|
+
audio_bytes = base64.b64decode(aud_artifact.base64_audio)
|
|
1141
|
+
audios.append(Audio(content=audio_bytes))
|
|
1142
|
+
|
|
1150
1143
|
return Message(
|
|
1151
1144
|
role=self.tool_message_role,
|
|
1152
1145
|
content=output if success else function_call.error,
|
|
@@ -1155,7 +1148,10 @@ class Model(ABC):
|
|
|
1155
1148
|
tool_args=function_call.arguments,
|
|
1156
1149
|
tool_call_error=not success,
|
|
1157
1150
|
stop_after_tool_call=function_call.function.stop_after_tool_call,
|
|
1158
|
-
|
|
1151
|
+
images=images,
|
|
1152
|
+
videos=videos,
|
|
1153
|
+
audio=audios,
|
|
1154
|
+
**kwargs, # type: ignore
|
|
1159
1155
|
)
|
|
1160
1156
|
|
|
1161
1157
|
def create_tool_call_limit_error_result(self, function_call: FunctionCall) -> Message:
|
|
@@ -1172,8 +1168,8 @@ class Model(ABC):
|
|
|
1172
1168
|
self,
|
|
1173
1169
|
function_call: FunctionCall,
|
|
1174
1170
|
function_call_results: List[Message],
|
|
1175
|
-
|
|
1176
|
-
) -> Iterator[Union[ModelResponse,
|
|
1171
|
+
additional_input: Optional[List[Message]] = None,
|
|
1172
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1177
1173
|
# Start function call
|
|
1178
1174
|
function_call_timer = Timer()
|
|
1179
1175
|
function_call_timer.start()
|
|
@@ -1196,7 +1192,7 @@ class Model(ABC):
|
|
|
1196
1192
|
function_execution_result = function_call.execute()
|
|
1197
1193
|
except AgentRunException as a_exc:
|
|
1198
1194
|
# Update additional messages from function call
|
|
1199
|
-
_handle_agent_exception(a_exc,
|
|
1195
|
+
_handle_agent_exception(a_exc, additional_input)
|
|
1200
1196
|
# Set function call success to False if an exception occurred
|
|
1201
1197
|
except Exception as e:
|
|
1202
1198
|
log_error(f"Error executing function {function_call.function.name}: {e}")
|
|
@@ -1210,14 +1206,14 @@ class Model(ABC):
|
|
|
1210
1206
|
# Process function call output
|
|
1211
1207
|
function_call_output: str = ""
|
|
1212
1208
|
|
|
1213
|
-
if isinstance(
|
|
1214
|
-
for item in
|
|
1209
|
+
if isinstance(function_execution_result.result, (GeneratorType, collections.abc.Iterator)):
|
|
1210
|
+
for item in function_execution_result.result:
|
|
1215
1211
|
# This function yields agent/team run events
|
|
1216
|
-
if isinstance(item, tuple(get_args(
|
|
1217
|
-
item, tuple(get_args(
|
|
1212
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1213
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1218
1214
|
):
|
|
1219
1215
|
# We only capture content events
|
|
1220
|
-
if isinstance(item,
|
|
1216
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1221
1217
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1222
1218
|
function_call_output += item.content.model_dump_json()
|
|
1223
1219
|
else:
|
|
@@ -1235,13 +1231,33 @@ class Model(ABC):
|
|
|
1235
1231
|
if function_call.function.show_result:
|
|
1236
1232
|
yield ModelResponse(content=str(item))
|
|
1237
1233
|
else:
|
|
1238
|
-
|
|
1234
|
+
from agno.tools.function import ToolResult
|
|
1235
|
+
|
|
1236
|
+
if isinstance(function_execution_result.result, ToolResult):
|
|
1237
|
+
# Extract content and media from ToolResult
|
|
1238
|
+
tool_result = function_execution_result.result
|
|
1239
|
+
function_call_output = tool_result.content
|
|
1240
|
+
|
|
1241
|
+
# Transfer media from ToolResult to FunctionExecutionResult
|
|
1242
|
+
if tool_result.images:
|
|
1243
|
+
function_execution_result.images = tool_result.images
|
|
1244
|
+
if tool_result.videos:
|
|
1245
|
+
function_execution_result.videos = tool_result.videos
|
|
1246
|
+
if tool_result.audios:
|
|
1247
|
+
function_execution_result.audios = tool_result.audios
|
|
1248
|
+
else:
|
|
1249
|
+
function_call_output = str(function_execution_result.result) if function_execution_result.result else ""
|
|
1250
|
+
|
|
1239
1251
|
if function_call.function.show_result:
|
|
1240
1252
|
yield ModelResponse(content=function_call_output)
|
|
1241
1253
|
|
|
1242
1254
|
# Create and yield function call result
|
|
1243
1255
|
function_call_result = self.create_function_call_result(
|
|
1244
|
-
function_call,
|
|
1256
|
+
function_call,
|
|
1257
|
+
success=function_call_success,
|
|
1258
|
+
output=function_call_output,
|
|
1259
|
+
timer=function_call_timer,
|
|
1260
|
+
function_execution_result=function_execution_result,
|
|
1245
1261
|
)
|
|
1246
1262
|
yield ModelResponse(
|
|
1247
1263
|
content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
|
|
@@ -1257,6 +1273,11 @@ class Model(ABC):
|
|
|
1257
1273
|
)
|
|
1258
1274
|
],
|
|
1259
1275
|
event=ModelResponseEvent.tool_call_completed.value,
|
|
1276
|
+
updated_session_state=function_execution_result.updated_session_state,
|
|
1277
|
+
# Add media artifacts from function execution
|
|
1278
|
+
images=function_execution_result.images,
|
|
1279
|
+
videos=function_execution_result.videos,
|
|
1280
|
+
audios=function_execution_result.audios,
|
|
1260
1281
|
)
|
|
1261
1282
|
|
|
1262
1283
|
# Add function call to function call results
|
|
@@ -1266,13 +1287,13 @@ class Model(ABC):
|
|
|
1266
1287
|
self,
|
|
1267
1288
|
function_calls: List[FunctionCall],
|
|
1268
1289
|
function_call_results: List[Message],
|
|
1269
|
-
|
|
1290
|
+
additional_input: Optional[List[Message]] = None,
|
|
1270
1291
|
current_function_call_count: int = 0,
|
|
1271
1292
|
function_call_limit: Optional[int] = None,
|
|
1272
|
-
) -> Iterator[Union[ModelResponse,
|
|
1293
|
+
) -> Iterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1273
1294
|
# Additional messages from function calls that will be added to the function call results
|
|
1274
|
-
if
|
|
1275
|
-
|
|
1295
|
+
if additional_input is None:
|
|
1296
|
+
additional_input = []
|
|
1276
1297
|
|
|
1277
1298
|
for fc in function_calls:
|
|
1278
1299
|
if function_call_limit is not None:
|
|
@@ -1358,17 +1379,17 @@ class Model(ABC):
|
|
|
1358
1379
|
continue
|
|
1359
1380
|
|
|
1360
1381
|
yield from self.run_function_call(
|
|
1361
|
-
function_call=fc, function_call_results=function_call_results,
|
|
1382
|
+
function_call=fc, function_call_results=function_call_results, additional_input=additional_input
|
|
1362
1383
|
)
|
|
1363
1384
|
|
|
1364
1385
|
# Add any additional messages at the end
|
|
1365
|
-
if
|
|
1366
|
-
function_call_results.extend(
|
|
1386
|
+
if additional_input:
|
|
1387
|
+
function_call_results.extend(additional_input)
|
|
1367
1388
|
|
|
1368
1389
|
async def arun_function_call(
|
|
1369
1390
|
self,
|
|
1370
1391
|
function_call: FunctionCall,
|
|
1371
|
-
) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall]:
|
|
1392
|
+
) -> Tuple[Union[bool, AgentRunException], Timer, FunctionCall, Optional[Dict[str, Any]]]:
|
|
1372
1393
|
"""Run a single function call and return its success status, timer, and the FunctionCall object."""
|
|
1373
1394
|
from inspect import isasyncgenfunction, iscoroutine, iscoroutinefunction
|
|
1374
1395
|
|
|
@@ -1402,20 +1423,20 @@ class Model(ABC):
|
|
|
1402
1423
|
raise e
|
|
1403
1424
|
|
|
1404
1425
|
function_call_timer.stop()
|
|
1405
|
-
return success, function_call_timer, function_call
|
|
1426
|
+
return success, function_call_timer, function_call, result.updated_session_state
|
|
1406
1427
|
|
|
1407
1428
|
async def arun_function_calls(
|
|
1408
1429
|
self,
|
|
1409
1430
|
function_calls: List[FunctionCall],
|
|
1410
1431
|
function_call_results: List[Message],
|
|
1411
|
-
|
|
1432
|
+
additional_input: Optional[List[Message]] = None,
|
|
1412
1433
|
current_function_call_count: int = 0,
|
|
1413
1434
|
function_call_limit: Optional[int] = None,
|
|
1414
1435
|
skip_pause_check: bool = False,
|
|
1415
|
-
) -> AsyncIterator[Union[ModelResponse,
|
|
1436
|
+
) -> AsyncIterator[Union[ModelResponse, RunOutputEvent, TeamRunOutputEvent]]:
|
|
1416
1437
|
# Additional messages from function calls that will be added to the function call results
|
|
1417
|
-
if
|
|
1418
|
-
|
|
1438
|
+
if additional_input is None:
|
|
1439
|
+
additional_input = []
|
|
1419
1440
|
|
|
1420
1441
|
function_calls_to_run = []
|
|
1421
1442
|
for fc in function_calls:
|
|
@@ -1548,33 +1569,33 @@ class Model(ABC):
|
|
|
1548
1569
|
raise result
|
|
1549
1570
|
|
|
1550
1571
|
# Unpack result
|
|
1551
|
-
function_call_success, function_call_timer,
|
|
1572
|
+
function_call_success, function_call_timer, function_call, updated_session_state = result
|
|
1552
1573
|
|
|
1553
1574
|
# Handle AgentRunException
|
|
1554
1575
|
if isinstance(function_call_success, AgentRunException):
|
|
1555
1576
|
a_exc = function_call_success
|
|
1556
1577
|
# Update additional messages from function call
|
|
1557
|
-
_handle_agent_exception(a_exc,
|
|
1578
|
+
_handle_agent_exception(a_exc, additional_input)
|
|
1558
1579
|
# Set function call success to False if an exception occurred
|
|
1559
1580
|
function_call_success = False
|
|
1560
1581
|
|
|
1561
1582
|
# Process function call output
|
|
1562
1583
|
function_call_output: str = ""
|
|
1563
|
-
if isinstance(
|
|
1564
|
-
for item in
|
|
1584
|
+
if isinstance(function_call.result, (GeneratorType, collections.abc.Iterator)):
|
|
1585
|
+
for item in function_call.result:
|
|
1565
1586
|
# This function yields agent/team run events
|
|
1566
|
-
if isinstance(item, tuple(get_args(
|
|
1567
|
-
item, tuple(get_args(
|
|
1587
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1588
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1568
1589
|
):
|
|
1569
1590
|
# We only capture content events
|
|
1570
|
-
if isinstance(item,
|
|
1591
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1571
1592
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1572
1593
|
function_call_output += item.content.model_dump_json()
|
|
1573
1594
|
else:
|
|
1574
1595
|
# Capture output
|
|
1575
1596
|
function_call_output += item.content or ""
|
|
1576
1597
|
|
|
1577
|
-
if
|
|
1598
|
+
if function_call.function.show_result:
|
|
1578
1599
|
yield ModelResponse(content=item.content)
|
|
1579
1600
|
continue
|
|
1580
1601
|
|
|
@@ -1582,23 +1603,23 @@ class Model(ABC):
|
|
|
1582
1603
|
yield item
|
|
1583
1604
|
else:
|
|
1584
1605
|
function_call_output += str(item)
|
|
1585
|
-
if
|
|
1606
|
+
if function_call.function.show_result:
|
|
1586
1607
|
yield ModelResponse(content=str(item))
|
|
1587
|
-
elif isinstance(
|
|
1588
|
-
async for item in
|
|
1608
|
+
elif isinstance(function_call.result, (AsyncGeneratorType, collections.abc.AsyncIterator)):
|
|
1609
|
+
async for item in function_call.result:
|
|
1589
1610
|
# This function yields agent/team run events
|
|
1590
|
-
if isinstance(item, tuple(get_args(
|
|
1591
|
-
item, tuple(get_args(
|
|
1611
|
+
if isinstance(item, tuple(get_args(RunOutputEvent))) or isinstance(
|
|
1612
|
+
item, tuple(get_args(TeamRunOutputEvent))
|
|
1592
1613
|
):
|
|
1593
1614
|
# We only capture content events
|
|
1594
|
-
if isinstance(item,
|
|
1615
|
+
if isinstance(item, RunContentEvent) or isinstance(item, TeamRunContentEvent):
|
|
1595
1616
|
if item.content is not None and isinstance(item.content, BaseModel):
|
|
1596
1617
|
function_call_output += item.content.model_dump_json()
|
|
1597
1618
|
else:
|
|
1598
1619
|
# Capture output
|
|
1599
1620
|
function_call_output += item.content or ""
|
|
1600
1621
|
|
|
1601
|
-
if
|
|
1622
|
+
if function_call.function.show_result:
|
|
1602
1623
|
yield ModelResponse(content=item.content)
|
|
1603
1624
|
continue
|
|
1604
1625
|
|
|
@@ -1606,19 +1627,19 @@ class Model(ABC):
|
|
|
1606
1627
|
yield item
|
|
1607
1628
|
else:
|
|
1608
1629
|
function_call_output += str(item)
|
|
1609
|
-
if
|
|
1630
|
+
if function_call.function.show_result:
|
|
1610
1631
|
yield ModelResponse(content=str(item))
|
|
1611
1632
|
else:
|
|
1612
|
-
function_call_output = str(
|
|
1613
|
-
if
|
|
1633
|
+
function_call_output = str(function_call.result)
|
|
1634
|
+
if function_call.function.show_result:
|
|
1614
1635
|
yield ModelResponse(content=function_call_output)
|
|
1615
1636
|
|
|
1616
1637
|
# Create and yield function call result
|
|
1617
1638
|
function_call_result = self.create_function_call_result(
|
|
1618
|
-
|
|
1639
|
+
function_call, success=function_call_success, output=function_call_output, timer=function_call_timer
|
|
1619
1640
|
)
|
|
1620
1641
|
yield ModelResponse(
|
|
1621
|
-
content=f"{
|
|
1642
|
+
content=f"{function_call.get_call_str()} completed in {function_call_timer.elapsed:.4f}s.",
|
|
1622
1643
|
tool_executions=[
|
|
1623
1644
|
ToolExecution(
|
|
1624
1645
|
tool_call_id=function_call_result.tool_call_id,
|
|
@@ -1631,14 +1652,15 @@ class Model(ABC):
|
|
|
1631
1652
|
)
|
|
1632
1653
|
],
|
|
1633
1654
|
event=ModelResponseEvent.tool_call_completed.value,
|
|
1655
|
+
updated_session_state=updated_session_state,
|
|
1634
1656
|
)
|
|
1635
1657
|
|
|
1636
1658
|
# Add function call result to function call results
|
|
1637
1659
|
function_call_results.append(function_call_result)
|
|
1638
1660
|
|
|
1639
1661
|
# Add any additional messages at the end
|
|
1640
|
-
if
|
|
1641
|
-
function_call_results.extend(
|
|
1662
|
+
if additional_input:
|
|
1663
|
+
function_call_results.extend(additional_input)
|
|
1642
1664
|
|
|
1643
1665
|
def _prepare_function_calls(
|
|
1644
1666
|
self,
|
|
@@ -1669,6 +1691,44 @@ class Model(ABC):
|
|
|
1669
1691
|
if len(function_call_results) > 0:
|
|
1670
1692
|
messages.extend(function_call_results)
|
|
1671
1693
|
|
|
1694
|
+
def _handle_function_call_media(self, messages: List[Message], function_call_results: List[Message]) -> None:
|
|
1695
|
+
"""
|
|
1696
|
+
Handle media artifacts from function calls by adding follow-up user messages for generated media if needed.
|
|
1697
|
+
"""
|
|
1698
|
+
if not function_call_results:
|
|
1699
|
+
return
|
|
1700
|
+
|
|
1701
|
+
# Collect all media artifacts from function calls
|
|
1702
|
+
all_images: List[Image] = []
|
|
1703
|
+
all_videos: List[Video] = []
|
|
1704
|
+
all_audio: List[Audio] = []
|
|
1705
|
+
|
|
1706
|
+
for result_message in function_call_results:
|
|
1707
|
+
if result_message.images:
|
|
1708
|
+
all_images.extend(result_message.images)
|
|
1709
|
+
# Remove images from tool message to avoid errors on the LLMs
|
|
1710
|
+
result_message.images = None
|
|
1711
|
+
|
|
1712
|
+
if result_message.videos:
|
|
1713
|
+
all_videos.extend(result_message.videos)
|
|
1714
|
+
result_message.videos = None
|
|
1715
|
+
|
|
1716
|
+
if result_message.audio:
|
|
1717
|
+
all_audio.extend(result_message.audio)
|
|
1718
|
+
result_message.audio = None
|
|
1719
|
+
|
|
1720
|
+
# If we have media artifacts, add a follow-up "user" message instead of a "tool"
|
|
1721
|
+
# message with the media artifacts which throws error for some models
|
|
1722
|
+
if all_images or all_videos or all_audio:
|
|
1723
|
+
media_message = Message(
|
|
1724
|
+
role="user",
|
|
1725
|
+
content="Take note of the following content",
|
|
1726
|
+
images=all_images if all_images else None,
|
|
1727
|
+
videos=all_videos if all_videos else None,
|
|
1728
|
+
audio=all_audio if all_audio else None,
|
|
1729
|
+
)
|
|
1730
|
+
messages.append(media_message)
|
|
1731
|
+
|
|
1672
1732
|
def get_system_message_for_model(self, tools: Optional[List[Any]] = None) -> Optional[str]:
|
|
1673
1733
|
return self.system_prompt
|
|
1674
1734
|
|