agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from datetime import date, datetime, timedelta, timezone
|
|
2
|
+
from textwrap import dedent
|
|
3
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
4
|
+
|
|
5
|
+
from surrealdb import BlockingHttpSurrealConnection, BlockingWsSurrealConnection, RecordID
|
|
6
|
+
|
|
7
|
+
from agno.db.base import SessionType
|
|
8
|
+
from agno.db.surrealdb import utils
|
|
9
|
+
from agno.db.surrealdb.models import desurrealize_session, surrealize_dates
|
|
10
|
+
from agno.db.surrealdb.queries import WhereClause
|
|
11
|
+
from agno.utils.log import log_error
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def get_all_sessions_for_metrics_calculation(
|
|
15
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
|
|
16
|
+
table: str,
|
|
17
|
+
start_timestamp: Optional[datetime] = None,
|
|
18
|
+
end_timestamp: Optional[datetime] = None,
|
|
19
|
+
) -> List[Dict[str, Any]]:
|
|
20
|
+
"""
|
|
21
|
+
Get all sessions of all types (agent, team, workflow) as raw dictionaries.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
start_timestamp (Optional[int]): The start timestamp to filter by. Defaults to None.
|
|
25
|
+
end_timestamp (Optional[int]): The end timestamp to filter by. Defaults to None.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
List[Dict[str, Any]]: List of session dictionaries with session_type field.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
Exception: If an error occurs during retrieval.
|
|
32
|
+
"""
|
|
33
|
+
where = WhereClause()
|
|
34
|
+
|
|
35
|
+
# starting_date
|
|
36
|
+
if start_timestamp is not None:
|
|
37
|
+
where = where.and_("created_at", start_timestamp, ">=")
|
|
38
|
+
|
|
39
|
+
# ending_date
|
|
40
|
+
if end_timestamp is not None:
|
|
41
|
+
where = where.and_("created_at", end_timestamp, "<=")
|
|
42
|
+
|
|
43
|
+
where_clause, where_vars = where.build()
|
|
44
|
+
|
|
45
|
+
# Query
|
|
46
|
+
query = dedent(f"""
|
|
47
|
+
SELECT *
|
|
48
|
+
FROM {table}
|
|
49
|
+
{where_clause}
|
|
50
|
+
""")
|
|
51
|
+
|
|
52
|
+
results = utils.query(client, query, where_vars, dict)
|
|
53
|
+
return [desurrealize_session(x) for x in results]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_metrics_calculation_starting_date(
|
|
57
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection], table: str, get_sessions: Callable
|
|
58
|
+
) -> Optional[date]:
|
|
59
|
+
"""Get the first date for which metrics calculation is needed:
|
|
60
|
+
|
|
61
|
+
1. If there are metrics records, return the date of the first day without a complete metrics record.
|
|
62
|
+
2. If there are no metrics records, return the date of the first recorded session.
|
|
63
|
+
3. If there are no metrics records and no sessions records, return None.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
table (Table): The table to get the starting date for.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Optional[date]: The starting date for which metrics calculation is needed.
|
|
70
|
+
"""
|
|
71
|
+
query = dedent(f"""
|
|
72
|
+
SELECT * FROM ONLY {table}
|
|
73
|
+
ORDER BY date DESC
|
|
74
|
+
LIMIT 1
|
|
75
|
+
""")
|
|
76
|
+
result = utils.query_one(client, query, {}, dict)
|
|
77
|
+
if result:
|
|
78
|
+
# 1. Return the date of the first day without a complete metrics record
|
|
79
|
+
result_date = result["date"]
|
|
80
|
+
assert isinstance(result_date, datetime)
|
|
81
|
+
result_date = result_date.date()
|
|
82
|
+
|
|
83
|
+
if result.get("completed"):
|
|
84
|
+
return result_date + timedelta(days=1)
|
|
85
|
+
else:
|
|
86
|
+
return result_date
|
|
87
|
+
|
|
88
|
+
# 2. No metrics records. Return the date of the first recorded session
|
|
89
|
+
first_session, _ = get_sessions(
|
|
90
|
+
session_type=SessionType.AGENT, # this is ignored because of component_id=None and deserialize=False
|
|
91
|
+
sort_by="created_at",
|
|
92
|
+
sort_order="asc",
|
|
93
|
+
limit=1,
|
|
94
|
+
component_id=None,
|
|
95
|
+
deserialize=False,
|
|
96
|
+
)
|
|
97
|
+
assert isinstance(first_session, list)
|
|
98
|
+
|
|
99
|
+
first_session_date = first_session[0]["created_at"] if first_session else None
|
|
100
|
+
|
|
101
|
+
# 3. No metrics records and no sessions records. Return None
|
|
102
|
+
if first_session_date is None:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
# Handle different types for created_at
|
|
106
|
+
if isinstance(first_session_date, datetime):
|
|
107
|
+
return first_session_date.date()
|
|
108
|
+
elif isinstance(first_session_date, int):
|
|
109
|
+
# Assume it's a Unix timestamp
|
|
110
|
+
return datetime.fromtimestamp(first_session_date, tz=timezone.utc).date()
|
|
111
|
+
elif isinstance(first_session_date, str):
|
|
112
|
+
# Try parsing as ISO format
|
|
113
|
+
return datetime.fromisoformat(first_session_date.replace("Z", "+00:00")).date()
|
|
114
|
+
else:
|
|
115
|
+
# If it's already a date object
|
|
116
|
+
if isinstance(first_session_date, date):
|
|
117
|
+
return first_session_date
|
|
118
|
+
raise ValueError(f"Unexpected type for created_at: {type(first_session_date)}")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def bulk_upsert_metrics(
|
|
122
|
+
client: Union[BlockingWsSurrealConnection, BlockingHttpSurrealConnection],
|
|
123
|
+
table: str,
|
|
124
|
+
metrics_records: List[Dict[str, Any]],
|
|
125
|
+
) -> List[Dict[str, Any]]:
|
|
126
|
+
"""Bulk upsert metrics into the database.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
table (Table): The table to upsert into.
|
|
130
|
+
metrics_records (List[Dict[str, Any]]): The list of metrics records to upsert.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
list[dict]: The upserted metrics records.
|
|
134
|
+
"""
|
|
135
|
+
if not metrics_records:
|
|
136
|
+
return []
|
|
137
|
+
|
|
138
|
+
metrics_records = [surrealize_dates(x) for x in metrics_records]
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
results = []
|
|
142
|
+
from agno.utils.log import log_debug
|
|
143
|
+
|
|
144
|
+
for metric in metrics_records:
|
|
145
|
+
log_debug(f"Upserting metric: {metric}") # Add this
|
|
146
|
+
result = utils.query_one(
|
|
147
|
+
client,
|
|
148
|
+
"UPSERT $record CONTENT $content",
|
|
149
|
+
{"record": RecordID(table, metric["id"]), "content": metric},
|
|
150
|
+
dict,
|
|
151
|
+
)
|
|
152
|
+
if result:
|
|
153
|
+
results.append(result)
|
|
154
|
+
return results
|
|
155
|
+
|
|
156
|
+
except Exception as e:
|
|
157
|
+
import traceback
|
|
158
|
+
|
|
159
|
+
log_error(traceback.format_exc())
|
|
160
|
+
log_error(f"Error upserting metrics: {e}")
|
|
161
|
+
|
|
162
|
+
return []
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def fetch_all_sessions_data(
|
|
166
|
+
sessions: List[Dict[str, Any]], dates_to_process: list[date], start_timestamp: int
|
|
167
|
+
) -> Optional[dict]:
|
|
168
|
+
"""Return all session data for the given dates, for all session types.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
sessions (List[Dict[str, Any]]): The sessions to process.
|
|
172
|
+
dates_to_process (list[date]): The dates to fetch session data for.
|
|
173
|
+
start_timestamp (int): The start timestamp (fallback if created_at is missing).
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
dict: A dictionary with dates as keys and session data as values, for all session types.
|
|
177
|
+
|
|
178
|
+
Example:
|
|
179
|
+
{
|
|
180
|
+
"2000-01-01": {
|
|
181
|
+
"agent": [<session1>, <session2>, ...],
|
|
182
|
+
"team": [...],
|
|
183
|
+
"workflow": [...],
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
"""
|
|
187
|
+
if not dates_to_process:
|
|
188
|
+
return None
|
|
189
|
+
|
|
190
|
+
all_sessions_data: Dict[str, Dict[str, List[Dict[str, Any]]]] = {
|
|
191
|
+
date_to_process.isoformat(): {"agent": [], "team": [], "workflow": []} for date_to_process in dates_to_process
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for session in sessions:
|
|
195
|
+
created_at = session.get("created_at", start_timestamp)
|
|
196
|
+
|
|
197
|
+
# Handle different types for created_at
|
|
198
|
+
if isinstance(created_at, datetime):
|
|
199
|
+
session_date = created_at.date().isoformat()
|
|
200
|
+
elif isinstance(created_at, int):
|
|
201
|
+
session_date = datetime.fromtimestamp(created_at, tz=timezone.utc).date().isoformat()
|
|
202
|
+
elif isinstance(created_at, date):
|
|
203
|
+
session_date = created_at.isoformat()
|
|
204
|
+
else:
|
|
205
|
+
# Fallback to start_timestamp if type is unexpected
|
|
206
|
+
session_date = datetime.fromtimestamp(start_timestamp, tz=timezone.utc).date().isoformat()
|
|
207
|
+
|
|
208
|
+
if session_date in all_sessions_data:
|
|
209
|
+
session_type = session.get("session_type", "agent") # Default to agent if missing
|
|
210
|
+
all_sessions_data[session_date][session_type].append(session)
|
|
211
|
+
|
|
212
|
+
return all_sessions_data
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
216
|
+
"""Calculate metrics for the given single date.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
date_to_process (date): The date to calculate metrics for.
|
|
220
|
+
sessions_data (dict): The sessions data to calculate metrics for.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
dict: The calculated metrics.
|
|
224
|
+
"""
|
|
225
|
+
metrics = {
|
|
226
|
+
"users_count": 0,
|
|
227
|
+
"agent_sessions_count": 0,
|
|
228
|
+
"team_sessions_count": 0,
|
|
229
|
+
"workflow_sessions_count": 0,
|
|
230
|
+
"agent_runs_count": 0,
|
|
231
|
+
"team_runs_count": 0,
|
|
232
|
+
"workflow_runs_count": 0,
|
|
233
|
+
}
|
|
234
|
+
token_metrics = {
|
|
235
|
+
"input_tokens": 0,
|
|
236
|
+
"output_tokens": 0,
|
|
237
|
+
"total_tokens": 0,
|
|
238
|
+
"audio_total_tokens": 0,
|
|
239
|
+
"audio_input_tokens": 0,
|
|
240
|
+
"audio_output_tokens": 0,
|
|
241
|
+
"cache_read_tokens": 0,
|
|
242
|
+
"cache_write_tokens": 0,
|
|
243
|
+
"reasoning_tokens": 0,
|
|
244
|
+
}
|
|
245
|
+
model_counts: Dict[str, int] = {}
|
|
246
|
+
|
|
247
|
+
session_types = [
|
|
248
|
+
("agent", "agent_sessions_count", "agent_runs_count"),
|
|
249
|
+
("team", "team_sessions_count", "team_runs_count"),
|
|
250
|
+
("workflow", "workflow_sessions_count", "workflow_runs_count"),
|
|
251
|
+
]
|
|
252
|
+
all_user_ids = set()
|
|
253
|
+
|
|
254
|
+
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
255
|
+
sessions = sessions_data.get(session_type, [])
|
|
256
|
+
metrics[sessions_count_key] = len(sessions)
|
|
257
|
+
|
|
258
|
+
for session in sessions:
|
|
259
|
+
if session.get("user_id"):
|
|
260
|
+
all_user_ids.add(session["user_id"])
|
|
261
|
+
metrics[runs_count_key] += len(session.get("runs", []))
|
|
262
|
+
if runs := session.get("runs", []):
|
|
263
|
+
for run in runs:
|
|
264
|
+
if model_id := run.get("model"):
|
|
265
|
+
model_provider = run.get("model_provider", "")
|
|
266
|
+
model_counts[f"{model_id}:{model_provider}"] = (
|
|
267
|
+
model_counts.get(f"{model_id}:{model_provider}", 0) + 1
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
session_metrics = session.get("session_data", {}).get("session_metrics", {})
|
|
271
|
+
for field in token_metrics:
|
|
272
|
+
token_metrics[field] += session_metrics.get(field, 0)
|
|
273
|
+
|
|
274
|
+
model_metrics = []
|
|
275
|
+
for model, count in model_counts.items():
|
|
276
|
+
model_id, model_provider = model.split(":")
|
|
277
|
+
model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
|
|
278
|
+
|
|
279
|
+
metrics["users_count"] = len(all_user_ids)
|
|
280
|
+
current_time = datetime.now(timezone.utc)
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
"id": date_to_process.isoformat(), # Changed: Use date as ID (e.g., "2025-10-16")
|
|
284
|
+
"date": current_time.replace(hour=0, minute=0, second=0, microsecond=0), # Date at midnight UTC
|
|
285
|
+
"completed": date_to_process < datetime.now(timezone.utc).date(),
|
|
286
|
+
"token_metrics": token_metrics,
|
|
287
|
+
"model_metrics": model_metrics,
|
|
288
|
+
"created_at": current_time,
|
|
289
|
+
"updated_at": current_time,
|
|
290
|
+
"aggregation_period": "daily",
|
|
291
|
+
**metrics,
|
|
292
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
from dataclasses import asdict
|
|
2
|
+
from datetime import date, datetime, timezone
|
|
3
|
+
from textwrap import dedent
|
|
4
|
+
from typing import Any, Dict, List, Literal, Optional, Sequence
|
|
5
|
+
|
|
6
|
+
from surrealdb import RecordID
|
|
7
|
+
|
|
8
|
+
from agno.db.base import SessionType
|
|
9
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
10
|
+
from agno.db.schemas.evals import EvalRunRecord
|
|
11
|
+
from agno.db.schemas.knowledge import KnowledgeRow
|
|
12
|
+
from agno.db.schemas.memory import UserMemory
|
|
13
|
+
from agno.session import Session
|
|
14
|
+
from agno.session.agent import AgentSession
|
|
15
|
+
from agno.session.team import TeamSession
|
|
16
|
+
from agno.session.workflow import WorkflowSession
|
|
17
|
+
|
|
18
|
+
TableType = Literal[
|
|
19
|
+
"agents",
|
|
20
|
+
"culture",
|
|
21
|
+
"evals",
|
|
22
|
+
"knowledge",
|
|
23
|
+
"memories",
|
|
24
|
+
"metrics",
|
|
25
|
+
"sessions",
|
|
26
|
+
"spans",
|
|
27
|
+
"teams",
|
|
28
|
+
"traces",
|
|
29
|
+
"users",
|
|
30
|
+
"workflows",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def deserialize_record_id(record: dict, agno_field: str, surreal_field: Optional[str] = None) -> dict:
|
|
35
|
+
if surreal_field is None:
|
|
36
|
+
surreal_field = agno_field
|
|
37
|
+
x = record.get(surreal_field)
|
|
38
|
+
if isinstance(x, RecordID):
|
|
39
|
+
record[agno_field] = x.id
|
|
40
|
+
if agno_field != surreal_field:
|
|
41
|
+
del record[surreal_field]
|
|
42
|
+
return record
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def surrealize_dates(record: dict) -> dict:
|
|
46
|
+
copy = record.copy()
|
|
47
|
+
for key, value in copy.items():
|
|
48
|
+
if isinstance(value, date):
|
|
49
|
+
copy[key] = datetime.combine(value, datetime.min.time()).replace(tzinfo=timezone.utc)
|
|
50
|
+
elif key in ["created_at", "updated_at"] and isinstance(value, (int, float)):
|
|
51
|
+
copy[key] = datetime.fromtimestamp(value).replace(tzinfo=timezone.utc)
|
|
52
|
+
elif key in ["created_at", "updated_at"] and isinstance(value, str):
|
|
53
|
+
# Handle ISO string format - convert back to datetime object for SurrealDB
|
|
54
|
+
try:
|
|
55
|
+
dt = datetime.fromisoformat(value)
|
|
56
|
+
if dt.tzinfo is None:
|
|
57
|
+
dt = dt.replace(tzinfo=timezone.utc)
|
|
58
|
+
copy[key] = dt
|
|
59
|
+
except ValueError:
|
|
60
|
+
# If it's not a valid ISO format, leave it as is
|
|
61
|
+
pass
|
|
62
|
+
elif key in ["created_at", "updated_at"] and value is None:
|
|
63
|
+
# Set current time for None datetime fields
|
|
64
|
+
copy[key] = datetime.now(timezone.utc)
|
|
65
|
+
elif isinstance(value, datetime):
|
|
66
|
+
copy[key] = value.replace(tzinfo=timezone.utc)
|
|
67
|
+
return copy
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def desurrealize_dates(record: dict) -> dict:
|
|
71
|
+
copy = record.copy()
|
|
72
|
+
for key, value in copy.items():
|
|
73
|
+
if isinstance(value, datetime):
|
|
74
|
+
copy[key] = int(value.timestamp())
|
|
75
|
+
return copy
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def serialize_session(session: Session, table_names: dict[TableType, str]) -> dict:
|
|
79
|
+
_dict = session.to_dict()
|
|
80
|
+
|
|
81
|
+
if session.session_id is not None:
|
|
82
|
+
_dict["id"] = RecordID(table_names["sessions"], session.session_id)
|
|
83
|
+
del _dict["session_id"]
|
|
84
|
+
|
|
85
|
+
if isinstance(session, AgentSession):
|
|
86
|
+
_dict["agent"] = RecordID(table_names["agents"], session.agent_id)
|
|
87
|
+
del _dict["agent_id"]
|
|
88
|
+
elif isinstance(session, TeamSession):
|
|
89
|
+
_dict["team"] = RecordID(table_names["teams"], session.team_id)
|
|
90
|
+
del _dict["team_id"]
|
|
91
|
+
elif isinstance(session, WorkflowSession):
|
|
92
|
+
_dict["workflow"] = RecordID(table_names["workflows"], session.workflow_id)
|
|
93
|
+
del _dict["workflow_id"]
|
|
94
|
+
|
|
95
|
+
# surrealize dates
|
|
96
|
+
_dict = surrealize_dates(_dict)
|
|
97
|
+
|
|
98
|
+
return _dict
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def desurrealize_session(session_raw: dict, session_type: Optional[SessionType] = None) -> dict:
|
|
102
|
+
session_raw = deserialize_record_id(session_raw, "session_id", "id")
|
|
103
|
+
if session_type == SessionType.AGENT:
|
|
104
|
+
session_raw = deserialize_record_id(session_raw, "agent_id", "agent")
|
|
105
|
+
elif session_type == SessionType.TEAM:
|
|
106
|
+
session_raw = deserialize_record_id(session_raw, "team_id", "team")
|
|
107
|
+
elif session_type == SessionType.WORKFLOW:
|
|
108
|
+
session_raw = deserialize_record_id(session_raw, "workflow_id", "workflow")
|
|
109
|
+
|
|
110
|
+
session_raw = desurrealize_dates(session_raw)
|
|
111
|
+
|
|
112
|
+
if session_raw.get("agent_id"):
|
|
113
|
+
session_raw["session_type"] = SessionType.AGENT
|
|
114
|
+
elif session_raw.get("team_id"):
|
|
115
|
+
session_raw["session_type"] = SessionType.TEAM
|
|
116
|
+
elif session_raw.get("workflow_id"):
|
|
117
|
+
session_raw["session_type"] = SessionType.WORKFLOW
|
|
118
|
+
|
|
119
|
+
return session_raw
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def deserialize_session(session_type: SessionType, session_raw: dict) -> Optional[Session]:
|
|
123
|
+
session_raw = desurrealize_session(session_raw, session_type)
|
|
124
|
+
|
|
125
|
+
if session_type == SessionType.AGENT:
|
|
126
|
+
return AgentSession.from_dict(session_raw)
|
|
127
|
+
elif session_type == SessionType.TEAM:
|
|
128
|
+
return TeamSession.from_dict(session_raw)
|
|
129
|
+
elif session_type == SessionType.WORKFLOW:
|
|
130
|
+
return WorkflowSession.from_dict(session_raw)
|
|
131
|
+
else:
|
|
132
|
+
raise ValueError(f"Invalid session type: {session_type}")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def deserialize_sessions(session_type: SessionType, sessions_raw: List[dict]) -> List[Session]:
|
|
136
|
+
return [x for x in [deserialize_session(session_type, x) for x in sessions_raw] if x is not None]
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_session_type(session: Session) -> SessionType:
|
|
140
|
+
if isinstance(session, AgentSession):
|
|
141
|
+
return SessionType.AGENT
|
|
142
|
+
elif isinstance(session, TeamSession):
|
|
143
|
+
return SessionType.TEAM
|
|
144
|
+
elif isinstance(session, WorkflowSession):
|
|
145
|
+
return SessionType.WORKFLOW
|
|
146
|
+
else:
|
|
147
|
+
raise ValueError(f"Invalid session instance: {type(session)}")
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def desurrealize_user_memory(memory_raw: dict) -> dict:
|
|
151
|
+
copy = memory_raw.copy()
|
|
152
|
+
|
|
153
|
+
copy = deserialize_record_id(copy, "memory_id", "id")
|
|
154
|
+
copy = deserialize_record_id(copy, "user_id", "user")
|
|
155
|
+
copy = deserialize_record_id(copy, "agent_id", "agent")
|
|
156
|
+
copy = deserialize_record_id(copy, "team_id", "team")
|
|
157
|
+
copy = deserialize_record_id(copy, "workflow_id", "workflow")
|
|
158
|
+
|
|
159
|
+
# TODO: is this ok? or should we cast datetimes to int? Like in desurrealize_session
|
|
160
|
+
# copy = desurrealize_dates(copy)
|
|
161
|
+
updated_at = copy.get("updated_at")
|
|
162
|
+
if not isinstance(updated_at, str):
|
|
163
|
+
copy["updated_at"] = str(updated_at)
|
|
164
|
+
|
|
165
|
+
return copy
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def deserialize_user_memory(memory_raw: dict) -> UserMemory:
|
|
169
|
+
return UserMemory.from_dict(desurrealize_user_memory(memory_raw))
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def deserialize_user_memories(memories_raw: Sequence[dict]) -> List[UserMemory]:
|
|
173
|
+
return [deserialize_user_memory(x) for x in memories_raw]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def serialize_user_memory(memory: UserMemory, memory_table_name: str, user_table_name: str) -> dict:
|
|
177
|
+
dict_ = asdict(memory)
|
|
178
|
+
if memory.memory_id is not None:
|
|
179
|
+
dict_["id"] = RecordID(memory_table_name, memory.memory_id)
|
|
180
|
+
del dict_["memory_id"]
|
|
181
|
+
if memory.user_id is not None:
|
|
182
|
+
dict_["user"] = RecordID(user_table_name, memory.user_id)
|
|
183
|
+
del dict_["user_id"]
|
|
184
|
+
|
|
185
|
+
# surrealize dates
|
|
186
|
+
dict_ = surrealize_dates(dict_)
|
|
187
|
+
|
|
188
|
+
return dict_
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def deserialize_knowledge_row(knowledge_row_raw: dict) -> KnowledgeRow:
|
|
192
|
+
copy = knowledge_row_raw.copy()
|
|
193
|
+
|
|
194
|
+
copy = deserialize_record_id(copy, "id")
|
|
195
|
+
copy = desurrealize_dates(copy)
|
|
196
|
+
|
|
197
|
+
return KnowledgeRow.model_validate(copy)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def serialize_knowledge_row(knowledge_row: KnowledgeRow, knowledge_table_name: str) -> dict:
|
|
201
|
+
dict_ = knowledge_row.model_dump()
|
|
202
|
+
if knowledge_row.id is not None:
|
|
203
|
+
dict_["id"] = RecordID(knowledge_table_name, knowledge_row.id)
|
|
204
|
+
|
|
205
|
+
# surrealize dates
|
|
206
|
+
dict_ = surrealize_dates(dict_)
|
|
207
|
+
|
|
208
|
+
return dict_
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def deserialize_cultural_knowledge(cultural_knowledge_raw: dict) -> CulturalKnowledge:
|
|
212
|
+
copy = cultural_knowledge_raw.copy()
|
|
213
|
+
|
|
214
|
+
copy = deserialize_record_id(copy, "id")
|
|
215
|
+
copy = desurrealize_dates(copy)
|
|
216
|
+
|
|
217
|
+
# Extract content, categories, and notes from the content field
|
|
218
|
+
content_json = copy.get("content", {}) or {}
|
|
219
|
+
if isinstance(content_json, dict):
|
|
220
|
+
copy["content"] = content_json.get("content")
|
|
221
|
+
copy["categories"] = content_json.get("categories")
|
|
222
|
+
copy["notes"] = content_json.get("notes")
|
|
223
|
+
|
|
224
|
+
return CulturalKnowledge.from_dict(copy)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def serialize_cultural_knowledge(cultural_knowledge: CulturalKnowledge, culture_table_name: str) -> dict:
|
|
228
|
+
dict_ = asdict(cultural_knowledge)
|
|
229
|
+
if cultural_knowledge.id is not None:
|
|
230
|
+
dict_["id"] = RecordID(culture_table_name, cultural_knowledge.id)
|
|
231
|
+
|
|
232
|
+
# Serialize content, categories, and notes into a single content dict for DB storage
|
|
233
|
+
content_dict: Dict[str, Any] = {}
|
|
234
|
+
if cultural_knowledge.content is not None:
|
|
235
|
+
content_dict["content"] = cultural_knowledge.content
|
|
236
|
+
if cultural_knowledge.categories is not None:
|
|
237
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
238
|
+
if cultural_knowledge.notes is not None:
|
|
239
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
240
|
+
|
|
241
|
+
# Replace the separate fields with the combined content field
|
|
242
|
+
dict_["content"] = content_dict if content_dict else None
|
|
243
|
+
# Remove the now-redundant fields since they're in content
|
|
244
|
+
dict_.pop("categories", None)
|
|
245
|
+
dict_.pop("notes", None)
|
|
246
|
+
|
|
247
|
+
# surrealize dates
|
|
248
|
+
dict_ = surrealize_dates(dict_)
|
|
249
|
+
|
|
250
|
+
return dict_
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def desurrealize_eval_run_record(eval_run_record_raw: dict) -> dict:
|
|
254
|
+
copy = eval_run_record_raw.copy()
|
|
255
|
+
|
|
256
|
+
copy = deserialize_record_id(copy, "run_id", "id")
|
|
257
|
+
copy = deserialize_record_id(copy, "agent_id", "agent")
|
|
258
|
+
copy = deserialize_record_id(copy, "team_id", "team")
|
|
259
|
+
copy = deserialize_record_id(copy, "workflow_id", "workflow")
|
|
260
|
+
|
|
261
|
+
return copy
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def deserialize_eval_run_record(eval_run_record_raw: dict) -> EvalRunRecord:
|
|
265
|
+
return EvalRunRecord.model_validate(desurrealize_eval_run_record(eval_run_record_raw))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def serialize_eval_run_record(eval_run_record: EvalRunRecord, table_names: dict[TableType, str]) -> dict:
|
|
269
|
+
dict_ = eval_run_record.model_dump()
|
|
270
|
+
if eval_run_record.run_id is not None:
|
|
271
|
+
dict_["id"] = RecordID(table_names["evals"], eval_run_record.run_id)
|
|
272
|
+
del dict_["run_id"]
|
|
273
|
+
if eval_run_record.agent_id is not None:
|
|
274
|
+
dict_["agent"] = RecordID(table_names["agents"], eval_run_record.agent_id)
|
|
275
|
+
del dict_["agent_id"]
|
|
276
|
+
if eval_run_record.team_id is not None:
|
|
277
|
+
dict_["team"] = RecordID(table_names["teams"], eval_run_record.team_id)
|
|
278
|
+
del dict_["team_id"]
|
|
279
|
+
if eval_run_record.workflow_id is not None:
|
|
280
|
+
dict_["workflow"] = RecordID(table_names["workflows"], eval_run_record.workflow_id)
|
|
281
|
+
del dict_["workflow_id"]
|
|
282
|
+
return dict_
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def get_schema(table_type: TableType, table_name: str) -> str:
|
|
286
|
+
define_table = f"DEFINE TABLE {table_name} SCHEMALESS;"
|
|
287
|
+
if table_type == "memories":
|
|
288
|
+
return dedent(f"""
|
|
289
|
+
{define_table}
|
|
290
|
+
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
291
|
+
""")
|
|
292
|
+
elif table_type == "knowledge":
|
|
293
|
+
return dedent(f"""
|
|
294
|
+
{define_table}
|
|
295
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
296
|
+
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
297
|
+
""")
|
|
298
|
+
elif table_type == "culture":
|
|
299
|
+
return dedent(f"""
|
|
300
|
+
{define_table}
|
|
301
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
302
|
+
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
303
|
+
""")
|
|
304
|
+
elif table_type == "sessions":
|
|
305
|
+
return dedent(f"""
|
|
306
|
+
{define_table}
|
|
307
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
308
|
+
DEFINE FIELD OVERWRITE updated_at ON {table_name} TYPE datetime VALUE time::now();
|
|
309
|
+
""")
|
|
310
|
+
elif table_type == "traces":
|
|
311
|
+
return dedent(f"""
|
|
312
|
+
{define_table}
|
|
313
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
314
|
+
DEFINE INDEX idx_trace_id ON {table_name} FIELDS trace_id UNIQUE;
|
|
315
|
+
DEFINE INDEX idx_run_id ON {table_name} FIELDS run_id;
|
|
316
|
+
DEFINE INDEX idx_session_id ON {table_name} FIELDS session_id;
|
|
317
|
+
DEFINE INDEX idx_user_id ON {table_name} FIELDS user_id;
|
|
318
|
+
DEFINE INDEX idx_agent_id ON {table_name} FIELDS agent_id;
|
|
319
|
+
DEFINE INDEX idx_team_id ON {table_name} FIELDS team_id;
|
|
320
|
+
DEFINE INDEX idx_workflow_id ON {table_name} FIELDS workflow_id;
|
|
321
|
+
DEFINE INDEX idx_status ON {table_name} FIELDS status;
|
|
322
|
+
DEFINE INDEX idx_start_time ON {table_name} FIELDS start_time;
|
|
323
|
+
""")
|
|
324
|
+
elif table_type == "spans":
|
|
325
|
+
return dedent(f"""
|
|
326
|
+
{define_table}
|
|
327
|
+
DEFINE FIELD OVERWRITE created_at ON {table_name} TYPE datetime VALUE time::now();
|
|
328
|
+
DEFINE INDEX idx_span_id ON {table_name} FIELDS span_id UNIQUE;
|
|
329
|
+
DEFINE INDEX idx_trace_id ON {table_name} FIELDS trace_id;
|
|
330
|
+
DEFINE INDEX idx_parent_span_id ON {table_name} FIELDS parent_span_id;
|
|
331
|
+
DEFINE INDEX idx_start_time ON {table_name} FIELDS start_time;
|
|
332
|
+
""")
|
|
333
|
+
else:
|
|
334
|
+
return define_table
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
2
|
+
from typing import Any, Final, Literal, Optional
|
|
3
|
+
|
|
4
|
+
OPERATOR = Literal["=", "!=", "<=", ">=", "~", "IN", "CONTAINSANY"]
|
|
5
|
+
|
|
6
|
+
COUNT_QUERY: Final[str] = dedent("""
|
|
7
|
+
RETURN (
|
|
8
|
+
SELECT count(id) AS count
|
|
9
|
+
{group_fields}
|
|
10
|
+
FROM {table}
|
|
11
|
+
{where_clause}
|
|
12
|
+
{group_clause}
|
|
13
|
+
)[0] OR {{count: 0}}
|
|
14
|
+
""")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class WhereClause:
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self._conditions = []
|
|
20
|
+
self._params = {}
|
|
21
|
+
self._param_count = 0
|
|
22
|
+
|
|
23
|
+
def _add_filter(self, field: str, operator: str, value: Any):
|
|
24
|
+
param_name = f"p{self._param_count}"
|
|
25
|
+
self._params[param_name] = value
|
|
26
|
+
self._param_count += 1
|
|
27
|
+
|
|
28
|
+
condition = f"{field} {operator} ${param_name}"
|
|
29
|
+
if not self._conditions:
|
|
30
|
+
self._conditions.append(condition)
|
|
31
|
+
else:
|
|
32
|
+
self._conditions.append("AND")
|
|
33
|
+
self._conditions.append(condition)
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
def and_(self, field: str, value: Any, operator: OPERATOR = "="):
|
|
37
|
+
return self._add_filter(field, operator, value)
|
|
38
|
+
|
|
39
|
+
def build(self) -> tuple[str, dict[str, Any]]:
|
|
40
|
+
if not self._conditions:
|
|
41
|
+
return "", {}
|
|
42
|
+
return "WHERE " + " ".join(self._conditions), self._params
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def order_limit_start(
|
|
46
|
+
sort_by: Optional[str] = None,
|
|
47
|
+
sort_order: Optional[str] = None,
|
|
48
|
+
limit: Optional[int] = None,
|
|
49
|
+
page: Optional[int] = None,
|
|
50
|
+
) -> str:
|
|
51
|
+
if sort_order is not None:
|
|
52
|
+
if "desc" in sort_order.lower():
|
|
53
|
+
sort_order = "DESC"
|
|
54
|
+
else:
|
|
55
|
+
sort_order = "ASC"
|
|
56
|
+
|
|
57
|
+
order_clause = f"ORDER BY {sort_by} {sort_order or ''}" if sort_by is not None else ""
|
|
58
|
+
|
|
59
|
+
if limit is not None:
|
|
60
|
+
limit_clause = f"LIMIT {limit}"
|
|
61
|
+
if page is not None:
|
|
62
|
+
offset = (page - 1) * limit
|
|
63
|
+
start_clause = f"START {offset}"
|
|
64
|
+
else:
|
|
65
|
+
start_clause = ""
|
|
66
|
+
else:
|
|
67
|
+
limit_clause = ""
|
|
68
|
+
start_clause = ""
|
|
69
|
+
|
|
70
|
+
clauses = [order_clause, limit_clause, start_clause]
|
|
71
|
+
return " ".join(clause for clause in clauses if clause)
|