agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
"""Migration utility to migrate your Agno tables from v1 to v2"""
|
|
2
|
+
|
|
3
|
+
import gc
|
|
4
|
+
import json
|
|
5
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import text
|
|
8
|
+
|
|
9
|
+
from agno.db.base import BaseDb
|
|
10
|
+
from agno.db.schemas.memory import UserMemory
|
|
11
|
+
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
12
|
+
from agno.utils.log import log_error, log_info, log_warning
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def convert_v1_metrics_to_v2(metrics_dict: Dict[str, Any]) -> Dict[str, Any]:
|
|
16
|
+
"""Convert v1 metrics dictionary to v2 format by mapping old field names to new ones."""
|
|
17
|
+
if not isinstance(metrics_dict, dict):
|
|
18
|
+
return metrics_dict
|
|
19
|
+
|
|
20
|
+
# Create a copy to avoid modifying the original
|
|
21
|
+
v2_metrics = metrics_dict.copy()
|
|
22
|
+
|
|
23
|
+
# Map v1 field names to v2 field names
|
|
24
|
+
field_mappings = {
|
|
25
|
+
"time": "duration",
|
|
26
|
+
"audio_tokens": "audio_total_tokens",
|
|
27
|
+
"input_audio_tokens": "audio_input_tokens",
|
|
28
|
+
"output_audio_tokens": "audio_output_tokens",
|
|
29
|
+
"cached_tokens": "cache_read_tokens",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Fields to remove (deprecated in v2)
|
|
33
|
+
deprecated_fields = ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "completion_tokens_details"]
|
|
34
|
+
|
|
35
|
+
# Apply field mappings
|
|
36
|
+
for old_field, new_field in field_mappings.items():
|
|
37
|
+
if old_field in v2_metrics:
|
|
38
|
+
v2_metrics[new_field] = v2_metrics.pop(old_field)
|
|
39
|
+
|
|
40
|
+
# Remove deprecated fields
|
|
41
|
+
for field in deprecated_fields:
|
|
42
|
+
v2_metrics.pop(field, None)
|
|
43
|
+
|
|
44
|
+
return v2_metrics
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def convert_any_metrics_in_data(data: Any) -> Any:
|
|
48
|
+
"""Recursively find and convert any metrics dictionaries and handle v1 to v2 field conversion."""
|
|
49
|
+
if isinstance(data, dict):
|
|
50
|
+
# First apply v1 to v2 field conversion (handles extra_data extraction, thinking/reasoning_content consolidation, etc.)
|
|
51
|
+
data = convert_v1_fields_to_v2(data)
|
|
52
|
+
|
|
53
|
+
# Check if this looks like a metrics dictionary
|
|
54
|
+
if _is_metrics_dict(data):
|
|
55
|
+
return convert_v1_metrics_to_v2(data)
|
|
56
|
+
|
|
57
|
+
# Otherwise, recursively process all values
|
|
58
|
+
converted_dict = {}
|
|
59
|
+
for key, value in data.items():
|
|
60
|
+
# Special handling for 'metrics' keys - always convert their values
|
|
61
|
+
if key == "metrics" and isinstance(value, dict):
|
|
62
|
+
converted_dict[key] = convert_v1_metrics_to_v2(value)
|
|
63
|
+
else:
|
|
64
|
+
converted_dict[key] = convert_any_metrics_in_data(value)
|
|
65
|
+
return converted_dict
|
|
66
|
+
|
|
67
|
+
elif isinstance(data, list):
|
|
68
|
+
return [convert_any_metrics_in_data(item) for item in data]
|
|
69
|
+
|
|
70
|
+
else:
|
|
71
|
+
# Not a dict or list, return as-is
|
|
72
|
+
return data
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _is_metrics_dict(data: Dict[str, Any]) -> bool:
|
|
76
|
+
"""Check if a dictionary looks like a metrics dictionary based on common field names."""
|
|
77
|
+
if not isinstance(data, dict):
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
# Common metrics field names (both v1 and v2)
|
|
81
|
+
metrics_indicators = {
|
|
82
|
+
"input_tokens",
|
|
83
|
+
"output_tokens",
|
|
84
|
+
"total_tokens",
|
|
85
|
+
"time",
|
|
86
|
+
"duration",
|
|
87
|
+
"audio_tokens",
|
|
88
|
+
"audio_total_tokens",
|
|
89
|
+
"audio_input_tokens",
|
|
90
|
+
"audio_output_tokens",
|
|
91
|
+
"cached_tokens",
|
|
92
|
+
"cache_read_tokens",
|
|
93
|
+
"cache_write_tokens",
|
|
94
|
+
"reasoning_tokens",
|
|
95
|
+
"prompt_tokens",
|
|
96
|
+
"completion_tokens",
|
|
97
|
+
"time_to_first_token",
|
|
98
|
+
"provider_metrics",
|
|
99
|
+
"additional_metrics",
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Deprecated v1 fields that are strong indicators this is a metrics dict
|
|
103
|
+
deprecated_v1_indicators = {"time", "audio_tokens", "cached_tokens", "prompt_tokens", "completion_tokens"}
|
|
104
|
+
|
|
105
|
+
# If we find any deprecated v1 field, it's definitely a metrics dict that needs conversion
|
|
106
|
+
if any(field in data for field in deprecated_v1_indicators):
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
# Otherwise, if the dict has at least 2 metrics-related fields, consider it a metrics dict
|
|
110
|
+
matching_fields = sum(1 for field in data.keys() if field in metrics_indicators)
|
|
111
|
+
return matching_fields >= 2
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def convert_session_data_comprehensively(session_data: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
|
|
115
|
+
"""Comprehensively convert session data from v1 to v2 format, including metrics conversion and field mapping."""
|
|
116
|
+
if not session_data:
|
|
117
|
+
return session_data
|
|
118
|
+
|
|
119
|
+
# Use the recursive converter to handle all v1 to v2 conversions (metrics, field mapping, extra_data extraction, etc.)
|
|
120
|
+
return convert_any_metrics_in_data(session_data)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def safe_get_runs_from_memory(memory_data: Any) -> Any:
|
|
124
|
+
"""Safely extract runs data from memory field, handling various data types."""
|
|
125
|
+
if memory_data is None:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
runs: Any = []
|
|
129
|
+
|
|
130
|
+
# If memory_data is a string, try to parse it as JSON
|
|
131
|
+
if isinstance(memory_data, str):
|
|
132
|
+
try:
|
|
133
|
+
memory_dict = json.loads(memory_data)
|
|
134
|
+
if isinstance(memory_dict, dict):
|
|
135
|
+
runs = memory_dict.get("runs")
|
|
136
|
+
except (json.JSONDecodeError, AttributeError):
|
|
137
|
+
# If JSON parsing fails, memory_data might just be a string value
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
# If memory_data is already a dict, access runs directly
|
|
141
|
+
elif isinstance(memory_data, dict):
|
|
142
|
+
runs = memory_data.get("runs")
|
|
143
|
+
|
|
144
|
+
for run in runs or []:
|
|
145
|
+
# Adjust fields mapping for Agent sessions
|
|
146
|
+
if run.get("agent_id") is not None:
|
|
147
|
+
if run.get("team_id") is not None:
|
|
148
|
+
run.pop("team_id")
|
|
149
|
+
if run.get("team_session_id") is not None:
|
|
150
|
+
run["session_id"] = run.pop("team_session_id")
|
|
151
|
+
if run.get("event"):
|
|
152
|
+
run["events"] = [run.pop("event")]
|
|
153
|
+
|
|
154
|
+
# Adjust fields mapping for Team sessions
|
|
155
|
+
if run.get("team_id") is not None:
|
|
156
|
+
if run.get("agent_id") is not None:
|
|
157
|
+
run.pop("agent_id")
|
|
158
|
+
if member_responses := run.get("member_responses"):
|
|
159
|
+
for response in member_responses:
|
|
160
|
+
if response.get("agent_id") is not None and response.get("team_id") is not None:
|
|
161
|
+
response.pop("team_id")
|
|
162
|
+
if response.get("agent_id") is not None and response.get("team_session_id") is not None:
|
|
163
|
+
response["session_id"] = response.pop("team_session_id")
|
|
164
|
+
run["member_responses"] = member_responses
|
|
165
|
+
|
|
166
|
+
return runs
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def convert_v1_media_to_v2(media_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
170
|
+
"""Convert v1 media objects to v2 format."""
|
|
171
|
+
if not isinstance(media_data, dict):
|
|
172
|
+
return media_data
|
|
173
|
+
|
|
174
|
+
# Create a copy to avoid modifying the original
|
|
175
|
+
v2_media = media_data.copy()
|
|
176
|
+
|
|
177
|
+
# Add id if missing (required in v2)
|
|
178
|
+
if "id" not in v2_media or v2_media["id"] is None:
|
|
179
|
+
from uuid import uuid4
|
|
180
|
+
|
|
181
|
+
v2_media["id"] = str(uuid4())
|
|
182
|
+
|
|
183
|
+
# Handle VideoArtifact → Video conversion
|
|
184
|
+
if "eta" in v2_media or "length" in v2_media:
|
|
185
|
+
# Convert length to duration if it's numeric
|
|
186
|
+
length = v2_media.pop("length", None)
|
|
187
|
+
if length and isinstance(length, (int, float)):
|
|
188
|
+
v2_media["duration"] = length
|
|
189
|
+
elif length and isinstance(length, str):
|
|
190
|
+
try:
|
|
191
|
+
v2_media["duration"] = float(length)
|
|
192
|
+
except ValueError:
|
|
193
|
+
pass # Keep as is if not convertible
|
|
194
|
+
|
|
195
|
+
# Handle AudioArtifact → Audio conversion
|
|
196
|
+
if "base64_audio" in v2_media:
|
|
197
|
+
# Map base64_audio to content
|
|
198
|
+
base64_audio = v2_media.pop("base64_audio", None)
|
|
199
|
+
if base64_audio:
|
|
200
|
+
v2_media["content"] = base64_audio
|
|
201
|
+
|
|
202
|
+
# Handle AudioResponse content conversion (base64 string to bytes if needed)
|
|
203
|
+
if "transcript" in v2_media and "content" in v2_media:
|
|
204
|
+
content = v2_media.get("content")
|
|
205
|
+
if content and isinstance(content, str):
|
|
206
|
+
# Try to decode base64 content to bytes for v2
|
|
207
|
+
try:
|
|
208
|
+
import base64
|
|
209
|
+
|
|
210
|
+
v2_media["content"] = base64.b64decode(content)
|
|
211
|
+
except Exception:
|
|
212
|
+
# If not valid base64, keep as string
|
|
213
|
+
pass
|
|
214
|
+
|
|
215
|
+
# Ensure format and mime_type are set appropriately
|
|
216
|
+
if "format" in v2_media and "mime_type" not in v2_media:
|
|
217
|
+
format_val = v2_media["format"]
|
|
218
|
+
if format_val:
|
|
219
|
+
# Set mime_type based on format for common types
|
|
220
|
+
mime_type_map = {
|
|
221
|
+
"mp4": "video/mp4",
|
|
222
|
+
"mov": "video/quicktime",
|
|
223
|
+
"avi": "video/x-msvideo",
|
|
224
|
+
"webm": "video/webm",
|
|
225
|
+
"mp3": "audio/mpeg",
|
|
226
|
+
"wav": "audio/wav",
|
|
227
|
+
"ogg": "audio/ogg",
|
|
228
|
+
"png": "image/png",
|
|
229
|
+
"jpg": "image/jpeg",
|
|
230
|
+
"jpeg": "image/jpeg",
|
|
231
|
+
"gif": "image/gif",
|
|
232
|
+
"webp": "image/webp",
|
|
233
|
+
}
|
|
234
|
+
if format_val.lower() in mime_type_map:
|
|
235
|
+
v2_media["mime_type"] = mime_type_map[format_val.lower()]
|
|
236
|
+
|
|
237
|
+
return v2_media
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def convert_v1_fields_to_v2(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
241
|
+
"""Convert v1 fields to v2 format with proper field mapping and extraction."""
|
|
242
|
+
if not isinstance(data, dict):
|
|
243
|
+
return data
|
|
244
|
+
|
|
245
|
+
# Create a copy to avoid modifying the original
|
|
246
|
+
v2_data = data.copy()
|
|
247
|
+
|
|
248
|
+
# Fields that should be completely ignored/removed in v2
|
|
249
|
+
deprecated_fields = {
|
|
250
|
+
"team_session_id", # RunOutput v1 field, removed in v2
|
|
251
|
+
"formatted_tool_calls", # RunOutput v1 field, removed in v2
|
|
252
|
+
"event", # Remove event field
|
|
253
|
+
"events", # Remove events field
|
|
254
|
+
# Add other deprecated fields here as needed
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# Extract and map fields from extra_data before removing it
|
|
258
|
+
extra_data = v2_data.get("extra_data")
|
|
259
|
+
if extra_data and isinstance(extra_data, dict):
|
|
260
|
+
# Map extra_data fields to their v2 locations
|
|
261
|
+
if "add_messages" in extra_data:
|
|
262
|
+
v2_data["additional_input"] = extra_data["add_messages"]
|
|
263
|
+
if "references" in extra_data:
|
|
264
|
+
v2_data["references"] = extra_data["references"]
|
|
265
|
+
if "reasoning_steps" in extra_data:
|
|
266
|
+
v2_data["reasoning_steps"] = extra_data["reasoning_steps"]
|
|
267
|
+
if "reasoning_content" in extra_data:
|
|
268
|
+
# reasoning_content from extra_data also goes to reasoning_content
|
|
269
|
+
v2_data["reasoning_content"] = extra_data["reasoning_content"]
|
|
270
|
+
if "reasoning_messages" in extra_data:
|
|
271
|
+
v2_data["reasoning_messages"] = extra_data["reasoning_messages"]
|
|
272
|
+
|
|
273
|
+
# Handle thinking and reasoning_content consolidation
|
|
274
|
+
# Both thinking and reasoning_content from v1 should become reasoning_content in v2
|
|
275
|
+
thinking = v2_data.get("thinking")
|
|
276
|
+
reasoning_content = v2_data.get("reasoning_content")
|
|
277
|
+
|
|
278
|
+
# Consolidate thinking and reasoning_content into reasoning_content
|
|
279
|
+
if thinking and reasoning_content:
|
|
280
|
+
# Both exist, combine them (thinking first, then reasoning_content)
|
|
281
|
+
v2_data["reasoning_content"] = f"{thinking}\n{reasoning_content}"
|
|
282
|
+
elif thinking and not reasoning_content:
|
|
283
|
+
# Only thinking exists, move it to reasoning_content
|
|
284
|
+
v2_data["reasoning_content"] = thinking
|
|
285
|
+
# If only reasoning_content exists, keep it as is
|
|
286
|
+
|
|
287
|
+
# Remove thinking field since it's now consolidated into reasoning_content
|
|
288
|
+
if "thinking" in v2_data:
|
|
289
|
+
del v2_data["thinking"]
|
|
290
|
+
|
|
291
|
+
# Handle media object conversions
|
|
292
|
+
media_fields = ["images", "videos", "audio", "response_audio"]
|
|
293
|
+
for field in media_fields:
|
|
294
|
+
if field in v2_data and v2_data[field]:
|
|
295
|
+
if isinstance(v2_data[field], list):
|
|
296
|
+
# Handle list of media objects
|
|
297
|
+
v2_data[field] = [
|
|
298
|
+
convert_v1_media_to_v2(item) if isinstance(item, dict) else item for item in v2_data[field]
|
|
299
|
+
]
|
|
300
|
+
elif isinstance(v2_data[field], dict):
|
|
301
|
+
# Handle single media object
|
|
302
|
+
v2_data[field] = convert_v1_media_to_v2(v2_data[field])
|
|
303
|
+
|
|
304
|
+
# Remove extra_data after extraction
|
|
305
|
+
if "extra_data" in v2_data:
|
|
306
|
+
del v2_data["extra_data"]
|
|
307
|
+
|
|
308
|
+
# Remove other deprecated fields
|
|
309
|
+
for field in deprecated_fields:
|
|
310
|
+
v2_data.pop(field, None)
|
|
311
|
+
|
|
312
|
+
return v2_data
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def migrate(
|
|
316
|
+
db: BaseDb,
|
|
317
|
+
v1_db_schema: str,
|
|
318
|
+
agent_sessions_table_name: Optional[str] = None,
|
|
319
|
+
team_sessions_table_name: Optional[str] = None,
|
|
320
|
+
workflow_sessions_table_name: Optional[str] = None,
|
|
321
|
+
memories_table_name: Optional[str] = None,
|
|
322
|
+
batch_size: int = 5000,
|
|
323
|
+
):
|
|
324
|
+
"""Given a database connection and table/collection names, parse and migrate the content to corresponding v2 tables/collections.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
db: The database to migrate (PostgresDb, MySQLDb, SqliteDb, or MongoDb)
|
|
328
|
+
v1_db_schema: The schema of the v1 tables (leave empty for SQLite and MongoDB)
|
|
329
|
+
agent_sessions_table_name: The name of the agent sessions table/collection. If not provided, agent sessions will not be migrated.
|
|
330
|
+
team_sessions_table_name: The name of the team sessions table/collection. If not provided, team sessions will not be migrated.
|
|
331
|
+
workflow_sessions_table_name: The name of the workflow sessions table/collection. If not provided, workflow sessions will not be migrated.
|
|
332
|
+
memories_table_name: The name of the memories table/collection. If not provided, memories will not be migrated.
|
|
333
|
+
batch_size: Number of records to process in each batch (default: 5000)
|
|
334
|
+
"""
|
|
335
|
+
if agent_sessions_table_name:
|
|
336
|
+
migrate_table_in_batches(
|
|
337
|
+
db=db,
|
|
338
|
+
v1_db_schema=v1_db_schema,
|
|
339
|
+
v1_table_name=agent_sessions_table_name,
|
|
340
|
+
v1_table_type="agent_sessions",
|
|
341
|
+
batch_size=batch_size,
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if team_sessions_table_name:
|
|
345
|
+
migrate_table_in_batches(
|
|
346
|
+
db=db,
|
|
347
|
+
v1_db_schema=v1_db_schema,
|
|
348
|
+
v1_table_name=team_sessions_table_name,
|
|
349
|
+
v1_table_type="team_sessions",
|
|
350
|
+
batch_size=batch_size,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
if workflow_sessions_table_name:
|
|
354
|
+
migrate_table_in_batches(
|
|
355
|
+
db=db,
|
|
356
|
+
v1_db_schema=v1_db_schema,
|
|
357
|
+
v1_table_name=workflow_sessions_table_name,
|
|
358
|
+
v1_table_type="workflow_sessions",
|
|
359
|
+
batch_size=batch_size,
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
if memories_table_name:
|
|
363
|
+
migrate_table_in_batches(
|
|
364
|
+
db=db,
|
|
365
|
+
v1_db_schema=v1_db_schema,
|
|
366
|
+
v1_table_name=memories_table_name,
|
|
367
|
+
v1_table_type="memories",
|
|
368
|
+
batch_size=batch_size,
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def migrate_table_in_batches(
|
|
373
|
+
db: BaseDb,
|
|
374
|
+
v1_db_schema: str,
|
|
375
|
+
v1_table_name: str,
|
|
376
|
+
v1_table_type: str,
|
|
377
|
+
batch_size: int = 5000,
|
|
378
|
+
):
|
|
379
|
+
log_info(f"Starting migration of table {v1_table_name} (type: {v1_table_type}) with batch size {batch_size}")
|
|
380
|
+
|
|
381
|
+
total_migrated = 0
|
|
382
|
+
batch_count = 0
|
|
383
|
+
|
|
384
|
+
for batch_content in get_table_content_in_batches(db, v1_db_schema, v1_table_name, batch_size):
|
|
385
|
+
batch_count += 1
|
|
386
|
+
batch_size_actual = len(batch_content)
|
|
387
|
+
log_info(f"Processing batch {batch_count} with {batch_size_actual} records from table {v1_table_name}")
|
|
388
|
+
|
|
389
|
+
# Parse the content into the new format
|
|
390
|
+
memories: List[UserMemory] = []
|
|
391
|
+
sessions: Union[List[AgentSession], List[TeamSession], List[WorkflowSession]] = []
|
|
392
|
+
|
|
393
|
+
if v1_table_type == "agent_sessions":
|
|
394
|
+
sessions = parse_agent_sessions(batch_content)
|
|
395
|
+
elif v1_table_type == "team_sessions":
|
|
396
|
+
sessions = parse_team_sessions(batch_content)
|
|
397
|
+
elif v1_table_type == "workflow_sessions":
|
|
398
|
+
sessions = parse_workflow_sessions(batch_content)
|
|
399
|
+
elif v1_table_type == "memories":
|
|
400
|
+
memories = parse_memories(batch_content)
|
|
401
|
+
else:
|
|
402
|
+
raise ValueError(f"Invalid table type: {v1_table_type}")
|
|
403
|
+
|
|
404
|
+
# Insert the batch into the new table
|
|
405
|
+
if v1_table_type in ["agent_sessions", "team_sessions", "workflow_sessions"]:
|
|
406
|
+
if sessions:
|
|
407
|
+
# Clear any existing scoped session state for SQL databases to prevent transaction conflicts
|
|
408
|
+
if hasattr(db, "Session"):
|
|
409
|
+
db.Session.remove() # type: ignore
|
|
410
|
+
|
|
411
|
+
db.upsert_sessions(sessions, preserve_updated_at=True) # type: ignore
|
|
412
|
+
total_migrated += len(sessions)
|
|
413
|
+
log_info(f"Bulk upserted {len(sessions)} sessions in batch {batch_count}")
|
|
414
|
+
|
|
415
|
+
elif v1_table_type == "memories":
|
|
416
|
+
if memories:
|
|
417
|
+
# Clear any existing scoped session state for SQL databases to prevent transaction conflicts
|
|
418
|
+
if hasattr(db, "Session"):
|
|
419
|
+
db.Session.remove() # type: ignore
|
|
420
|
+
|
|
421
|
+
db.upsert_memories(memories, preserve_updated_at=True)
|
|
422
|
+
total_migrated += len(memories)
|
|
423
|
+
log_info(f"Bulk upserted {len(memories)} memories in batch {batch_count}")
|
|
424
|
+
|
|
425
|
+
log_info(f"Completed batch {batch_count}: migrated {batch_size_actual} records")
|
|
426
|
+
|
|
427
|
+
# Explicit cleanup to free memory before next batch
|
|
428
|
+
del batch_content
|
|
429
|
+
if v1_table_type in ["agent_sessions", "team_sessions", "workflow_sessions"]:
|
|
430
|
+
del sessions
|
|
431
|
+
elif v1_table_type == "memories":
|
|
432
|
+
del memories
|
|
433
|
+
|
|
434
|
+
# Force garbage collection to return memory to OS
|
|
435
|
+
# This is necessary because Python's memory allocator retains memory after large operations
|
|
436
|
+
# See: https://github.com/sqlalchemy/sqlalchemy/issues/4616
|
|
437
|
+
gc.collect()
|
|
438
|
+
|
|
439
|
+
log_info(f"✅ Migration completed for table {v1_table_name}: {total_migrated} total records migrated")
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def get_table_content_in_batches(db: BaseDb, db_schema: str, table_name: str, batch_size: int = 5000):
|
|
443
|
+
"""Get table content in batches to avoid memory issues with large tables"""
|
|
444
|
+
try:
|
|
445
|
+
if type(db).__name__ == "MongoDb":
|
|
446
|
+
from agno.db.mongo.mongo import MongoDb
|
|
447
|
+
|
|
448
|
+
db = cast(MongoDb, db)
|
|
449
|
+
|
|
450
|
+
# MongoDB implementation with cursor and batching
|
|
451
|
+
collection = db.database[table_name]
|
|
452
|
+
cursor = collection.find({}).batch_size(batch_size)
|
|
453
|
+
|
|
454
|
+
batch = []
|
|
455
|
+
for doc in cursor:
|
|
456
|
+
# Convert ObjectId to string for compatibility
|
|
457
|
+
if "_id" in doc:
|
|
458
|
+
doc["_id"] = str(doc["_id"])
|
|
459
|
+
batch.append(doc)
|
|
460
|
+
|
|
461
|
+
if len(batch) >= batch_size:
|
|
462
|
+
yield batch
|
|
463
|
+
batch = []
|
|
464
|
+
|
|
465
|
+
# Yield remaining items
|
|
466
|
+
if batch:
|
|
467
|
+
yield batch
|
|
468
|
+
else:
|
|
469
|
+
# SQL database implementations (PostgresDb, MySQLDb, SqliteDb)
|
|
470
|
+
if type(db).__name__ == "PostgresDb":
|
|
471
|
+
from agno.db.postgres.postgres import PostgresDb
|
|
472
|
+
|
|
473
|
+
db = cast(PostgresDb, db)
|
|
474
|
+
|
|
475
|
+
elif type(db).__name__ == "MySQLDb":
|
|
476
|
+
from agno.db.mysql.mysql import MySQLDb
|
|
477
|
+
|
|
478
|
+
db = cast(MySQLDb, db)
|
|
479
|
+
|
|
480
|
+
elif type(db).__name__ == "SqliteDb":
|
|
481
|
+
from agno.db.sqlite.sqlite import SqliteDb
|
|
482
|
+
|
|
483
|
+
db = cast(SqliteDb, db)
|
|
484
|
+
|
|
485
|
+
else:
|
|
486
|
+
raise ValueError(f"Invalid database type: {type(db).__name__}")
|
|
487
|
+
|
|
488
|
+
offset = 0
|
|
489
|
+
while True:
|
|
490
|
+
# Create a new session for each batch to avoid transaction conflicts
|
|
491
|
+
with db.Session() as sess:
|
|
492
|
+
# Handle empty schema by omitting the schema prefix (needed for SQLite)
|
|
493
|
+
if db_schema and db_schema.strip():
|
|
494
|
+
sql_query = f"SELECT * FROM {db_schema}.{table_name} LIMIT {batch_size} OFFSET {offset}"
|
|
495
|
+
else:
|
|
496
|
+
sql_query = f"SELECT * FROM {table_name} LIMIT {batch_size} OFFSET {offset}"
|
|
497
|
+
|
|
498
|
+
result = sess.execute(text(sql_query))
|
|
499
|
+
batch = [row._asdict() for row in result]
|
|
500
|
+
|
|
501
|
+
if not batch:
|
|
502
|
+
break
|
|
503
|
+
|
|
504
|
+
yield batch
|
|
505
|
+
offset += batch_size
|
|
506
|
+
|
|
507
|
+
# If batch is smaller than batch_size, we've reached the end
|
|
508
|
+
if len(batch) < batch_size:
|
|
509
|
+
break
|
|
510
|
+
|
|
511
|
+
except Exception as e:
|
|
512
|
+
log_error(f"Error getting batched content from table/collection {table_name}: {e}")
|
|
513
|
+
return
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def get_all_table_content(db, db_schema: str, table_name: str) -> list[dict[str, Any]]:
|
|
517
|
+
"""Get all content from the given table/collection (legacy method kept for backward compatibility)
|
|
518
|
+
|
|
519
|
+
WARNING: This method loads all data into memory and should not be used for large tables.
|
|
520
|
+
Use get_table_content_in_batches() for large datasets.
|
|
521
|
+
"""
|
|
522
|
+
log_warning(
|
|
523
|
+
f"Loading entire table {table_name} into memory. Consider using get_table_content_in_batches() for large tables, or if you experience any complication."
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
all_content = []
|
|
527
|
+
for batch in get_table_content_in_batches(db, db_schema, table_name):
|
|
528
|
+
all_content.extend(batch)
|
|
529
|
+
return all_content
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
def parse_agent_sessions(v1_content: List[Dict[str, Any]]) -> List[AgentSession]:
|
|
533
|
+
"""Parse v1 Agent sessions into v2 Agent sessions and Memories"""
|
|
534
|
+
sessions_v2 = []
|
|
535
|
+
|
|
536
|
+
for item in v1_content:
|
|
537
|
+
session = {
|
|
538
|
+
"agent_id": item.get("agent_id"),
|
|
539
|
+
"agent_data": item.get("agent_data"),
|
|
540
|
+
"session_id": item.get("session_id"),
|
|
541
|
+
"user_id": item.get("user_id"),
|
|
542
|
+
"session_data": convert_session_data_comprehensively(item.get("session_data")),
|
|
543
|
+
"metadata": convert_any_metrics_in_data(item.get("extra_data")),
|
|
544
|
+
"runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
|
|
545
|
+
"created_at": item.get("created_at"),
|
|
546
|
+
"updated_at": item.get("updated_at"),
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
try:
|
|
550
|
+
agent_session = AgentSession.from_dict(session)
|
|
551
|
+
except Exception as e:
|
|
552
|
+
log_error(f"Error parsing agent session: {e}. This is the complete session that failed: {session}")
|
|
553
|
+
continue
|
|
554
|
+
|
|
555
|
+
if agent_session is not None:
|
|
556
|
+
sessions_v2.append(agent_session)
|
|
557
|
+
|
|
558
|
+
return sessions_v2
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def parse_team_sessions(v1_content: List[Dict[str, Any]]) -> List[TeamSession]:
|
|
562
|
+
"""Parse v1 Team sessions into v2 Team sessions and Memories"""
|
|
563
|
+
sessions_v2 = []
|
|
564
|
+
|
|
565
|
+
for item in v1_content:
|
|
566
|
+
session = {
|
|
567
|
+
"team_id": item.get("team_id"),
|
|
568
|
+
"team_data": item.get("team_data"),
|
|
569
|
+
"session_id": item.get("session_id"),
|
|
570
|
+
"user_id": item.get("user_id"),
|
|
571
|
+
"session_data": convert_session_data_comprehensively(item.get("session_data")),
|
|
572
|
+
"metadata": convert_any_metrics_in_data(item.get("extra_data")),
|
|
573
|
+
"runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
|
|
574
|
+
"created_at": item.get("created_at"),
|
|
575
|
+
"updated_at": item.get("updated_at"),
|
|
576
|
+
}
|
|
577
|
+
try:
|
|
578
|
+
team_session = TeamSession.from_dict(session)
|
|
579
|
+
except Exception as e:
|
|
580
|
+
log_error(f"Error parsing team session: {e}. This is the complete session that failed: {session}")
|
|
581
|
+
continue
|
|
582
|
+
|
|
583
|
+
if team_session is not None:
|
|
584
|
+
sessions_v2.append(team_session)
|
|
585
|
+
|
|
586
|
+
return sessions_v2
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
def parse_workflow_sessions(v1_content: List[Dict[str, Any]]) -> List[WorkflowSession]:
|
|
590
|
+
"""Parse v1 Workflow sessions into v2 Workflow sessions"""
|
|
591
|
+
sessions_v2 = []
|
|
592
|
+
|
|
593
|
+
for item in v1_content:
|
|
594
|
+
session = {
|
|
595
|
+
"workflow_id": item.get("workflow_id"),
|
|
596
|
+
"workflow_data": item.get("workflow_data"),
|
|
597
|
+
"session_id": item.get("session_id"),
|
|
598
|
+
"user_id": item.get("user_id"),
|
|
599
|
+
"session_data": convert_session_data_comprehensively(item.get("session_data")),
|
|
600
|
+
"metadata": convert_any_metrics_in_data(item.get("extra_data")),
|
|
601
|
+
"created_at": item.get("created_at"),
|
|
602
|
+
"updated_at": item.get("updated_at"),
|
|
603
|
+
# Workflow v2 specific fields
|
|
604
|
+
"workflow_name": item.get("workflow_name"),
|
|
605
|
+
"runs": convert_any_metrics_in_data(item.get("runs")),
|
|
606
|
+
}
|
|
607
|
+
try:
|
|
608
|
+
workflow_session = WorkflowSession.from_dict(session)
|
|
609
|
+
except Exception as e:
|
|
610
|
+
log_error(f"Error parsing workflow session: {e}. This is the complete session that failed: {session}")
|
|
611
|
+
continue
|
|
612
|
+
|
|
613
|
+
if workflow_session is not None:
|
|
614
|
+
sessions_v2.append(workflow_session)
|
|
615
|
+
|
|
616
|
+
return sessions_v2
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def parse_memories(v1_content: List[Dict[str, Any]]) -> List[UserMemory]:
|
|
620
|
+
"""Parse v1 Memories into v2 Memories"""
|
|
621
|
+
memories_v2 = []
|
|
622
|
+
|
|
623
|
+
for item in v1_content:
|
|
624
|
+
memory = {
|
|
625
|
+
"memory_id": item.get("memory_id"),
|
|
626
|
+
"memory": item.get("memory"),
|
|
627
|
+
"input": item.get("input"),
|
|
628
|
+
"updated_at": item.get("updated_at"),
|
|
629
|
+
"agent_id": item.get("agent_id"),
|
|
630
|
+
"team_id": item.get("team_id"),
|
|
631
|
+
"user_id": item.get("user_id"),
|
|
632
|
+
}
|
|
633
|
+
memories_v2.append(UserMemory.from_dict(memory))
|
|
634
|
+
|
|
635
|
+
return memories_v2
|