agno 1.8.1__py3-none-any.whl → 2.0.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +19 -27
- agno/agent/agent.py +2778 -4123
- agno/api/agent.py +9 -65
- agno/api/api.py +5 -46
- agno/api/evals.py +6 -17
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -41
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +5 -21
- agno/api/schemas/evals.py +7 -16
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +5 -21
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +11 -7
- agno/api/settings.py +53 -0
- agno/api/team.py +9 -64
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/db/__init__.py +24 -0
- agno/db/base.py +245 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +1749 -0
- agno/db/dynamo/schemas.py +278 -0
- agno/db/dynamo/utils.py +684 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1438 -0
- agno/db/firestore/schemas.py +130 -0
- agno/db/firestore/utils.py +278 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1001 -0
- agno/db/gcs_json/utils.py +194 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +888 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1051 -0
- agno/db/json/utils.py +196 -0
- agno/db/migrations/v1_to_v2.py +162 -0
- agno/db/mongo/__init__.py +3 -0
- agno/db/mongo/mongo.py +1417 -0
- agno/db/mongo/schemas.py +77 -0
- agno/db/mongo/utils.py +204 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +1719 -0
- agno/db/mysql/schemas.py +124 -0
- agno/db/mysql/utils.py +298 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1720 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +281 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1371 -0
- agno/db/redis/schemas.py +109 -0
- agno/db/redis/utils.py +288 -0
- agno/db/schemas/__init__.py +3 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +116 -0
- agno/db/singlestore/singlestore.py +1722 -0
- agno/db/singlestore/utils.py +327 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1680 -0
- agno/db/sqlite/utils.py +269 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +142 -43
- agno/eval/performance.py +88 -23
- agno/eval/reliability.py +73 -20
- agno/eval/utils.py +23 -13
- agno/integrations/discord/__init__.py +3 -0
- agno/{app → integrations}/discord/client.py +10 -10
- agno/knowledge/__init__.py +2 -2
- agno/{document → knowledge}/chunking/agentic.py +2 -2
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +3 -3
- agno/{document → knowledge}/chunking/markdown.py +2 -2
- agno/{document → knowledge}/chunking/recursive.py +2 -2
- agno/{document → knowledge}/chunking/row.py +2 -2
- agno/knowledge/chunking/semantic.py +59 -0
- agno/knowledge/chunking/strategy.py +121 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
- agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
- agno/{embedder → knowledge/embedder}/base.py +6 -0
- agno/{embedder → knowledge/embedder}/cohere.py +72 -1
- agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/{embedder → knowledge/embedder}/google.py +74 -1
- agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
- agno/{embedder → knowledge/embedder}/jina.py +48 -2
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +139 -0
- agno/{embedder → knowledge/embedder}/nebius.py +1 -1
- agno/{embedder → knowledge/embedder}/ollama.py +54 -3
- agno/knowledge/embedder/openai.py +223 -0
- agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
- agno/knowledge/knowledge.py +1515 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
- agno/knowledge/reader/base.py +88 -0
- agno/{document → knowledge}/reader/csv_reader.py +68 -15
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/knowledge/reader/gcs_reader.py +67 -0
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +36 -9
- agno/{document → knowledge}/reader/pdf_reader.py +79 -21
- agno/knowledge/reader/reader_factory.py +275 -0
- agno/knowledge/reader/s3_reader.py +171 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +84 -0
- agno/knowledge/reader/web_search_reader.py +389 -0
- agno/{document → knowledge}/reader/website_reader.py +37 -10
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/{reranker → knowledge/reranker}/infinity.py +2 -2
- agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
- agno/knowledge/types.py +30 -0
- agno/knowledge/utils.py +169 -0
- agno/memory/__init__.py +2 -10
- agno/memory/manager.py +1003 -148
- agno/models/aimlapi/__init__.py +2 -2
- agno/models/aimlapi/aimlapi.py +6 -6
- agno/models/anthropic/claude.py +129 -82
- agno/models/aws/bedrock.py +107 -175
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +347 -287
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +100 -42
- agno/models/groq/groq.py +97 -35
- agno/models/huggingface/huggingface.py +92 -27
- agno/models/ibm/watsonx.py +72 -13
- agno/models/litellm/chat.py +85 -13
- agno/models/message.py +38 -144
- agno/models/meta/llama.py +85 -49
- agno/models/metrics.py +120 -0
- agno/models/mistral/mistral.py +90 -21
- agno/models/ollama/__init__.py +0 -2
- agno/models/ollama/chat.py +84 -46
- agno/models/openai/chat.py +121 -23
- agno/models/openai/responses.py +178 -105
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +14 -8
- agno/models/utils.py +20 -0
- agno/models/vercel/__init__.py +2 -2
- agno/models/vercel/v0.py +1 -1
- agno/models/vllm/__init__.py +2 -2
- agno/models/vllm/vllm.py +3 -3
- agno/models/xai/xai.py +10 -10
- agno/os/__init__.py +3 -0
- agno/os/app.py +393 -0
- agno/os/auth.py +47 -0
- agno/os/config.py +103 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +31 -0
- agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
- agno/{app → os/interfaces}/agui/utils.py +65 -28
- agno/os/interfaces/base.py +21 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
- agno/os/interfaces/slack/slack.py +33 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
- agno/os/interfaces/whatsapp/whatsapp.py +30 -0
- agno/os/router.py +843 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +204 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +161 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +413 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +179 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +58 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +163 -0
- agno/os/schema.py +892 -0
- agno/{app/playground → os}/settings.py +8 -15
- agno/os/utils.py +270 -0
- agno/reasoning/azure_ai_foundry.py +4 -4
- agno/reasoning/deepseek.py +4 -4
- agno/reasoning/default.py +6 -11
- agno/reasoning/groq.py +4 -4
- agno/reasoning/helpers.py +4 -6
- agno/reasoning/ollama.py +4 -4
- agno/reasoning/openai.py +4 -4
- agno/run/{response.py → agent.py} +144 -72
- agno/run/base.py +44 -58
- agno/run/cancel.py +83 -0
- agno/run/team.py +133 -77
- agno/run/workflow.py +537 -12
- agno/session/__init__.py +10 -0
- agno/session/agent.py +244 -0
- agno/session/summary.py +225 -0
- agno/session/team.py +262 -0
- agno/{storage/session/v2 → session}/workflow.py +47 -24
- agno/team/__init__.py +15 -16
- agno/team/team.py +2961 -4253
- agno/tools/agentql.py +14 -5
- agno/tools/airflow.py +9 -4
- agno/tools/api.py +7 -3
- agno/tools/apify.py +2 -46
- agno/tools/arxiv.py +8 -3
- agno/tools/aws_lambda.py +7 -5
- agno/tools/aws_ses.py +7 -1
- agno/tools/baidusearch.py +4 -1
- agno/tools/bitbucket.py +4 -4
- agno/tools/brandfetch.py +14 -11
- agno/tools/bravesearch.py +4 -1
- agno/tools/brightdata.py +42 -22
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +18 -13
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +8 -8
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +18 -11
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +16 -7
- agno/tools/discord.py +11 -8
- agno/tools/docker.py +30 -42
- agno/tools/duckdb.py +34 -53
- agno/tools/duckduckgo.py +8 -7
- agno/tools/e2b.py +61 -61
- agno/tools/eleven_labs.py +35 -28
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +29 -29
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +68 -17
- agno/tools/giphy.py +22 -10
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +45 -61
- agno/tools/google_bigquery.py +7 -6
- agno/tools/google_maps.py +11 -26
- agno/tools/googlesearch.py +7 -2
- agno/tools/googlesheets.py +21 -17
- agno/tools/hackernews.py +9 -5
- agno/tools/jina.py +5 -4
- agno/tools/jira.py +18 -9
- agno/tools/knowledge.py +31 -32
- agno/tools/linear.py +18 -33
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +31 -19
- agno/tools/mem0.py +18 -12
- agno/tools/memori.py +14 -10
- agno/tools/mlx_transcribe.py +3 -2
- agno/tools/models/azure_openai.py +32 -14
- agno/tools/models/gemini.py +58 -31
- agno/tools/models/groq.py +29 -20
- agno/tools/models/nebius.py +27 -11
- agno/tools/models_labs.py +39 -15
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +10 -8
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +57 -26
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +62 -46
- agno/tools/openweather.py +14 -12
- agno/tools/pandas.py +11 -3
- agno/tools/postgres.py +4 -12
- agno/tools/pubmed.py +4 -1
- agno/tools/python.py +9 -22
- agno/tools/reasoning.py +35 -27
- agno/tools/reddit.py +11 -26
- agno/tools/replicate.py +54 -41
- agno/tools/resend.py +4 -1
- agno/tools/scrapegraph.py +15 -14
- agno/tools/searxng.py +10 -23
- agno/tools/serpapi.py +6 -3
- agno/tools/serper.py +13 -4
- agno/tools/shell.py +9 -2
- agno/tools/slack.py +12 -11
- agno/tools/sleep.py +3 -2
- agno/tools/spider.py +24 -4
- agno/tools/sql.py +7 -6
- agno/tools/tavily.py +6 -4
- agno/tools/telegram.py +12 -4
- agno/tools/todoist.py +11 -31
- agno/tools/toolkit.py +1 -1
- agno/tools/trafilatura.py +22 -6
- agno/tools/trello.py +9 -22
- agno/tools/twilio.py +10 -3
- agno/tools/user_control_flow.py +6 -1
- agno/tools/valyu.py +34 -5
- agno/tools/visualization.py +19 -28
- agno/tools/webbrowser.py +4 -3
- agno/tools/webex.py +11 -7
- agno/tools/website.py +15 -46
- agno/tools/webtools.py +12 -4
- agno/tools/whatsapp.py +5 -9
- agno/tools/wikipedia.py +20 -13
- agno/tools/x.py +14 -13
- agno/tools/yfinance.py +13 -40
- agno/tools/youtube.py +26 -20
- agno/tools/zendesk.py +7 -2
- agno/tools/zep.py +10 -7
- agno/tools/zoom.py +10 -9
- agno/utils/common.py +1 -19
- agno/utils/events.py +95 -118
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +2 -2
- agno/utils/mcp.py +11 -5
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/claude.py +6 -4
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1565 -0
- agno/utils/print_response/workflow.py +1451 -0
- agno/utils/prompts.py +14 -14
- agno/utils/reasoning.py +87 -0
- agno/utils/response.py +42 -42
- agno/utils/string.py +8 -22
- agno/utils/team.py +50 -0
- agno/utils/timer.py +2 -2
- agno/vectordb/base.py +33 -21
- agno/vectordb/cassandra/cassandra.py +287 -23
- agno/vectordb/chroma/chromadb.py +482 -59
- agno/vectordb/clickhouse/clickhousedb.py +270 -63
- agno/vectordb/couchbase/couchbase.py +309 -29
- agno/vectordb/lancedb/lance_db.py +360 -21
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +145 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +374 -0
- agno/vectordb/llamaindex/llamaindexdb.py +127 -0
- agno/vectordb/milvus/milvus.py +242 -32
- agno/vectordb/mongodb/mongodb.py +200 -24
- agno/vectordb/pgvector/pgvector.py +319 -37
- agno/vectordb/pineconedb/pineconedb.py +221 -27
- agno/vectordb/qdrant/qdrant.py +334 -14
- agno/vectordb/singlestore/singlestore.py +286 -29
- agno/vectordb/surrealdb/surrealdb.py +187 -7
- agno/vectordb/upstashdb/upstashdb.py +342 -26
- agno/vectordb/weaviate/weaviate.py +227 -165
- agno/workflow/__init__.py +17 -13
- agno/workflow/{v2/condition.py → condition.py} +135 -32
- agno/workflow/{v2/loop.py → loop.py} +115 -28
- agno/workflow/{v2/parallel.py → parallel.py} +138 -108
- agno/workflow/{v2/router.py → router.py} +133 -32
- agno/workflow/{v2/step.py → step.py} +200 -42
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2394 -696
- agno-2.0.0a1.dist-info/METADATA +355 -0
- agno-2.0.0a1.dist-info/RECORD +514 -0
- agno/agent/metrics.py +0 -107
- agno/api/app.py +0 -35
- agno/api/playground.py +0 -92
- agno/api/schemas/app.py +0 -12
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -35
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workflows.py +0 -33
- agno/api/workspace.py +0 -175
- agno/app/agui/__init__.py +0 -3
- agno/app/agui/app.py +0 -17
- agno/app/agui/sync_router.py +0 -120
- agno/app/base.py +0 -186
- agno/app/discord/__init__.py +0 -3
- agno/app/fastapi/__init__.py +0 -3
- agno/app/fastapi/app.py +0 -107
- agno/app/fastapi/async_router.py +0 -457
- agno/app/fastapi/sync_router.py +0 -448
- agno/app/playground/app.py +0 -228
- agno/app/playground/async_router.py +0 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- agno/app/playground/utils.py +0 -46
- agno/app/settings.py +0 -15
- agno/app/slack/__init__.py +0 -3
- agno/app/slack/app.py +0 -19
- agno/app/slack/sync_router.py +0 -92
- agno/app/utils.py +0 -54
- agno/app/whatsapp/__init__.py +0 -3
- agno/app/whatsapp/app.py +0 -15
- agno/app/whatsapp/sync_router.py +0 -197
- agno/cli/auth_server.py +0 -249
- agno/cli/config.py +0 -274
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -357
- agno/cli/settings.py +0 -96
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -5
- agno/document/chunking/semantic.py +0 -45
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -5
- agno/document/reader/base.py +0 -47
- agno/document/reader/docx_reader.py +0 -60
- agno/document/reader/gcs/pdf_reader.py +0 -44
- agno/document/reader/s3/pdf_reader.py +0 -59
- agno/document/reader/s3/text_reader.py +0 -63
- agno/document/reader/url_reader.py +0 -59
- agno/document/reader/youtube_reader.py +0 -58
- agno/embedder/__init__.py +0 -5
- agno/embedder/langdb.py +0 -80
- agno/embedder/mistral.py +0 -82
- agno/embedder/openai.py +0 -78
- agno/file/__init__.py +0 -5
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -702
- agno/knowledge/arxiv.py +0 -33
- agno/knowledge/combined.py +0 -36
- agno/knowledge/csv.py +0 -144
- agno/knowledge/csv_url.py +0 -124
- agno/knowledge/document.py +0 -223
- agno/knowledge/docx.py +0 -137
- agno/knowledge/firecrawl.py +0 -34
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +0 -39
- agno/knowledge/gcs/pdf.py +0 -125
- agno/knowledge/json.py +0 -137
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/light_rag.py +0 -273
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/markdown.py +0 -154
- agno/knowledge/pdf.py +0 -164
- agno/knowledge/pdf_bytes.py +0 -42
- agno/knowledge/pdf_url.py +0 -148
- agno/knowledge/s3/__init__.py +0 -0
- agno/knowledge/s3/base.py +0 -64
- agno/knowledge/s3/pdf.py +0 -33
- agno/knowledge/s3/text.py +0 -34
- agno/knowledge/text.py +0 -141
- agno/knowledge/url.py +0 -46
- agno/knowledge/website.py +0 -179
- agno/knowledge/wikipedia.py +0 -32
- agno/knowledge/youtube.py +0 -35
- agno/memory/agent.py +0 -423
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -5
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -22
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -201
- agno/memory/summary.py +0 -19
- agno/memory/team.py +0 -415
- agno/memory/v2/__init__.py +0 -2
- agno/memory/v2/db/__init__.py +0 -1
- agno/memory/v2/db/base.py +0 -42
- agno/memory/v2/db/firestore.py +0 -339
- agno/memory/v2/db/mongodb.py +0 -196
- agno/memory/v2/db/postgres.py +0 -214
- agno/memory/v2/db/redis.py +0 -187
- agno/memory/v2/db/schema.py +0 -54
- agno/memory/v2/db/sqlite.py +0 -209
- agno/memory/v2/manager.py +0 -437
- agno/memory/v2/memory.py +0 -1097
- agno/memory/v2/schema.py +0 -55
- agno/memory/v2/summarizer.py +0 -215
- agno/memory/workflow.py +0 -38
- agno/models/ollama/tools.py +0 -430
- agno/models/qwen/__init__.py +0 -5
- agno/playground/__init__.py +0 -10
- agno/playground/deploy.py +0 -3
- agno/playground/playground.py +0 -3
- agno/playground/serve.py +0 -3
- agno/playground/settings.py +0 -3
- agno/reranker/__init__.py +0 -0
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +0 -567
- agno/storage/__init__.py +0 -0
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/dynamodb.py +0 -1
- agno/storage/agent/json.py +0 -1
- agno/storage/agent/mongodb.py +0 -1
- agno/storage/agent/postgres.py +0 -1
- agno/storage/agent/singlestore.py +0 -1
- agno/storage/agent/sqlite.py +0 -1
- agno/storage/agent/yaml.py +0 -1
- agno/storage/base.py +0 -60
- agno/storage/dynamodb.py +0 -673
- agno/storage/firestore.py +0 -297
- agno/storage/gcs_json.py +0 -261
- agno/storage/in_memory.py +0 -234
- agno/storage/json.py +0 -237
- agno/storage/mongodb.py +0 -328
- agno/storage/mysql.py +0 -685
- agno/storage/postgres.py +0 -682
- agno/storage/redis.py +0 -336
- agno/storage/session/__init__.py +0 -16
- agno/storage/session/agent.py +0 -64
- agno/storage/session/team.py +0 -63
- agno/storage/session/v2/__init__.py +0 -5
- agno/storage/session/workflow.py +0 -61
- agno/storage/singlestore.py +0 -606
- agno/storage/sqlite.py +0 -646
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/mongodb.py +0 -1
- agno/storage/workflow/postgres.py +0 -1
- agno/storage/workflow/sqlite.py +0 -1
- agno/storage/yaml.py +0 -241
- agno/tools/thinking.py +0 -73
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/workflow/v2/__init__.py +0 -21
- agno/workflow/v2/types.py +0 -357
- agno/workflow/v2/workflow.py +0 -3312
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -52
- agno/workspace/operator.py +0 -757
- agno/workspace/settings.py +0 -158
- agno-1.8.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- /agno/{app → db/migrations}/__init__.py +0 -0
- /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{cli → integrations}/__init__.py +0 -0
- /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
- /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
- /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
- /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
- /agno/{app → os/interfaces}/slack/security.py +0 -0
- /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
- /agno/{file/local → utils/print_response}/__init__.py +0 -0
- /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/storage/firestore.py
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone
|
|
2
|
-
from typing import Any, Dict, List, Literal, Optional, Union
|
|
3
|
-
from uuid import UUID
|
|
4
|
-
|
|
5
|
-
from agno.storage.base import Storage
|
|
6
|
-
from agno.storage.session import Session
|
|
7
|
-
from agno.storage.session.agent import AgentSession
|
|
8
|
-
from agno.storage.session.team import TeamSession
|
|
9
|
-
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
10
|
-
from agno.storage.session.workflow import WorkflowSession
|
|
11
|
-
from agno.utils.log import log_debug, logger
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
from google.api_core import exceptions as google_exceptions
|
|
15
|
-
from google.cloud.firestore_v1 import (
|
|
16
|
-
Client,
|
|
17
|
-
CollectionReference,
|
|
18
|
-
DocumentReference,
|
|
19
|
-
Query,
|
|
20
|
-
)
|
|
21
|
-
from google.cloud.firestore_v1.base_query import FieldFilter
|
|
22
|
-
except ImportError:
|
|
23
|
-
raise ImportError("`firestore` not installed. Please install it with `pip install google-cloud-firestore`")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class FirestoreStorage(Storage):
|
|
27
|
-
def __init__(
|
|
28
|
-
self,
|
|
29
|
-
collection_name: str,
|
|
30
|
-
db_name: Optional[str] = "(default)",
|
|
31
|
-
project_id: Optional[str] = None,
|
|
32
|
-
client: Optional[Client] = None,
|
|
33
|
-
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
34
|
-
):
|
|
35
|
-
super().__init__(mode)
|
|
36
|
-
self.collection_name = collection_name
|
|
37
|
-
self.db_name = db_name
|
|
38
|
-
self.project_id = project_id
|
|
39
|
-
|
|
40
|
-
# Initialize Firestore client
|
|
41
|
-
self._client = client
|
|
42
|
-
if self._client is None:
|
|
43
|
-
self._client = self._initialize_client()
|
|
44
|
-
|
|
45
|
-
# Get collection reference
|
|
46
|
-
self.collection: CollectionReference = self._client.collection(self.collection_name)
|
|
47
|
-
log_debug(f"Created FirestoreStorage with collection: '{self.collection_name}'")
|
|
48
|
-
|
|
49
|
-
def _initialize_client(self) -> Client:
|
|
50
|
-
"""Initialize and return a Firestore client with proper error handling."""
|
|
51
|
-
try:
|
|
52
|
-
client = Client(database=self.db_name, project=self.project_id)
|
|
53
|
-
log_debug(f"Firestore client initialized with database: '{self.db_name}'")
|
|
54
|
-
return client
|
|
55
|
-
except google_exceptions.Unauthenticated as e:
|
|
56
|
-
raise ImportError(
|
|
57
|
-
"Failed to authenticate with Google Cloud. Please set up authentication:\n"
|
|
58
|
-
"1. Run: gcloud auth application-default login\n"
|
|
59
|
-
"2. Or set GOOGLE_APPLICATION_CREDENTIALS environment variable"
|
|
60
|
-
) from e
|
|
61
|
-
except google_exceptions.PermissionDenied as e:
|
|
62
|
-
raise ImportError(
|
|
63
|
-
"Permission denied when accessing Firestore. Please ensure:\n"
|
|
64
|
-
"1. Your service account has the 'Cloud Datastore User' role\n"
|
|
65
|
-
"2. The Firestore API is enabled for your project"
|
|
66
|
-
) from e
|
|
67
|
-
except Exception as e:
|
|
68
|
-
raise ImportError(f"Failed to initialize Firestore client: {e}") from e
|
|
69
|
-
|
|
70
|
-
def _delete_document(self, document: DocumentReference) -> None:
|
|
71
|
-
"""Recursively delete a document and all its subcollections."""
|
|
72
|
-
log_debug(f"Deleting document: {document.path}")
|
|
73
|
-
for collection in document.collections():
|
|
74
|
-
self._delete_collection(collection)
|
|
75
|
-
document.delete()
|
|
76
|
-
|
|
77
|
-
def _delete_collection(self, collection: CollectionReference) -> None:
|
|
78
|
-
"""Recursively delete all documents in a collection."""
|
|
79
|
-
for document in collection.list_documents():
|
|
80
|
-
self._delete_document(document)
|
|
81
|
-
|
|
82
|
-
def _build_query(
|
|
83
|
-
self, base_query: CollectionReference, user_id: Optional[str] = None, entity_id: Optional[str] = None
|
|
84
|
-
) -> Union[Query, CollectionReference]:
|
|
85
|
-
"""Build a Firestore query with optional filters."""
|
|
86
|
-
query: Union[Query, CollectionReference] = base_query
|
|
87
|
-
|
|
88
|
-
if user_id:
|
|
89
|
-
query = query.where(filter=FieldFilter("user_id", "==", user_id))
|
|
90
|
-
|
|
91
|
-
if entity_id:
|
|
92
|
-
if self.mode == "agent":
|
|
93
|
-
query = query.where(filter=FieldFilter("agent_id", "==", entity_id))
|
|
94
|
-
elif self.mode == "team":
|
|
95
|
-
query = query.where(filter=FieldFilter("team_id", "==", entity_id))
|
|
96
|
-
elif self.mode == "workflow":
|
|
97
|
-
query = query.where(filter=FieldFilter("workflow_id", "==", entity_id))
|
|
98
|
-
elif self.mode == "workflow_v2":
|
|
99
|
-
query = query.where(filter=FieldFilter("workflow_id", "==", entity_id))
|
|
100
|
-
|
|
101
|
-
return query
|
|
102
|
-
|
|
103
|
-
def _parse_session(self, doc_data: Optional[Dict[str, Any]]) -> Optional[Session]:
|
|
104
|
-
"""Parse document data into appropriate Session type."""
|
|
105
|
-
if not doc_data:
|
|
106
|
-
return None
|
|
107
|
-
|
|
108
|
-
try:
|
|
109
|
-
if self.mode == "agent":
|
|
110
|
-
return AgentSession.from_dict(doc_data)
|
|
111
|
-
elif self.mode == "team":
|
|
112
|
-
return TeamSession.from_dict(doc_data)
|
|
113
|
-
elif self.mode == "workflow":
|
|
114
|
-
return WorkflowSession.from_dict(doc_data)
|
|
115
|
-
elif self.mode == "workflow_v2":
|
|
116
|
-
return WorkflowSessionV2.from_dict(doc_data)
|
|
117
|
-
except Exception as e:
|
|
118
|
-
logger.error(f"Error parsing session data: {e}")
|
|
119
|
-
return None
|
|
120
|
-
|
|
121
|
-
def create(self) -> None:
|
|
122
|
-
"""
|
|
123
|
-
Create storage if it doesn't exist.
|
|
124
|
-
For Firestore, this is a no-operation as collections are created automatically.
|
|
125
|
-
"""
|
|
126
|
-
try:
|
|
127
|
-
if self._client:
|
|
128
|
-
list(self._client.collections())
|
|
129
|
-
log_debug("Firestore connection successful")
|
|
130
|
-
except Exception as e:
|
|
131
|
-
logger.error(f"Could not connect to Firestore: {e}")
|
|
132
|
-
raise
|
|
133
|
-
|
|
134
|
-
def read(self, session_id: str, user_id: Optional[str] = None) -> Optional[Session]:
|
|
135
|
-
"""Read a session from Firestore."""
|
|
136
|
-
try:
|
|
137
|
-
query = self.collection.where(filter=FieldFilter("session_id", "==", session_id))
|
|
138
|
-
|
|
139
|
-
if user_id:
|
|
140
|
-
query = query.where(filter=FieldFilter("user_id", "==", user_id))
|
|
141
|
-
|
|
142
|
-
docs = query.get()
|
|
143
|
-
for doc in docs:
|
|
144
|
-
doc_dict = doc.to_dict()
|
|
145
|
-
if doc_dict:
|
|
146
|
-
return self._parse_session(doc_dict)
|
|
147
|
-
|
|
148
|
-
return None
|
|
149
|
-
except Exception as e:
|
|
150
|
-
logger.error(f"Error reading session: {e}")
|
|
151
|
-
return None
|
|
152
|
-
|
|
153
|
-
def get_all_session_ids(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[str]:
|
|
154
|
-
"""Get all session IDs, optionally filtered by user_id and/or entity_id."""
|
|
155
|
-
try:
|
|
156
|
-
query = self._build_query(self.collection, user_id, entity_id)
|
|
157
|
-
docs = query.get()
|
|
158
|
-
|
|
159
|
-
# Sort by created_at descending and extract session IDs
|
|
160
|
-
session_ids: List[str] = []
|
|
161
|
-
doc_list = []
|
|
162
|
-
for doc in docs:
|
|
163
|
-
doc_data = doc.to_dict()
|
|
164
|
-
if doc_data:
|
|
165
|
-
doc_list.append(doc_data)
|
|
166
|
-
|
|
167
|
-
sorted_docs = sorted(doc_list, key=lambda x: x.get("created_at", 0), reverse=True)
|
|
168
|
-
|
|
169
|
-
for doc_data in sorted_docs:
|
|
170
|
-
session_id = doc_data.get("session_id")
|
|
171
|
-
if session_id:
|
|
172
|
-
session_ids.append(session_id)
|
|
173
|
-
|
|
174
|
-
return session_ids
|
|
175
|
-
except Exception as e:
|
|
176
|
-
logger.error(f"Error getting session IDs: {e}")
|
|
177
|
-
return []
|
|
178
|
-
|
|
179
|
-
def get_all_sessions(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[Session]:
|
|
180
|
-
"""Get all sessions, optionally filtered by user_id and/or entity_id."""
|
|
181
|
-
sessions: List[Session] = []
|
|
182
|
-
try:
|
|
183
|
-
query = self._build_query(self.collection, user_id, entity_id)
|
|
184
|
-
docs = query.get()
|
|
185
|
-
|
|
186
|
-
# Sort by created_at descending
|
|
187
|
-
doc_list = []
|
|
188
|
-
for doc in docs:
|
|
189
|
-
doc_data = doc.to_dict()
|
|
190
|
-
if doc_data:
|
|
191
|
-
doc_list.append(doc_data)
|
|
192
|
-
|
|
193
|
-
sorted_docs = sorted(doc_list, key=lambda x: x.get("created_at", 0), reverse=True)
|
|
194
|
-
|
|
195
|
-
for doc_data in sorted_docs:
|
|
196
|
-
session = self._parse_session(doc_data)
|
|
197
|
-
if session:
|
|
198
|
-
sessions.append(session)
|
|
199
|
-
|
|
200
|
-
return sessions
|
|
201
|
-
except Exception as e:
|
|
202
|
-
logger.error(f"Error getting all sessions: {e}")
|
|
203
|
-
return []
|
|
204
|
-
|
|
205
|
-
def get_recent_sessions(
|
|
206
|
-
self,
|
|
207
|
-
user_id: Optional[str] = None,
|
|
208
|
-
entity_id: Optional[str] = None,
|
|
209
|
-
limit: Optional[int] = 2,
|
|
210
|
-
) -> List[Session]:
|
|
211
|
-
"""Get the last N sessions, ordered by created_at descending."""
|
|
212
|
-
sessions: List[Session] = []
|
|
213
|
-
try:
|
|
214
|
-
query = self._build_query(self.collection, user_id, entity_id)
|
|
215
|
-
docs = query.get()
|
|
216
|
-
|
|
217
|
-
# Sort by created_at descending
|
|
218
|
-
doc_list = []
|
|
219
|
-
for doc in docs:
|
|
220
|
-
doc_data = doc.to_dict()
|
|
221
|
-
if doc_data:
|
|
222
|
-
doc_list.append(doc_data)
|
|
223
|
-
|
|
224
|
-
sorted_docs = sorted(doc_list, key=lambda x: x.get("created_at", 0), reverse=True)
|
|
225
|
-
|
|
226
|
-
# Apply limit
|
|
227
|
-
if limit is not None:
|
|
228
|
-
sorted_docs = sorted_docs[:limit]
|
|
229
|
-
|
|
230
|
-
for doc_data in sorted_docs:
|
|
231
|
-
session = self._parse_session(doc_data)
|
|
232
|
-
if session:
|
|
233
|
-
sessions.append(session)
|
|
234
|
-
|
|
235
|
-
return sessions
|
|
236
|
-
except Exception as e:
|
|
237
|
-
logger.error(f"Error getting recent sessions: {e}")
|
|
238
|
-
return []
|
|
239
|
-
|
|
240
|
-
def upsert(self, session: Session, create_and_retry: bool = True) -> Optional[Session]:
|
|
241
|
-
"""Insert or update a session in Firestore."""
|
|
242
|
-
try:
|
|
243
|
-
# Prepare session data
|
|
244
|
-
session_dict = session.to_dict()
|
|
245
|
-
now = datetime.now(timezone.utc)
|
|
246
|
-
timestamp = int(now.timestamp())
|
|
247
|
-
|
|
248
|
-
if isinstance(session.session_id, UUID):
|
|
249
|
-
session_dict["session_id"] = str(session.session_id)
|
|
250
|
-
|
|
251
|
-
# Add timestamps
|
|
252
|
-
session_dict["updated_at"] = timestamp
|
|
253
|
-
|
|
254
|
-
# Get document reference
|
|
255
|
-
doc_ref = self.collection.document(session_dict["session_id"])
|
|
256
|
-
doc = doc_ref.get()
|
|
257
|
-
|
|
258
|
-
# Add created_at for new documents
|
|
259
|
-
if not doc.exists:
|
|
260
|
-
session_dict["created_at"] = timestamp
|
|
261
|
-
|
|
262
|
-
# Save to Firestore
|
|
263
|
-
doc_ref.set(session_dict)
|
|
264
|
-
|
|
265
|
-
# Return the updated session
|
|
266
|
-
return self.read(session_id=session_dict["session_id"])
|
|
267
|
-
|
|
268
|
-
except Exception as e:
|
|
269
|
-
logger.error(f"Error upserting session: {e}")
|
|
270
|
-
return None
|
|
271
|
-
|
|
272
|
-
def delete_session(self, session_id: Optional[str] = None) -> None:
|
|
273
|
-
"""Delete a session from Firestore."""
|
|
274
|
-
if session_id is None:
|
|
275
|
-
logger.warning("No session_id provided for deletion")
|
|
276
|
-
return
|
|
277
|
-
|
|
278
|
-
try:
|
|
279
|
-
self.collection.document(session_id).delete()
|
|
280
|
-
log_debug(f"Deleted session: {session_id}")
|
|
281
|
-
except Exception as e:
|
|
282
|
-
logger.error(f"Error deleting session: {e}")
|
|
283
|
-
|
|
284
|
-
def drop(self) -> None:
|
|
285
|
-
"""Drop all sessions from storage."""
|
|
286
|
-
try:
|
|
287
|
-
self._delete_collection(self.collection)
|
|
288
|
-
log_debug(f"Dropped all sessions in collection: {self.collection_name}")
|
|
289
|
-
except Exception as e:
|
|
290
|
-
logger.error(f"Error dropping collection: {e}")
|
|
291
|
-
|
|
292
|
-
def upgrade_schema(self) -> None:
|
|
293
|
-
"""
|
|
294
|
-
Upgrade the schema of the storage.
|
|
295
|
-
For Firestore, this is a no-op as it's schema-less.
|
|
296
|
-
"""
|
|
297
|
-
pass
|
agno/storage/gcs_json.py
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import time
|
|
3
|
-
from typing import Any, List, Literal, Optional
|
|
4
|
-
|
|
5
|
-
from agno.storage.json import JsonStorage, Storage
|
|
6
|
-
from agno.storage.session import Session
|
|
7
|
-
from agno.storage.session.agent import AgentSession
|
|
8
|
-
from agno.storage.session.team import TeamSession
|
|
9
|
-
from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
|
|
10
|
-
from agno.storage.session.workflow import WorkflowSession
|
|
11
|
-
from agno.utils.log import logger
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
from google.cloud import storage as gcs
|
|
15
|
-
except ImportError:
|
|
16
|
-
raise ImportError("`google-cloud-storage` not installed. Please install it with `pip install google-cloud-storage`")
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class GCSJsonStorage(JsonStorage):
|
|
20
|
-
"""
|
|
21
|
-
A Cloud-based JSON storage for agent sessions that stores session (memory) data
|
|
22
|
-
in a GCS bucket. This class derives from JsonStorage and replaces local
|
|
23
|
-
file system operations with Cloud Storage operations. The GCS client and bucket
|
|
24
|
-
are initialized once in the constructor and then reused for all subsequent operations.
|
|
25
|
-
|
|
26
|
-
Parameters:
|
|
27
|
-
- bucket_name: The GCS bucket name (must be provided).
|
|
28
|
-
- prefix: The GCS folder path prefix). See (Flat Namespace)[https://cloud.google.com/storage/docs/objects#flat-namespace] in GCS docs for details.
|
|
29
|
-
- mode: One of "agent", "team", or "workflow". Defaults to "agent".
|
|
30
|
-
- project: Optional; the GCP project ID. Defaults to current Google Cloud's project (set with `gcloud init`).
|
|
31
|
-
- location: Optional; the GCP location for the bucket. Default's to current project's location.
|
|
32
|
-
- credentials: Optional credentials object; if not provided, defaults will be used.
|
|
33
|
-
"""
|
|
34
|
-
|
|
35
|
-
def __init__(
|
|
36
|
-
self,
|
|
37
|
-
bucket_name: str,
|
|
38
|
-
prefix: Optional[str] = "",
|
|
39
|
-
mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
|
|
40
|
-
project: Optional[str] = None,
|
|
41
|
-
location: Optional[str] = None,
|
|
42
|
-
credentials: Optional[Any] = None,
|
|
43
|
-
):
|
|
44
|
-
# Call Storage's __init__ directly to bypass the folder creation logic in JsonStorage.
|
|
45
|
-
Storage.__init__(self, mode=mode)
|
|
46
|
-
self.bucket_name = bucket_name
|
|
47
|
-
if prefix is not None and prefix != "" and not prefix.endswith("/"):
|
|
48
|
-
prefix += "/"
|
|
49
|
-
self.prefix = prefix
|
|
50
|
-
self.project = project
|
|
51
|
-
self.location = location
|
|
52
|
-
|
|
53
|
-
# Initialize the GCS client once; if STORAGE_EMULATOR_HOST is set, it will be used automatically.
|
|
54
|
-
self.client = gcs.Client(project=self.project, credentials=credentials)
|
|
55
|
-
self.bucket = self.client.bucket(self.bucket_name)
|
|
56
|
-
|
|
57
|
-
def _get_blob_path(self, session_id: str) -> str:
|
|
58
|
-
"""Returns the blob path for a given session."""
|
|
59
|
-
return f"{self.prefix}{session_id}.json"
|
|
60
|
-
|
|
61
|
-
def create(self) -> None:
|
|
62
|
-
"""
|
|
63
|
-
Creates the bucket if it doesn't exist
|
|
64
|
-
The client and bucket are already stored in self.
|
|
65
|
-
"""
|
|
66
|
-
try:
|
|
67
|
-
self.bucket = self.client.create_bucket(self.bucket_name, self.location, self.project)
|
|
68
|
-
logger.info(f"Bucket {self.bucket_name} created successfully.")
|
|
69
|
-
except Exception as e:
|
|
70
|
-
# If the bucket already exists, check for conflict (HTTP 409) and continue.
|
|
71
|
-
if hasattr(e, "code") and e.code == 409:
|
|
72
|
-
logger.info(f"Bucket {self.bucket_name} already exists.")
|
|
73
|
-
else:
|
|
74
|
-
logger.error(f"Failed to create bucket {self.bucket_name}: {e}")
|
|
75
|
-
raise
|
|
76
|
-
|
|
77
|
-
def serialize(self, data: dict) -> str:
|
|
78
|
-
return json.dumps(data, ensure_ascii=False, indent=4)
|
|
79
|
-
|
|
80
|
-
def deserialize(self, data: str) -> dict:
|
|
81
|
-
return json.loads(data)
|
|
82
|
-
|
|
83
|
-
def read(self, session_id: str, user_id: Optional[str] = None) -> Optional[Session]:
|
|
84
|
-
"""
|
|
85
|
-
Reads a session JSON blob from the GCS bucket and returns a Session object.
|
|
86
|
-
If the blob is not found, returns None.
|
|
87
|
-
"""
|
|
88
|
-
blob = self.bucket.blob(self._get_blob_path(session_id))
|
|
89
|
-
try:
|
|
90
|
-
data_str = blob.download_as_bytes().decode("utf-8")
|
|
91
|
-
data = self.deserialize(data_str)
|
|
92
|
-
except Exception as e:
|
|
93
|
-
# If the error indicates that the blob was not found (404), return None.
|
|
94
|
-
if "404" in str(e):
|
|
95
|
-
return None
|
|
96
|
-
logger.error(f"Error reading session {session_id} from GCS: {e}")
|
|
97
|
-
return None
|
|
98
|
-
|
|
99
|
-
if user_id and data.get("user_id") != user_id:
|
|
100
|
-
return None
|
|
101
|
-
|
|
102
|
-
if self.mode == "agent":
|
|
103
|
-
return AgentSession.from_dict(data)
|
|
104
|
-
elif self.mode == "team":
|
|
105
|
-
return TeamSession.from_dict(data)
|
|
106
|
-
elif self.mode == "workflow":
|
|
107
|
-
return WorkflowSession.from_dict(data)
|
|
108
|
-
elif self.mode == "workflow_v2":
|
|
109
|
-
return WorkflowSessionV2.from_dict(data)
|
|
110
|
-
return None
|
|
111
|
-
|
|
112
|
-
def get_all_session_ids(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[str]:
|
|
113
|
-
"""
|
|
114
|
-
Lists all session IDs stored in the bucket.
|
|
115
|
-
"""
|
|
116
|
-
session_ids = []
|
|
117
|
-
for blob in self.client.list_blobs(self.bucket, prefix=self.prefix):
|
|
118
|
-
if blob.name.endswith(".json"):
|
|
119
|
-
session_ids.append(blob.name.replace(".json", ""))
|
|
120
|
-
return session_ids
|
|
121
|
-
|
|
122
|
-
def get_all_sessions(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[Session]:
|
|
123
|
-
"""
|
|
124
|
-
Retrieves all sessions stored in the bucket.
|
|
125
|
-
"""
|
|
126
|
-
sessions: List[Session] = []
|
|
127
|
-
for blob in self.client.list_blobs(self.bucket, prefix=self.prefix):
|
|
128
|
-
if blob.name.endswith(".json"):
|
|
129
|
-
try:
|
|
130
|
-
data_str = blob.download_as_bytes().decode("utf-8")
|
|
131
|
-
data = self.deserialize(data_str)
|
|
132
|
-
|
|
133
|
-
if user_id and data.get("user_id") != user_id:
|
|
134
|
-
continue
|
|
135
|
-
session: Optional[Session] = None
|
|
136
|
-
if self.mode == "agent":
|
|
137
|
-
session = AgentSession.from_dict(data)
|
|
138
|
-
elif self.mode == "team":
|
|
139
|
-
session = TeamSession.from_dict(data)
|
|
140
|
-
elif self.mode == "workflow":
|
|
141
|
-
session = WorkflowSession.from_dict(data)
|
|
142
|
-
elif self.mode == "workflow_v2":
|
|
143
|
-
session = WorkflowSessionV2.from_dict(data)
|
|
144
|
-
if session is not None:
|
|
145
|
-
sessions.append(session)
|
|
146
|
-
except Exception as e:
|
|
147
|
-
logger.error(f"Error reading session from blob {blob.name}: {e}")
|
|
148
|
-
continue
|
|
149
|
-
return sessions
|
|
150
|
-
|
|
151
|
-
def get_recent_sessions(
|
|
152
|
-
self,
|
|
153
|
-
user_id: Optional[str] = None,
|
|
154
|
-
entity_id: Optional[str] = None,
|
|
155
|
-
limit: Optional[int] = 2,
|
|
156
|
-
) -> List[Session]:
|
|
157
|
-
"""Get the last N sessions, ordered by created_at descending.
|
|
158
|
-
|
|
159
|
-
Args:
|
|
160
|
-
num_history_sessions: Number of most recent sessions to return
|
|
161
|
-
user_id: Filter by user ID
|
|
162
|
-
entity_id: Filter by entity ID (agent_id, team_id, or workflow_id)
|
|
163
|
-
|
|
164
|
-
Returns:
|
|
165
|
-
List[Session]: List of most recent sessions
|
|
166
|
-
"""
|
|
167
|
-
sessions: List[Session] = []
|
|
168
|
-
# List of (created_at, data) tuples for sorting
|
|
169
|
-
session_data: List[tuple[int, dict]] = []
|
|
170
|
-
|
|
171
|
-
try:
|
|
172
|
-
# Get all blobs with the specified prefix
|
|
173
|
-
for blob in self.client.list_blobs(self.bucket, prefix=self.prefix):
|
|
174
|
-
if not blob.name.endswith(".json"):
|
|
175
|
-
continue
|
|
176
|
-
|
|
177
|
-
try:
|
|
178
|
-
data_str = blob.download_as_bytes().decode("utf-8")
|
|
179
|
-
data = self.deserialize(data_str)
|
|
180
|
-
|
|
181
|
-
# Apply filters
|
|
182
|
-
if user_id and data.get("user_id") != user_id:
|
|
183
|
-
continue
|
|
184
|
-
|
|
185
|
-
# Store with created_at for sorting
|
|
186
|
-
created_at = data.get("created_at", 0)
|
|
187
|
-
session_data.append((created_at, data))
|
|
188
|
-
|
|
189
|
-
except Exception as e:
|
|
190
|
-
logger.error(f"Error reading session from blob {blob.name}: {e}")
|
|
191
|
-
continue
|
|
192
|
-
|
|
193
|
-
# Sort by created_at descending and take only num_history_sessions
|
|
194
|
-
session_data.sort(key=lambda x: x[0], reverse=True)
|
|
195
|
-
if limit is not None:
|
|
196
|
-
session_data = session_data[:limit]
|
|
197
|
-
|
|
198
|
-
# Convert filtered and sorted data to Session objects
|
|
199
|
-
for _, data in session_data:
|
|
200
|
-
session: Optional[Session] = None
|
|
201
|
-
if self.mode == "agent":
|
|
202
|
-
session = AgentSession.from_dict(data)
|
|
203
|
-
elif self.mode == "team":
|
|
204
|
-
session = TeamSession.from_dict(data)
|
|
205
|
-
elif self.mode == "workflow":
|
|
206
|
-
session = WorkflowSession.from_dict(data)
|
|
207
|
-
elif self.mode == "workflow_v2":
|
|
208
|
-
session = WorkflowSessionV2.from_dict(data)
|
|
209
|
-
if session is not None:
|
|
210
|
-
sessions.append(session)
|
|
211
|
-
|
|
212
|
-
except Exception as e:
|
|
213
|
-
logger.error(f"Error getting last {limit} sessions: {e}")
|
|
214
|
-
|
|
215
|
-
return sessions
|
|
216
|
-
|
|
217
|
-
def upsert(self, session: Session) -> Optional[Session]:
|
|
218
|
-
"""
|
|
219
|
-
Inserts or updates a session JSON blob in the GCS bucket.
|
|
220
|
-
"""
|
|
221
|
-
blob = self.bucket.blob(self._get_blob_path(session.session_id))
|
|
222
|
-
try:
|
|
223
|
-
data = session.to_dict()
|
|
224
|
-
data["updated_at"] = int(time.time())
|
|
225
|
-
if "created_at" not in data or data["created_at"] is None:
|
|
226
|
-
data["created_at"] = data["updated_at"]
|
|
227
|
-
json_data = self.serialize(data)
|
|
228
|
-
blob.upload_from_string(json_data, content_type="application/json")
|
|
229
|
-
return session
|
|
230
|
-
except Exception as e:
|
|
231
|
-
logger.error(f"Error upserting session {session.session_id}: {e}")
|
|
232
|
-
return None
|
|
233
|
-
|
|
234
|
-
def delete_session(self, session_id: Optional[str] = None):
|
|
235
|
-
"""
|
|
236
|
-
Deletes a session JSON blob from the GCS bucket.
|
|
237
|
-
"""
|
|
238
|
-
if session_id is None:
|
|
239
|
-
return
|
|
240
|
-
blob = self.bucket.blob(self._get_blob_path(session_id))
|
|
241
|
-
try:
|
|
242
|
-
blob.delete()
|
|
243
|
-
except Exception as e:
|
|
244
|
-
logger.error(f"Error deleting session {session_id}: {e}")
|
|
245
|
-
|
|
246
|
-
def drop(self) -> None:
|
|
247
|
-
"""
|
|
248
|
-
Deletes all session JSON blobs from the bucket.
|
|
249
|
-
"""
|
|
250
|
-
prefix = ""
|
|
251
|
-
for blob in self.client.list_blobs(self.bucket, prefix=prefix):
|
|
252
|
-
try:
|
|
253
|
-
blob.delete()
|
|
254
|
-
except Exception as e:
|
|
255
|
-
logger.error(f"Error deleting blob {blob.name}: {e}")
|
|
256
|
-
|
|
257
|
-
def upgrade_schema(self) -> None:
|
|
258
|
-
"""
|
|
259
|
-
Schema upgrade is not implemented.
|
|
260
|
-
"""
|
|
261
|
-
pass
|