agno 1.8.0__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 +2781 -4126
- 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/media.py +2 -2
- 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/dashscope/dashscope.py +14 -5
- agno/models/google/gemini.py +123 -53
- 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 +135 -27
- agno/models/openai/responses.py +233 -115
- 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 +2967 -4243
- 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 +71 -18
- 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 +62 -62
- 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 +46 -62
- 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 +134 -0
- 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/location.py +2 -2
- 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 +356 -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 -698
- 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.0.dist-info/METADATA +0 -979
- agno-1.8.0.dist-info/RECORD +0 -565
- agno-1.8.0.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.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/storage/mysql.py
DELETED
|
@@ -1,685 +0,0 @@
|
|
|
1
|
-
import time
|
|
2
|
-
from typing import List, Literal, Optional
|
|
3
|
-
|
|
4
|
-
from agno.storage.base import Storage
|
|
5
|
-
from agno.storage.session import Session
|
|
6
|
-
from agno.storage.session.agent import AgentSession
|
|
7
|
-
from agno.storage.session.team import TeamSession
|
|
8
|
-
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
9
|
-
from agno.storage.session.workflow import WorkflowSession
|
|
10
|
-
from agno.utils.log import log_debug, log_info, log_warning, logger
|
|
11
|
-
|
|
12
|
-
try:
|
|
13
|
-
from sqlalchemy.dialects import mysql
|
|
14
|
-
from sqlalchemy.engine import Engine, create_engine
|
|
15
|
-
from sqlalchemy.inspection import inspect
|
|
16
|
-
from sqlalchemy.orm import scoped_session, sessionmaker
|
|
17
|
-
from sqlalchemy.schema import Column, MetaData, Table
|
|
18
|
-
from sqlalchemy.sql.expression import select, text
|
|
19
|
-
from sqlalchemy.types import JSON, BigInteger, String
|
|
20
|
-
except ImportError:
|
|
21
|
-
raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy pymysql`")
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class MySQLStorage(Storage):
|
|
25
|
-
def __init__(
|
|
26
|
-
self,
|
|
27
|
-
table_name: str,
|
|
28
|
-
schema: Optional[str] = "ai",
|
|
29
|
-
db_url: Optional[str] = None,
|
|
30
|
-
db_engine: Optional[Engine] = None,
|
|
31
|
-
schema_version: int = 1,
|
|
32
|
-
auto_upgrade_schema: bool = False,
|
|
33
|
-
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
34
|
-
):
|
|
35
|
-
"""
|
|
36
|
-
This class provides agent storage using a MySQL table.
|
|
37
|
-
|
|
38
|
-
The following order is used to determine the database connection:
|
|
39
|
-
1. Use the db_engine if provided
|
|
40
|
-
2. Use the db_url
|
|
41
|
-
3. Raise an error if neither is provided
|
|
42
|
-
|
|
43
|
-
Args:
|
|
44
|
-
table_name (str): Name of the table to store Agent sessions.
|
|
45
|
-
schema (Optional[str]): The schema to use for the table. Defaults to "ai".
|
|
46
|
-
db_url (Optional[str]): The database URL to connect to.
|
|
47
|
-
db_engine (Optional[Engine]): The SQLAlchemy database engine to use.
|
|
48
|
-
schema_version (int): Version of the schema. Defaults to 1.
|
|
49
|
-
auto_upgrade_schema (bool): Whether to automatically upgrade the schema.
|
|
50
|
-
mode (Optional[Literal["agent", "team", "workflow", "workflow_v2"]]): The mode of the storage.
|
|
51
|
-
Raises:
|
|
52
|
-
ValueError: If neither db_url nor db_engine is provided.
|
|
53
|
-
"""
|
|
54
|
-
super().__init__(mode)
|
|
55
|
-
_engine: Optional[Engine] = db_engine
|
|
56
|
-
if _engine is None and db_url is not None:
|
|
57
|
-
_engine = create_engine(db_url)
|
|
58
|
-
|
|
59
|
-
if _engine is None:
|
|
60
|
-
raise ValueError("Must provide either db_url or db_engine")
|
|
61
|
-
|
|
62
|
-
# Database attributes
|
|
63
|
-
self.table_name: str = table_name
|
|
64
|
-
self.schema: Optional[str] = schema
|
|
65
|
-
self.db_url: Optional[str] = db_url
|
|
66
|
-
self.db_engine: Engine = _engine
|
|
67
|
-
self.metadata: MetaData = MetaData(schema=self.schema)
|
|
68
|
-
self.inspector = inspect(self.db_engine)
|
|
69
|
-
|
|
70
|
-
# Table schema version
|
|
71
|
-
self.schema_version: int = schema_version
|
|
72
|
-
# Automatically upgrade schema if True
|
|
73
|
-
self.auto_upgrade_schema: bool = auto_upgrade_schema
|
|
74
|
-
self._schema_up_to_date: bool = False
|
|
75
|
-
|
|
76
|
-
# Database session
|
|
77
|
-
self.Session: scoped_session = scoped_session(sessionmaker(bind=self.db_engine))
|
|
78
|
-
# Database table for storage
|
|
79
|
-
self.table: Table = self.get_table()
|
|
80
|
-
log_debug(f"Created MySQLStorage: '{self.schema}.{self.table_name}'")
|
|
81
|
-
|
|
82
|
-
@property
|
|
83
|
-
def mode(self) -> Literal["agent", "team", "workflow", "workflow_v2"]:
|
|
84
|
-
"""Get the mode of the storage."""
|
|
85
|
-
return super().mode
|
|
86
|
-
|
|
87
|
-
@mode.setter
|
|
88
|
-
def mode(self, value: Optional[Literal["agent", "team", "workflow", "workflow_v2"]]) -> None:
|
|
89
|
-
"""Set the mode and refresh the table if mode changes."""
|
|
90
|
-
super(MySQLStorage, type(self)).mode.fset(self, value) # type: ignore
|
|
91
|
-
if value is not None:
|
|
92
|
-
self.table = self.get_table()
|
|
93
|
-
|
|
94
|
-
def get_table_v1(self) -> Table:
|
|
95
|
-
"""
|
|
96
|
-
Define the table schema for version 1.
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
Table: SQLAlchemy Table object representing the schema.
|
|
100
|
-
"""
|
|
101
|
-
# Common columns for both agent and workflow modes
|
|
102
|
-
common_columns = [
|
|
103
|
-
Column("session_id", String(255), primary_key=True),
|
|
104
|
-
Column("user_id", String(255), index=True),
|
|
105
|
-
Column("memory", JSON),
|
|
106
|
-
Column("session_data", JSON),
|
|
107
|
-
Column("extra_data", JSON),
|
|
108
|
-
Column("created_at", BigInteger, server_default=text("UNIX_TIMESTAMP()")),
|
|
109
|
-
Column("updated_at", BigInteger, server_onupdate=text("UNIX_TIMESTAMP()")),
|
|
110
|
-
]
|
|
111
|
-
|
|
112
|
-
# Mode-specific columns
|
|
113
|
-
specific_columns = []
|
|
114
|
-
if self.mode == "agent":
|
|
115
|
-
specific_columns = [
|
|
116
|
-
Column("agent_id", String(255), index=True),
|
|
117
|
-
Column("team_session_id", String(255), index=True, nullable=True),
|
|
118
|
-
Column("agent_data", JSON),
|
|
119
|
-
]
|
|
120
|
-
elif self.mode == "team":
|
|
121
|
-
specific_columns = [
|
|
122
|
-
Column("team_id", String(255), index=True),
|
|
123
|
-
Column("team_session_id", String(255), index=True, nullable=True),
|
|
124
|
-
Column("team_data", JSON),
|
|
125
|
-
]
|
|
126
|
-
elif self.mode == "workflow":
|
|
127
|
-
specific_columns = [
|
|
128
|
-
Column("workflow_id", String(255), index=True),
|
|
129
|
-
Column("workflow_data", JSON),
|
|
130
|
-
]
|
|
131
|
-
elif self.mode == "workflow_v2":
|
|
132
|
-
specific_columns = [
|
|
133
|
-
Column("workflow_id", String(255), index=True),
|
|
134
|
-
Column("workflow_name", String(255), index=True),
|
|
135
|
-
Column("workflow_data", JSON),
|
|
136
|
-
Column("runs", JSON),
|
|
137
|
-
]
|
|
138
|
-
# Create table with all columns
|
|
139
|
-
table = Table(
|
|
140
|
-
self.table_name,
|
|
141
|
-
self.metadata,
|
|
142
|
-
*common_columns,
|
|
143
|
-
*specific_columns,
|
|
144
|
-
extend_existing=True,
|
|
145
|
-
schema=self.schema, # type: ignore
|
|
146
|
-
)
|
|
147
|
-
|
|
148
|
-
return table
|
|
149
|
-
|
|
150
|
-
def get_table(self) -> Table:
|
|
151
|
-
"""
|
|
152
|
-
Get the table schema based on the schema version.
|
|
153
|
-
|
|
154
|
-
Returns:
|
|
155
|
-
Table: SQLAlchemy Table object for the current schema version.
|
|
156
|
-
|
|
157
|
-
Raises:
|
|
158
|
-
ValueError: If an unsupported schema version is specified.
|
|
159
|
-
"""
|
|
160
|
-
if self.schema_version == 1:
|
|
161
|
-
return self.get_table_v1()
|
|
162
|
-
else:
|
|
163
|
-
raise ValueError(f"Unsupported schema version: {self.schema_version}")
|
|
164
|
-
|
|
165
|
-
def table_exists(self) -> bool:
|
|
166
|
-
"""
|
|
167
|
-
Check if the table exists in the database.
|
|
168
|
-
|
|
169
|
-
Returns:
|
|
170
|
-
bool: True if the table exists, False otherwise.
|
|
171
|
-
"""
|
|
172
|
-
try:
|
|
173
|
-
# Use a direct SQL query to check if the table exists
|
|
174
|
-
with self.Session() as sess:
|
|
175
|
-
if self.schema is not None:
|
|
176
|
-
exists_query = text(
|
|
177
|
-
"SELECT 1 FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table"
|
|
178
|
-
)
|
|
179
|
-
exists = (
|
|
180
|
-
sess.execute(exists_query, {"schema": self.schema, "table": self.table_name}).scalar()
|
|
181
|
-
is not None
|
|
182
|
-
)
|
|
183
|
-
else:
|
|
184
|
-
exists_query = text("SELECT 1 FROM information_schema.tables WHERE table_name = :table")
|
|
185
|
-
exists = sess.execute(exists_query, {"table": self.table_name}).scalar() is not None
|
|
186
|
-
|
|
187
|
-
log_debug(f"Table '{self.table.fullname}' does{' not ' if not exists else ' '}exist")
|
|
188
|
-
return exists
|
|
189
|
-
|
|
190
|
-
except Exception as e:
|
|
191
|
-
logger.error(f"Error checking if table exists: {e}")
|
|
192
|
-
return False
|
|
193
|
-
|
|
194
|
-
def create(self) -> None:
|
|
195
|
-
"""
|
|
196
|
-
Create the table if it does not exist.
|
|
197
|
-
"""
|
|
198
|
-
self.table = self.get_table()
|
|
199
|
-
if not self.table_exists():
|
|
200
|
-
try:
|
|
201
|
-
with self.Session() as sess, sess.begin():
|
|
202
|
-
if self.schema is not None:
|
|
203
|
-
log_debug(f"Creating schema: {self.schema}")
|
|
204
|
-
sess.execute(text(f"CREATE SCHEMA IF NOT EXISTS `{self.schema}`;"))
|
|
205
|
-
|
|
206
|
-
log_debug(f"Creating table: {self.table_name}")
|
|
207
|
-
|
|
208
|
-
# First create the table without indexes
|
|
209
|
-
table_without_indexes = Table(
|
|
210
|
-
self.table_name,
|
|
211
|
-
MetaData(schema=self.schema),
|
|
212
|
-
*[c.copy() for c in self.table.columns],
|
|
213
|
-
schema=self.schema,
|
|
214
|
-
)
|
|
215
|
-
table_without_indexes.create(self.db_engine, checkfirst=True)
|
|
216
|
-
|
|
217
|
-
# Then create each index individually with error handling
|
|
218
|
-
for idx in self.table.indexes:
|
|
219
|
-
try:
|
|
220
|
-
idx_name = idx.name
|
|
221
|
-
log_debug(f"Creating index: {idx_name}")
|
|
222
|
-
|
|
223
|
-
# Check if index already exists in MySQL
|
|
224
|
-
with self.Session() as sess:
|
|
225
|
-
if self.schema:
|
|
226
|
-
exists_query = text(
|
|
227
|
-
"""SELECT 1 FROM information_schema.statistics
|
|
228
|
-
WHERE table_schema = :schema
|
|
229
|
-
AND table_name = :table
|
|
230
|
-
AND index_name = :index_name"""
|
|
231
|
-
)
|
|
232
|
-
exists = (
|
|
233
|
-
sess.execute(
|
|
234
|
-
exists_query,
|
|
235
|
-
{"schema": self.schema, "table": self.table_name, "index_name": idx_name},
|
|
236
|
-
).scalar()
|
|
237
|
-
is not None
|
|
238
|
-
)
|
|
239
|
-
else:
|
|
240
|
-
exists_query = text(
|
|
241
|
-
"""SELECT 1 FROM information_schema.statistics
|
|
242
|
-
WHERE table_name = :table
|
|
243
|
-
AND index_name = :index_name"""
|
|
244
|
-
)
|
|
245
|
-
exists = (
|
|
246
|
-
sess.execute(
|
|
247
|
-
exists_query, {"table": self.table_name, "index_name": idx_name}
|
|
248
|
-
).scalar()
|
|
249
|
-
is not None
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
if not exists:
|
|
253
|
-
idx.create(self.db_engine)
|
|
254
|
-
else:
|
|
255
|
-
log_debug(f"Index {idx_name} already exists, skipping creation")
|
|
256
|
-
|
|
257
|
-
except Exception as e:
|
|
258
|
-
# Log the error but continue with other indexes
|
|
259
|
-
logger.warning(f"Error creating index {idx.name}: {e}")
|
|
260
|
-
|
|
261
|
-
except Exception as e:
|
|
262
|
-
logger.error(f"Could not create table: '{self.table.fullname}': {e}")
|
|
263
|
-
raise
|
|
264
|
-
|
|
265
|
-
def read(self, session_id: str, user_id: Optional[str] = None) -> Optional[Session]:
|
|
266
|
-
"""
|
|
267
|
-
Read an Session from the database.
|
|
268
|
-
|
|
269
|
-
Args:
|
|
270
|
-
session_id (str): ID of the session to read.
|
|
271
|
-
user_id (Optional[str]): User ID to filter by. Defaults to None.
|
|
272
|
-
|
|
273
|
-
Returns:
|
|
274
|
-
Optional[Session]: Session object if found, None otherwise.
|
|
275
|
-
"""
|
|
276
|
-
try:
|
|
277
|
-
with self.Session() as sess:
|
|
278
|
-
stmt = select(self.table).where(self.table.c.session_id == session_id)
|
|
279
|
-
if user_id:
|
|
280
|
-
stmt = stmt.where(self.table.c.user_id == user_id)
|
|
281
|
-
result = sess.execute(stmt).fetchone()
|
|
282
|
-
if self.mode == "agent":
|
|
283
|
-
return AgentSession.from_dict(result._mapping) if result is not None else None
|
|
284
|
-
elif self.mode == "team":
|
|
285
|
-
return TeamSession.from_dict(result._mapping) if result is not None else None
|
|
286
|
-
elif self.mode == "workflow":
|
|
287
|
-
return WorkflowSession.from_dict(result._mapping) if result is not None else None
|
|
288
|
-
elif self.mode == "workflow_v2":
|
|
289
|
-
return WorkflowSessionV2.from_dict(result._mapping) if result is not None else None
|
|
290
|
-
except Exception as e:
|
|
291
|
-
if "doesn't exist" in str(e) or "doesn't exist" in str(e):
|
|
292
|
-
log_debug(f"Table does not exist: {self.table.name}")
|
|
293
|
-
log_debug("Creating table for future transactions")
|
|
294
|
-
self.create()
|
|
295
|
-
else:
|
|
296
|
-
log_debug(f"Exception reading from table: {e}")
|
|
297
|
-
return None
|
|
298
|
-
|
|
299
|
-
def get_all_session_ids(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[str]:
|
|
300
|
-
"""
|
|
301
|
-
Get all session IDs, optionally filtered by user_id and/or entity_id.
|
|
302
|
-
|
|
303
|
-
Args:
|
|
304
|
-
user_id (Optional[str]): The ID of the user to filter by.
|
|
305
|
-
entity_id (Optional[str]): The ID of the agent / workflow to filter by.
|
|
306
|
-
|
|
307
|
-
Returns:
|
|
308
|
-
List[str]: List of session IDs matching the criteria.
|
|
309
|
-
"""
|
|
310
|
-
try:
|
|
311
|
-
with self.Session() as sess, sess.begin():
|
|
312
|
-
# get all session_ids
|
|
313
|
-
stmt = select(self.table.c.session_id)
|
|
314
|
-
if user_id is not None:
|
|
315
|
-
stmt = stmt.where(self.table.c.user_id == user_id)
|
|
316
|
-
if entity_id is not None:
|
|
317
|
-
if self.mode == "agent":
|
|
318
|
-
stmt = stmt.where(self.table.c.agent_id == entity_id)
|
|
319
|
-
elif self.mode == "team":
|
|
320
|
-
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
321
|
-
elif self.mode == "workflow":
|
|
322
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
323
|
-
elif self.mode == "workflow_v2":
|
|
324
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
325
|
-
# order by created_at desc
|
|
326
|
-
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
327
|
-
# execute query
|
|
328
|
-
rows = sess.execute(stmt).fetchall()
|
|
329
|
-
return [row[0] for row in rows] if rows is not None else []
|
|
330
|
-
except Exception as e:
|
|
331
|
-
log_debug(f"Exception reading from table: {e}")
|
|
332
|
-
log_debug(f"Table does not exist: {self.table.name}")
|
|
333
|
-
log_debug("Creating table for future transactions")
|
|
334
|
-
self.create()
|
|
335
|
-
return []
|
|
336
|
-
|
|
337
|
-
def get_all_sessions(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[Session]:
|
|
338
|
-
"""
|
|
339
|
-
Get all sessions, optionally filtered by user_id and/or entity_id.
|
|
340
|
-
|
|
341
|
-
Args:
|
|
342
|
-
user_id (Optional[str]): The ID of the user to filter by.
|
|
343
|
-
entity_id (Optional[str]): The ID of the agent / workflow to filter by.
|
|
344
|
-
|
|
345
|
-
Returns:
|
|
346
|
-
List[Session]: List of Session objects matching the criteria.
|
|
347
|
-
"""
|
|
348
|
-
try:
|
|
349
|
-
with self.Session() as sess, sess.begin():
|
|
350
|
-
# get all sessions
|
|
351
|
-
stmt = select(self.table)
|
|
352
|
-
if user_id is not None:
|
|
353
|
-
stmt = stmt.where(self.table.c.user_id == user_id)
|
|
354
|
-
if entity_id is not None:
|
|
355
|
-
if self.mode == "agent":
|
|
356
|
-
stmt = stmt.where(self.table.c.agent_id == entity_id)
|
|
357
|
-
elif self.mode == "team":
|
|
358
|
-
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
359
|
-
elif self.mode == "workflow":
|
|
360
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
361
|
-
elif self.mode == "workflow_v2":
|
|
362
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
363
|
-
# order by created_at desc
|
|
364
|
-
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
365
|
-
# execute query
|
|
366
|
-
rows = sess.execute(stmt).fetchall()
|
|
367
|
-
if rows is not None:
|
|
368
|
-
if self.mode == "agent":
|
|
369
|
-
return [AgentSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
370
|
-
elif self.mode == "team":
|
|
371
|
-
return [TeamSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
372
|
-
elif self.mode == "workflow":
|
|
373
|
-
return [WorkflowSession.from_dict(row._mapping) for row in rows] # type: ignore
|
|
374
|
-
elif self.mode == "workflow_v2":
|
|
375
|
-
return [WorkflowSessionV2.from_dict(row._mapping) for row in rows] # type: ignore[misc]
|
|
376
|
-
else:
|
|
377
|
-
return []
|
|
378
|
-
except Exception as e:
|
|
379
|
-
log_debug(f"Exception reading from table: {e}")
|
|
380
|
-
log_debug(f"Table does not exist: {self.table.name}")
|
|
381
|
-
log_debug("Creating table for future transactions")
|
|
382
|
-
self.create()
|
|
383
|
-
return []
|
|
384
|
-
|
|
385
|
-
def get_recent_sessions(
|
|
386
|
-
self,
|
|
387
|
-
user_id: Optional[str] = None,
|
|
388
|
-
entity_id: Optional[str] = None,
|
|
389
|
-
limit: Optional[int] = 2,
|
|
390
|
-
) -> List[Session]:
|
|
391
|
-
"""Get the last N sessions, ordered by created_at descending.
|
|
392
|
-
|
|
393
|
-
Args:
|
|
394
|
-
num_history_sessions: Number of most recent sessions to return
|
|
395
|
-
user_id: Filter by user ID
|
|
396
|
-
entity_id: Filter by entity ID (agent_id, team_id, or workflow_id)
|
|
397
|
-
|
|
398
|
-
Returns:
|
|
399
|
-
List[Session]: List of most recent sessions
|
|
400
|
-
"""
|
|
401
|
-
try:
|
|
402
|
-
with self.Session() as sess, sess.begin():
|
|
403
|
-
# Build the base query
|
|
404
|
-
stmt = select(self.table)
|
|
405
|
-
|
|
406
|
-
# Add filters
|
|
407
|
-
if user_id is not None:
|
|
408
|
-
stmt = stmt.where(self.table.c.user_id == user_id)
|
|
409
|
-
if entity_id is not None:
|
|
410
|
-
if self.mode == "agent":
|
|
411
|
-
stmt = stmt.where(self.table.c.agent_id == entity_id)
|
|
412
|
-
elif self.mode == "team":
|
|
413
|
-
stmt = stmt.where(self.table.c.team_id == entity_id)
|
|
414
|
-
elif self.mode == "workflow":
|
|
415
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
416
|
-
elif self.mode == "workflow_v2":
|
|
417
|
-
stmt = stmt.where(self.table.c.workflow_id == entity_id)
|
|
418
|
-
# Order by created_at desc and limit results
|
|
419
|
-
stmt = stmt.order_by(self.table.c.created_at.desc())
|
|
420
|
-
if limit is not None:
|
|
421
|
-
stmt = stmt.limit(limit)
|
|
422
|
-
|
|
423
|
-
# Execute query
|
|
424
|
-
rows = sess.execute(stmt).fetchall()
|
|
425
|
-
if rows is not None:
|
|
426
|
-
sessions: List[Session] = []
|
|
427
|
-
for row in rows:
|
|
428
|
-
session: Optional[Session] = None
|
|
429
|
-
if self.mode == "agent":
|
|
430
|
-
session = AgentSession.from_dict(row._mapping) # type: ignore
|
|
431
|
-
elif self.mode == "team":
|
|
432
|
-
session = TeamSession.from_dict(row._mapping) # type: ignore
|
|
433
|
-
elif self.mode == "workflow":
|
|
434
|
-
session = WorkflowSession.from_dict(row._mapping) # type: ignore
|
|
435
|
-
elif self.mode == "workflow_v2":
|
|
436
|
-
session = WorkflowSessionV2.from_dict(row._mapping) # type: ignore
|
|
437
|
-
if session is not None:
|
|
438
|
-
sessions.append(session)
|
|
439
|
-
return sessions
|
|
440
|
-
return []
|
|
441
|
-
|
|
442
|
-
except Exception as e:
|
|
443
|
-
if "doesn't exist" in str(e) or "doesn't exist" in str(e):
|
|
444
|
-
log_debug(f"Table does not exist: {self.table.name}")
|
|
445
|
-
log_debug("Creating table for future transactions")
|
|
446
|
-
self.create()
|
|
447
|
-
else:
|
|
448
|
-
log_debug(f"Exception reading from table: {e}")
|
|
449
|
-
return []
|
|
450
|
-
|
|
451
|
-
def upgrade_schema(self) -> None:
|
|
452
|
-
"""
|
|
453
|
-
Upgrade the schema to the latest version.
|
|
454
|
-
Currently handles adding the team_session_id column for agent mode.
|
|
455
|
-
"""
|
|
456
|
-
if not self.auto_upgrade_schema:
|
|
457
|
-
log_debug("Auto schema upgrade disabled. Skipping upgrade.")
|
|
458
|
-
return
|
|
459
|
-
|
|
460
|
-
try:
|
|
461
|
-
if self.mode == "agent" and self.table_exists():
|
|
462
|
-
with self.Session() as sess:
|
|
463
|
-
# Check if team_session_id column exists
|
|
464
|
-
column_exists_query = text(
|
|
465
|
-
"""
|
|
466
|
-
SELECT 1 FROM information_schema.columns
|
|
467
|
-
WHERE table_schema = :schema AND table_name = :table
|
|
468
|
-
AND column_name = 'team_session_id'
|
|
469
|
-
"""
|
|
470
|
-
)
|
|
471
|
-
column_exists = (
|
|
472
|
-
sess.execute(column_exists_query, {"schema": self.schema, "table": self.table_name}).scalar()
|
|
473
|
-
is not None
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
if not column_exists:
|
|
477
|
-
log_info(f"Adding 'team_session_id' column to {self.schema}.{self.table_name}")
|
|
478
|
-
alter_table_query = text(
|
|
479
|
-
f"ALTER TABLE `{self.schema}`.`{self.table_name}` ADD COLUMN team_session_id VARCHAR(255)"
|
|
480
|
-
)
|
|
481
|
-
sess.execute(alter_table_query)
|
|
482
|
-
sess.commit()
|
|
483
|
-
self._schema_up_to_date = True
|
|
484
|
-
log_info("Schema upgrade completed successfully")
|
|
485
|
-
except Exception as e:
|
|
486
|
-
logger.error(f"Error during schema upgrade: {e}")
|
|
487
|
-
raise
|
|
488
|
-
|
|
489
|
-
def upsert(self, session: Session, create_and_retry: bool = True) -> Optional[Session]:
|
|
490
|
-
"""
|
|
491
|
-
Insert or update an Session in the database.
|
|
492
|
-
|
|
493
|
-
Args:
|
|
494
|
-
session (Session): The session data to upsert.
|
|
495
|
-
create_and_retry (bool): Retry upsert if table does not exist.
|
|
496
|
-
|
|
497
|
-
Returns:
|
|
498
|
-
Optional[Session]: The upserted Session, or None if operation failed.
|
|
499
|
-
"""
|
|
500
|
-
# Perform schema upgrade if auto_upgrade_schema is enabled
|
|
501
|
-
if self.auto_upgrade_schema and not self._schema_up_to_date:
|
|
502
|
-
self.upgrade_schema()
|
|
503
|
-
|
|
504
|
-
try:
|
|
505
|
-
with self.Session() as sess, sess.begin():
|
|
506
|
-
# Create an insert statement
|
|
507
|
-
if self.mode == "agent":
|
|
508
|
-
stmt = mysql.insert(self.table).values(
|
|
509
|
-
session_id=session.session_id,
|
|
510
|
-
agent_id=session.agent_id, # type: ignore
|
|
511
|
-
team_session_id=session.team_session_id, # type: ignore
|
|
512
|
-
user_id=session.user_id,
|
|
513
|
-
memory=getattr(session, "memory", None),
|
|
514
|
-
agent_data=session.agent_data, # type: ignore
|
|
515
|
-
session_data=session.session_data,
|
|
516
|
-
extra_data=session.extra_data,
|
|
517
|
-
)
|
|
518
|
-
# Define the upsert if the session_id already exists
|
|
519
|
-
# See: https://docs.sqlalchemy.org/en/20/dialects/mysql.html#insert-on-duplicate-key-update
|
|
520
|
-
stmt = stmt.on_duplicate_key_update(
|
|
521
|
-
agent_id=session.agent_id, # type: ignore
|
|
522
|
-
team_session_id=session.team_session_id, # type: ignore
|
|
523
|
-
user_id=session.user_id,
|
|
524
|
-
memory=getattr(session, "memory", None),
|
|
525
|
-
agent_data=session.agent_data, # type: ignore
|
|
526
|
-
session_data=session.session_data,
|
|
527
|
-
extra_data=session.extra_data,
|
|
528
|
-
updated_at=int(time.time()),
|
|
529
|
-
)
|
|
530
|
-
elif self.mode == "team":
|
|
531
|
-
stmt = mysql.insert(self.table).values(
|
|
532
|
-
session_id=session.session_id,
|
|
533
|
-
team_id=session.team_id, # type: ignore
|
|
534
|
-
user_id=session.user_id,
|
|
535
|
-
team_session_id=session.team_session_id, # type: ignore
|
|
536
|
-
memory=getattr(session, "memory", None),
|
|
537
|
-
team_data=session.team_data, # type: ignore
|
|
538
|
-
session_data=session.session_data,
|
|
539
|
-
extra_data=session.extra_data,
|
|
540
|
-
)
|
|
541
|
-
# Define the upsert if the session_id already exists
|
|
542
|
-
# See: https://docs.sqlalchemy.org/en/20/dialects/mysql.html#insert-on-duplicate-key-update
|
|
543
|
-
stmt = stmt.on_duplicate_key_update(
|
|
544
|
-
team_id=session.team_id, # type: ignore
|
|
545
|
-
user_id=session.user_id,
|
|
546
|
-
team_session_id=session.team_session_id, # type: ignore
|
|
547
|
-
memory=getattr(session, "memory", None),
|
|
548
|
-
team_data=session.team_data, # type: ignore
|
|
549
|
-
session_data=session.session_data,
|
|
550
|
-
extra_data=session.extra_data,
|
|
551
|
-
updated_at=int(time.time()),
|
|
552
|
-
)
|
|
553
|
-
elif self.mode == "workflow":
|
|
554
|
-
stmt = mysql.insert(self.table).values(
|
|
555
|
-
session_id=session.session_id,
|
|
556
|
-
workflow_id=session.workflow_id, # type: ignore
|
|
557
|
-
user_id=session.user_id,
|
|
558
|
-
memory=getattr(session, "memory", None),
|
|
559
|
-
workflow_data=session.workflow_data, # type: ignore
|
|
560
|
-
session_data=session.session_data,
|
|
561
|
-
extra_data=session.extra_data,
|
|
562
|
-
)
|
|
563
|
-
# Define the upsert if the session_id already exists
|
|
564
|
-
# See: https://docs.sqlalchemy.org/en/20/dialects/mysql.html#insert-on-duplicate-key-update
|
|
565
|
-
stmt = stmt.on_duplicate_key_update(
|
|
566
|
-
workflow_id=session.workflow_id, # type: ignore
|
|
567
|
-
user_id=session.user_id,
|
|
568
|
-
memory=getattr(session, "memory", None),
|
|
569
|
-
workflow_data=session.workflow_data, # type: ignore
|
|
570
|
-
session_data=session.session_data,
|
|
571
|
-
extra_data=session.extra_data,
|
|
572
|
-
updated_at=int(time.time()),
|
|
573
|
-
)
|
|
574
|
-
elif self.mode == "workflow_v2":
|
|
575
|
-
# Convert session to dict to ensure proper serialization
|
|
576
|
-
session_dict = session.to_dict()
|
|
577
|
-
|
|
578
|
-
stmt = mysql.insert(self.table).values(
|
|
579
|
-
session_id=session.session_id,
|
|
580
|
-
workflow_id=session.workflow_id, # type: ignore
|
|
581
|
-
workflow_name=session.workflow_name, # type: ignore
|
|
582
|
-
user_id=session.user_id,
|
|
583
|
-
runs=session_dict.get("runs"),
|
|
584
|
-
workflow_data=session.workflow_data, # type: ignore
|
|
585
|
-
session_data=session.session_data,
|
|
586
|
-
extra_data=session.extra_data,
|
|
587
|
-
)
|
|
588
|
-
# Define the upsert if the session_id already exists
|
|
589
|
-
# See: https://docs.sqlalchemy.org/en/20/dialects/mysql.html#insert-on-duplicate-key-update
|
|
590
|
-
stmt = stmt.on_duplicate_key_update(
|
|
591
|
-
workflow_id=session.workflow_id, # type: ignore
|
|
592
|
-
workflow_name=session.workflow_name, # type: ignore
|
|
593
|
-
user_id=session.user_id,
|
|
594
|
-
runs=session_dict.get("runs"), # type: ignore
|
|
595
|
-
workflow_data=session.workflow_data, # type: ignore
|
|
596
|
-
session_data=session.session_data,
|
|
597
|
-
extra_data=session.extra_data,
|
|
598
|
-
updated_at=int(time.time()),
|
|
599
|
-
)
|
|
600
|
-
sess.execute(stmt)
|
|
601
|
-
except Exception as e:
|
|
602
|
-
if create_and_retry and not self.table_exists():
|
|
603
|
-
log_debug(f"Table does not exist: {self.table.name}")
|
|
604
|
-
log_debug("Creating table and retrying upsert")
|
|
605
|
-
self.create()
|
|
606
|
-
return self.upsert(session, create_and_retry=False)
|
|
607
|
-
else:
|
|
608
|
-
log_warning(f"Exception upserting into table: {e}")
|
|
609
|
-
log_warning(
|
|
610
|
-
"A table upgrade might be required, please review these docs for more information: https://agno.link/upgrade-schema"
|
|
611
|
-
)
|
|
612
|
-
return None
|
|
613
|
-
return self.read(session_id=session.session_id)
|
|
614
|
-
|
|
615
|
-
def delete_session(self, session_id: Optional[str] = None):
|
|
616
|
-
"""
|
|
617
|
-
Delete a session from the database.
|
|
618
|
-
|
|
619
|
-
Args:
|
|
620
|
-
session_id (Optional[str], optional): ID of the session to delete. Defaults to None.
|
|
621
|
-
|
|
622
|
-
Raises:
|
|
623
|
-
Exception: If an error occurs during deletion.
|
|
624
|
-
"""
|
|
625
|
-
if session_id is None:
|
|
626
|
-
logger.warning("No session_id provided for deletion.")
|
|
627
|
-
return
|
|
628
|
-
|
|
629
|
-
try:
|
|
630
|
-
with self.Session() as sess, sess.begin():
|
|
631
|
-
# Delete the session with the given session_id
|
|
632
|
-
delete_stmt = self.table.delete().where(self.table.c.session_id == session_id)
|
|
633
|
-
result = sess.execute(delete_stmt)
|
|
634
|
-
if result.rowcount == 0:
|
|
635
|
-
log_debug(f"No session found with session_id: {session_id}")
|
|
636
|
-
else:
|
|
637
|
-
log_debug(f"Successfully deleted session with session_id: {session_id}")
|
|
638
|
-
except Exception as e:
|
|
639
|
-
logger.error(f"Error deleting session: {e}")
|
|
640
|
-
|
|
641
|
-
def drop(self) -> None:
|
|
642
|
-
"""
|
|
643
|
-
Drop the table from the database if it exists.
|
|
644
|
-
"""
|
|
645
|
-
if self.table_exists():
|
|
646
|
-
log_debug(f"Deleting table: {self.table_name}")
|
|
647
|
-
# Drop with checkfirst=True to avoid errors if the table doesn't exist
|
|
648
|
-
self.table.drop(self.db_engine, checkfirst=True)
|
|
649
|
-
# Clear metadata to ensure indexes are recreated properly
|
|
650
|
-
self.metadata = MetaData(schema=self.schema)
|
|
651
|
-
self.table = self.get_table()
|
|
652
|
-
|
|
653
|
-
def __deepcopy__(self, memo):
|
|
654
|
-
"""
|
|
655
|
-
Create a deep copy of the MySQLStorage instance, handling unpickleable attributes.
|
|
656
|
-
|
|
657
|
-
Args:
|
|
658
|
-
memo (dict): A dictionary of objects already copied during the current copying pass.
|
|
659
|
-
|
|
660
|
-
Returns:
|
|
661
|
-
MySQLStorage: A deep-copied instance of MySQLStorage.
|
|
662
|
-
"""
|
|
663
|
-
from copy import deepcopy
|
|
664
|
-
|
|
665
|
-
# Create a new instance without calling __init__
|
|
666
|
-
cls = self.__class__
|
|
667
|
-
copied_obj = cls.__new__(cls)
|
|
668
|
-
memo[id(self)] = copied_obj
|
|
669
|
-
|
|
670
|
-
# Deep copy attributes
|
|
671
|
-
for k, v in self.__dict__.items():
|
|
672
|
-
if k in {"metadata", "table", "inspector"}:
|
|
673
|
-
continue
|
|
674
|
-
# Reuse db_engine and Session without copying
|
|
675
|
-
elif k in {"db_engine", "SqlSession"}:
|
|
676
|
-
setattr(copied_obj, k, v)
|
|
677
|
-
else:
|
|
678
|
-
setattr(copied_obj, k, deepcopy(v, memo))
|
|
679
|
-
|
|
680
|
-
# Recreate metadata and table for the copied instance
|
|
681
|
-
copied_obj.metadata = MetaData(schema=copied_obj.schema)
|
|
682
|
-
copied_obj.inspector = inspect(copied_obj.db_engine)
|
|
683
|
-
copied_obj.table = copied_obj.get_table()
|
|
684
|
-
|
|
685
|
-
return copied_obj
|