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
agno/db/dynamo/utils.py
ADDED
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import time
|
|
3
|
+
from datetime import date, datetime, timedelta, timezone
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from agno.db.base import SessionType
|
|
8
|
+
from agno.db.schemas.culture import CulturalKnowledge
|
|
9
|
+
from agno.db.schemas.evals import EvalRunRecord
|
|
10
|
+
from agno.db.schemas.knowledge import KnowledgeRow
|
|
11
|
+
from agno.session import Session
|
|
12
|
+
from agno.utils.log import log_debug, log_error, log_info
|
|
13
|
+
|
|
14
|
+
# -- Serialization utils --
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def serialize_to_dynamo_item(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
18
|
+
"""Serialize the given dict to a valid DynamoDB item
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
data: The dict to serialize
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
A DynamoDB-ready dict with the serialized data
|
|
25
|
+
|
|
26
|
+
"""
|
|
27
|
+
item: Dict[str, Any] = {}
|
|
28
|
+
for key, value in data.items():
|
|
29
|
+
if value is not None:
|
|
30
|
+
if isinstance(value, (int, float)):
|
|
31
|
+
item[key] = {"N": str(value)}
|
|
32
|
+
elif isinstance(value, str):
|
|
33
|
+
item[key] = {"S": value}
|
|
34
|
+
elif isinstance(value, bool):
|
|
35
|
+
item[key] = {"BOOL": value}
|
|
36
|
+
elif isinstance(value, (dict, list)):
|
|
37
|
+
item[key] = {"S": json.dumps(value)}
|
|
38
|
+
else:
|
|
39
|
+
item[key] = {"S": str(value)}
|
|
40
|
+
return item
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def deserialize_from_dynamodb_item(item: Dict[str, Any]) -> Dict[str, Any]:
|
|
44
|
+
data = {}
|
|
45
|
+
for key, value in item.items():
|
|
46
|
+
if "S" in value:
|
|
47
|
+
try:
|
|
48
|
+
data[key] = json.loads(value["S"])
|
|
49
|
+
except (json.JSONDecodeError, TypeError):
|
|
50
|
+
data[key] = value["S"]
|
|
51
|
+
elif "N" in value:
|
|
52
|
+
data[key] = float(value["N"]) if "." in value["N"] else int(value["N"])
|
|
53
|
+
elif "BOOL" in value:
|
|
54
|
+
data[key] = value["BOOL"]
|
|
55
|
+
elif "SS" in value:
|
|
56
|
+
data[key] = value["SS"]
|
|
57
|
+
elif "NS" in value:
|
|
58
|
+
data[key] = [float(n) if "." in n else int(n) for n in value["NS"]]
|
|
59
|
+
elif "M" in value:
|
|
60
|
+
data[key] = deserialize_from_dynamodb_item(value["M"])
|
|
61
|
+
elif "L" in value:
|
|
62
|
+
data[key] = [deserialize_from_dynamodb_item({"item": item})["item"] for item in value["L"]]
|
|
63
|
+
return data
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def serialize_knowledge_row(knowledge: KnowledgeRow) -> Dict[str, Any]:
|
|
67
|
+
"""Convert KnowledgeRow to DynamoDB item format."""
|
|
68
|
+
return serialize_to_dynamo_item(
|
|
69
|
+
{
|
|
70
|
+
"id": knowledge.id,
|
|
71
|
+
"name": knowledge.name,
|
|
72
|
+
"description": knowledge.description,
|
|
73
|
+
"type": getattr(knowledge, "type", None),
|
|
74
|
+
"status": getattr(knowledge, "status", None),
|
|
75
|
+
"status_message": getattr(knowledge, "status_message", None),
|
|
76
|
+
"metadata": getattr(knowledge, "metadata", None),
|
|
77
|
+
"size": getattr(knowledge, "size", None),
|
|
78
|
+
"linked_to": getattr(knowledge, "linked_to", None),
|
|
79
|
+
"access_count": getattr(knowledge, "access_count", None),
|
|
80
|
+
"created_at": int(knowledge.created_at) if knowledge.created_at else None,
|
|
81
|
+
"updated_at": int(knowledge.updated_at) if knowledge.updated_at else None,
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def deserialize_knowledge_row(item: Dict[str, Any]) -> KnowledgeRow:
|
|
87
|
+
"""Convert DynamoDB item to KnowledgeRow."""
|
|
88
|
+
data = deserialize_from_dynamodb_item(item)
|
|
89
|
+
return KnowledgeRow(
|
|
90
|
+
id=data["id"],
|
|
91
|
+
name=data["name"],
|
|
92
|
+
description=data["description"],
|
|
93
|
+
metadata=data.get("metadata"),
|
|
94
|
+
type=data.get("type"),
|
|
95
|
+
size=data.get("size"),
|
|
96
|
+
linked_to=data.get("linked_to"),
|
|
97
|
+
access_count=data.get("access_count"),
|
|
98
|
+
status=data.get("status"),
|
|
99
|
+
status_message=data.get("status_message"),
|
|
100
|
+
created_at=data.get("created_at"),
|
|
101
|
+
updated_at=data.get("updated_at"),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def serialize_eval_record(eval_record: EvalRunRecord) -> Dict[str, Any]:
|
|
106
|
+
"""Convert EvalRunRecord to DynamoDB item format."""
|
|
107
|
+
return serialize_to_dynamo_item(
|
|
108
|
+
{
|
|
109
|
+
"run_id": eval_record.run_id,
|
|
110
|
+
"eval_type": eval_record.eval_type,
|
|
111
|
+
"eval_data": eval_record.eval_data,
|
|
112
|
+
"name": getattr(eval_record, "name", None),
|
|
113
|
+
"agent_id": getattr(eval_record, "agent_id", None),
|
|
114
|
+
"team_id": getattr(eval_record, "team_id", None),
|
|
115
|
+
"workflow_id": getattr(eval_record, "workflow_id", None),
|
|
116
|
+
"model_id": getattr(eval_record, "model_id", None),
|
|
117
|
+
"model_provider": getattr(eval_record, "model_provider", None),
|
|
118
|
+
"evaluated_component_name": getattr(eval_record, "evaluated_component_name", None),
|
|
119
|
+
}
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def deserialize_eval_record(item: Dict[str, Any]) -> EvalRunRecord:
|
|
124
|
+
"""Convert DynamoDB item to EvalRunRecord."""
|
|
125
|
+
data = deserialize_from_dynamodb_item(item)
|
|
126
|
+
# Convert timestamp fields back to datetime
|
|
127
|
+
if "created_at" in data and data["created_at"]:
|
|
128
|
+
data["created_at"] = datetime.fromtimestamp(data["created_at"], tz=timezone.utc)
|
|
129
|
+
if "updated_at" in data and data["updated_at"]:
|
|
130
|
+
data["updated_at"] = datetime.fromtimestamp(data["updated_at"], tz=timezone.utc)
|
|
131
|
+
return EvalRunRecord(run_id=data["run_id"], eval_type=data["eval_type"], eval_data=data["eval_data"])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# -- DB Utils --
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def create_table_if_not_exists(dynamodb_client, table_name: str, schema: Dict[str, Any]) -> bool:
|
|
138
|
+
"""Create DynamoDB table if it doesn't exist."""
|
|
139
|
+
try:
|
|
140
|
+
dynamodb_client.describe_table(TableName=table_name)
|
|
141
|
+
return True
|
|
142
|
+
|
|
143
|
+
except dynamodb_client.exceptions.ResourceNotFoundException:
|
|
144
|
+
log_info(f"Creating table {table_name}")
|
|
145
|
+
try:
|
|
146
|
+
dynamodb_client.create_table(**schema)
|
|
147
|
+
# Wait for table to be created
|
|
148
|
+
waiter = dynamodb_client.get_waiter("table_exists")
|
|
149
|
+
waiter.wait(TableName=table_name)
|
|
150
|
+
|
|
151
|
+
log_debug(f"Table {table_name} created successfully")
|
|
152
|
+
|
|
153
|
+
return True
|
|
154
|
+
|
|
155
|
+
except Exception as e:
|
|
156
|
+
log_error(f"Failed to create table {table_name}: {e}")
|
|
157
|
+
return False
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def apply_pagination(
|
|
161
|
+
items: List[Dict[str, Any]], limit: Optional[int] = None, page: Optional[int] = None
|
|
162
|
+
) -> List[Dict[str, Any]]:
|
|
163
|
+
"""Apply pagination to a list of items."""
|
|
164
|
+
if limit is None:
|
|
165
|
+
return items
|
|
166
|
+
|
|
167
|
+
start_index = 0
|
|
168
|
+
if page is not None and page > 1:
|
|
169
|
+
start_index = (page - 1) * limit
|
|
170
|
+
|
|
171
|
+
return items[start_index : start_index + limit]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def apply_sorting(
|
|
175
|
+
items: List[Dict[str, Any]], sort_by: Optional[str] = None, sort_order: Optional[str] = None
|
|
176
|
+
) -> List[Dict[str, Any]]:
|
|
177
|
+
"""Apply sorting to a list of items."""
|
|
178
|
+
if sort_by is None:
|
|
179
|
+
sort_by = "created_at"
|
|
180
|
+
|
|
181
|
+
reverse = sort_order == "desc"
|
|
182
|
+
|
|
183
|
+
return sorted(items, key=lambda x: x.get(sort_by, ""), reverse=reverse)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
# -- Session utils --
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def prepare_session_data(session: "Session") -> Dict[str, Any]:
|
|
190
|
+
"""Prepare session data for storage by serializing JSON fields and setting session type."""
|
|
191
|
+
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
192
|
+
|
|
193
|
+
serialized_session = session.to_dict()
|
|
194
|
+
|
|
195
|
+
# Handle JSON fields
|
|
196
|
+
json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
|
|
197
|
+
for field in json_fields:
|
|
198
|
+
if field in serialized_session and serialized_session[field] is not None:
|
|
199
|
+
if isinstance(serialized_session[field], (dict, list)):
|
|
200
|
+
serialized_session[field] = json.dumps(serialized_session[field])
|
|
201
|
+
|
|
202
|
+
# Set the session type
|
|
203
|
+
if isinstance(session, AgentSession):
|
|
204
|
+
serialized_session["session_type"] = SessionType.AGENT.value
|
|
205
|
+
elif isinstance(session, TeamSession):
|
|
206
|
+
serialized_session["session_type"] = SessionType.TEAM.value
|
|
207
|
+
elif isinstance(session, WorkflowSession):
|
|
208
|
+
serialized_session["session_type"] = SessionType.WORKFLOW.value
|
|
209
|
+
|
|
210
|
+
return serialized_session
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def merge_with_existing_session(new_session: Dict[str, Any], existing_item: Dict[str, Any]) -> Dict[str, Any]:
|
|
214
|
+
"""Merge new session data with existing session, preserving important fields."""
|
|
215
|
+
existing_session = deserialize_from_dynamodb_item(existing_item)
|
|
216
|
+
|
|
217
|
+
# Start with existing session as base
|
|
218
|
+
merged_session = existing_session.copy()
|
|
219
|
+
|
|
220
|
+
if "session_data" in new_session:
|
|
221
|
+
merged_session_data = merge_session_data(
|
|
222
|
+
existing_session.get("session_data", {}), new_session.get("session_data", {})
|
|
223
|
+
)
|
|
224
|
+
merged_session["session_data"] = json.dumps(merged_session_data)
|
|
225
|
+
|
|
226
|
+
for key, value in new_session.items():
|
|
227
|
+
if key != "created_at" and key != "session_data" and value is not None:
|
|
228
|
+
merged_session[key] = value
|
|
229
|
+
|
|
230
|
+
# Always preserve created_at and set updated_at
|
|
231
|
+
merged_session["created_at"] = existing_session.get("created_at")
|
|
232
|
+
merged_session["updated_at"] = int(time.time())
|
|
233
|
+
|
|
234
|
+
return merged_session
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def merge_session_data(existing_data: Any, new_data: Any) -> Dict[str, Any]:
|
|
238
|
+
"""Merge session_data fields, handling JSON string conversion."""
|
|
239
|
+
|
|
240
|
+
# Parse existing session_data
|
|
241
|
+
if isinstance(existing_data, str):
|
|
242
|
+
existing_data = json.loads(existing_data)
|
|
243
|
+
existing_data = existing_data or {}
|
|
244
|
+
|
|
245
|
+
# Parse new session_data
|
|
246
|
+
if isinstance(new_data, str):
|
|
247
|
+
new_data = json.loads(new_data)
|
|
248
|
+
new_data = new_data or {}
|
|
249
|
+
|
|
250
|
+
# Merge letting new data take precedence
|
|
251
|
+
return {**existing_data, **new_data}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def deserialize_session_result(
|
|
255
|
+
serialized_session: Dict[str, Any], original_session: "Session", deserialize: Optional[bool]
|
|
256
|
+
) -> Optional[Union["Session", Dict[str, Any]]]:
|
|
257
|
+
"""Deserialize the session result based on the deserialize flag and session type."""
|
|
258
|
+
from agno.session import AgentSession, TeamSession, WorkflowSession
|
|
259
|
+
|
|
260
|
+
if not deserialize:
|
|
261
|
+
return serialized_session
|
|
262
|
+
|
|
263
|
+
if isinstance(original_session, AgentSession):
|
|
264
|
+
return AgentSession.from_dict(serialized_session)
|
|
265
|
+
elif isinstance(original_session, TeamSession):
|
|
266
|
+
return TeamSession.from_dict(serialized_session)
|
|
267
|
+
elif isinstance(original_session, WorkflowSession):
|
|
268
|
+
return WorkflowSession.from_dict(serialized_session)
|
|
269
|
+
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def deserialize_session(session: Dict[str, Any]) -> Optional[Session]:
|
|
274
|
+
"""Deserialize session data from DynamoDB format to Session object."""
|
|
275
|
+
try:
|
|
276
|
+
deserialized = session.copy()
|
|
277
|
+
|
|
278
|
+
# Handle JSON fields
|
|
279
|
+
json_fields = ["session_data", "memory", "tools", "functions", "additional_data"]
|
|
280
|
+
for field in json_fields:
|
|
281
|
+
if field in deserialized and deserialized[field] is not None:
|
|
282
|
+
if isinstance(deserialized[field], str):
|
|
283
|
+
try:
|
|
284
|
+
deserialized[field] = json.loads(deserialized[field])
|
|
285
|
+
except json.JSONDecodeError:
|
|
286
|
+
log_error(f"Failed to deserialize {field} field")
|
|
287
|
+
deserialized[field] = None
|
|
288
|
+
|
|
289
|
+
# Handle timestamp fields
|
|
290
|
+
for field in ["created_at", "updated_at"]:
|
|
291
|
+
if field in deserialized and deserialized[field] is not None:
|
|
292
|
+
if isinstance(deserialized[field], (int, float)):
|
|
293
|
+
deserialized[field] = datetime.fromtimestamp(deserialized[field], tz=timezone.utc)
|
|
294
|
+
elif isinstance(deserialized[field], str):
|
|
295
|
+
try:
|
|
296
|
+
deserialized[field] = datetime.fromisoformat(deserialized[field])
|
|
297
|
+
except ValueError:
|
|
298
|
+
deserialized[field] = datetime.fromtimestamp(float(deserialized[field]), tz=timezone.utc)
|
|
299
|
+
|
|
300
|
+
return Session.from_dict(deserialized) # type: ignore
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
log_error(f"Failed to deserialize session: {e}")
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# -- Metrics utils --
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def calculate_date_metrics(date_to_process: date, sessions_data: dict) -> dict:
|
|
311
|
+
"""Calculate metrics for the given single date.
|
|
312
|
+
Args:
|
|
313
|
+
date_to_process (date): The date to calculate metrics for.
|
|
314
|
+
sessions_data (dict): The sessions data to calculate metrics for.
|
|
315
|
+
Returns:
|
|
316
|
+
dict: The calculated metrics.
|
|
317
|
+
"""
|
|
318
|
+
metrics = {
|
|
319
|
+
"users_count": 0,
|
|
320
|
+
"agent_sessions_count": 0,
|
|
321
|
+
"team_sessions_count": 0,
|
|
322
|
+
"workflow_sessions_count": 0,
|
|
323
|
+
"agent_runs_count": 0,
|
|
324
|
+
"team_runs_count": 0,
|
|
325
|
+
"workflow_runs_count": 0,
|
|
326
|
+
}
|
|
327
|
+
token_metrics = {
|
|
328
|
+
"input_tokens": 0,
|
|
329
|
+
"output_tokens": 0,
|
|
330
|
+
"total_tokens": 0,
|
|
331
|
+
"audio_total_tokens": 0,
|
|
332
|
+
"audio_input_tokens": 0,
|
|
333
|
+
"audio_output_tokens": 0,
|
|
334
|
+
"cache_read_tokens": 0,
|
|
335
|
+
"cache_write_tokens": 0,
|
|
336
|
+
"reasoning_tokens": 0,
|
|
337
|
+
}
|
|
338
|
+
model_counts: Dict[str, int] = {}
|
|
339
|
+
session_types = [
|
|
340
|
+
("agent", "agent_sessions_count", "agent_runs_count"),
|
|
341
|
+
("team", "team_sessions_count", "team_runs_count"),
|
|
342
|
+
("workflow", "workflow_sessions_count", "workflow_runs_count"),
|
|
343
|
+
]
|
|
344
|
+
all_user_ids = set()
|
|
345
|
+
for session_type, sessions_count_key, runs_count_key in session_types:
|
|
346
|
+
sessions = sessions_data.get(session_type, []) or []
|
|
347
|
+
metrics[sessions_count_key] = len(sessions)
|
|
348
|
+
for session in sessions:
|
|
349
|
+
if session.get("user_id"):
|
|
350
|
+
all_user_ids.add(session["user_id"])
|
|
351
|
+
metrics[runs_count_key] += len(session.get("runs", []))
|
|
352
|
+
if runs := session.get("runs", []):
|
|
353
|
+
for run in runs:
|
|
354
|
+
if model_id := run.get("model"):
|
|
355
|
+
model_provider = run.get("model_provider", "")
|
|
356
|
+
model_counts[f"{model_id}:{model_provider}"] = (
|
|
357
|
+
model_counts.get(f"{model_id}:{model_provider}", 0) + 1
|
|
358
|
+
)
|
|
359
|
+
session_metrics = session.get("session_data", {}).get("session_metrics", {})
|
|
360
|
+
for field in token_metrics:
|
|
361
|
+
token_metrics[field] += session_metrics.get(field, 0)
|
|
362
|
+
model_metrics = []
|
|
363
|
+
for model, count in model_counts.items():
|
|
364
|
+
model_id, model_provider = model.rsplit(":", 1)
|
|
365
|
+
model_metrics.append({"model_id": model_id, "model_provider": model_provider, "count": count})
|
|
366
|
+
metrics["users_count"] = len(all_user_ids)
|
|
367
|
+
current_time = int(time.time())
|
|
368
|
+
return {
|
|
369
|
+
"id": str(uuid4()),
|
|
370
|
+
"date": date_to_process,
|
|
371
|
+
"completed": date_to_process < datetime.now(timezone.utc).date(),
|
|
372
|
+
"token_metrics": token_metrics,
|
|
373
|
+
"model_metrics": model_metrics,
|
|
374
|
+
"created_at": current_time,
|
|
375
|
+
"updated_at": current_time,
|
|
376
|
+
"aggregation_period": "daily",
|
|
377
|
+
**metrics,
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def get_dates_to_calculate_metrics_for(starting_date: date) -> list[date]:
|
|
382
|
+
"""Return the list of dates to calculate metrics for.
|
|
383
|
+
Args:
|
|
384
|
+
starting_date (date): The starting date to calculate metrics for.
|
|
385
|
+
Returns:
|
|
386
|
+
list[date]: The list of dates to calculate metrics for.
|
|
387
|
+
"""
|
|
388
|
+
today = datetime.now(timezone.utc).date()
|
|
389
|
+
days_diff = (today - starting_date).days + 1
|
|
390
|
+
if days_diff <= 0:
|
|
391
|
+
return []
|
|
392
|
+
return [starting_date + timedelta(days=x) for x in range(days_diff)]
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def fetch_all_sessions_data(
|
|
396
|
+
sessions: List[Dict[str, Any]], dates_to_process: list[date], start_timestamp: int
|
|
397
|
+
) -> Optional[dict]:
|
|
398
|
+
"""Return all session data for the given dates, for all session types.
|
|
399
|
+
Args:
|
|
400
|
+
sessions: List of session data dictionaries
|
|
401
|
+
dates_to_process (list[date]): The dates to fetch session data for.
|
|
402
|
+
start_timestamp: The start timestamp for the range
|
|
403
|
+
Returns:
|
|
404
|
+
dict: A dictionary with dates as keys and session data as values, for all session types.
|
|
405
|
+
Example:
|
|
406
|
+
{
|
|
407
|
+
"2000-01-01": {
|
|
408
|
+
"agent": [<session1>, <session2>, ...],
|
|
409
|
+
"team": [...],
|
|
410
|
+
"workflow": [...],
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
"""
|
|
414
|
+
if not dates_to_process:
|
|
415
|
+
return None
|
|
416
|
+
all_sessions_data: Dict[str, Dict[str, List[Dict[str, Any]]]] = {
|
|
417
|
+
date_to_process.isoformat(): {"agent": [], "team": [], "workflow": []} for date_to_process in dates_to_process
|
|
418
|
+
}
|
|
419
|
+
for session in sessions:
|
|
420
|
+
session_date = (
|
|
421
|
+
datetime.fromtimestamp(session.get("created_at", start_timestamp), tz=timezone.utc).date().isoformat()
|
|
422
|
+
)
|
|
423
|
+
if session_date in all_sessions_data:
|
|
424
|
+
all_sessions_data[session_date][session["session_type"]].append(session)
|
|
425
|
+
return all_sessions_data
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def fetch_all_sessions_data_by_type(
|
|
429
|
+
dynamodb_client,
|
|
430
|
+
table_name: str,
|
|
431
|
+
session_type: str,
|
|
432
|
+
user_id: Optional[str] = None,
|
|
433
|
+
component_id: Optional[str] = None,
|
|
434
|
+
session_name: Optional[str] = None,
|
|
435
|
+
) -> List[Dict[str, Any]]:
|
|
436
|
+
"""Fetch all sessions data from DynamoDB table using GSI for session_type."""
|
|
437
|
+
items = []
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
# Build filter expression for additional filters
|
|
441
|
+
filter_expression = None
|
|
442
|
+
expression_attribute_names = {}
|
|
443
|
+
expression_attribute_values = {":session_type": {"S": session_type}}
|
|
444
|
+
|
|
445
|
+
if user_id:
|
|
446
|
+
filter_expression = "#user_id = :user_id"
|
|
447
|
+
expression_attribute_names["#user_id"] = "user_id"
|
|
448
|
+
expression_attribute_values[":user_id"] = {"S": user_id}
|
|
449
|
+
|
|
450
|
+
if component_id:
|
|
451
|
+
component_filter = "#component_id = :component_id"
|
|
452
|
+
expression_attribute_names["#component_id"] = "component_id"
|
|
453
|
+
expression_attribute_values[":component_id"] = {"S": component_id}
|
|
454
|
+
|
|
455
|
+
if filter_expression:
|
|
456
|
+
filter_expression += f" AND {component_filter}"
|
|
457
|
+
else:
|
|
458
|
+
filter_expression = component_filter
|
|
459
|
+
|
|
460
|
+
if session_name:
|
|
461
|
+
name_filter = "#session_name = :session_name"
|
|
462
|
+
expression_attribute_names["#session_name"] = "session_name"
|
|
463
|
+
expression_attribute_values[":session_name"] = {"S": session_name}
|
|
464
|
+
|
|
465
|
+
if filter_expression:
|
|
466
|
+
filter_expression += f" AND {name_filter}"
|
|
467
|
+
else:
|
|
468
|
+
filter_expression = name_filter
|
|
469
|
+
|
|
470
|
+
# Use GSI query for session_type (more efficient than scan)
|
|
471
|
+
query_kwargs = {
|
|
472
|
+
"TableName": table_name,
|
|
473
|
+
"IndexName": "session_type-created_at-index",
|
|
474
|
+
"KeyConditionExpression": "session_type = :session_type",
|
|
475
|
+
"ExpressionAttributeValues": expression_attribute_values,
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if filter_expression:
|
|
479
|
+
query_kwargs["FilterExpression"] = filter_expression
|
|
480
|
+
|
|
481
|
+
if expression_attribute_names:
|
|
482
|
+
query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
|
|
483
|
+
|
|
484
|
+
response = dynamodb_client.query(**query_kwargs)
|
|
485
|
+
items.extend(response.get("Items", []))
|
|
486
|
+
|
|
487
|
+
# Handle pagination
|
|
488
|
+
while "LastEvaluatedKey" in response:
|
|
489
|
+
query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
|
|
490
|
+
response = dynamodb_client.query(**query_kwargs)
|
|
491
|
+
items.extend(response.get("Items", []))
|
|
492
|
+
|
|
493
|
+
except Exception as e:
|
|
494
|
+
log_error(f"Failed to fetch sessions: {e}")
|
|
495
|
+
|
|
496
|
+
return items
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def bulk_upsert_metrics(dynamodb_client, table_name: str, metrics_data: List[Dict[str, Any]]) -> None:
|
|
500
|
+
"""Bulk upsert metrics data to DynamoDB."""
|
|
501
|
+
try:
|
|
502
|
+
# DynamoDB batch write has a limit of 25 items
|
|
503
|
+
batch_size = 25
|
|
504
|
+
|
|
505
|
+
for i in range(0, len(metrics_data), batch_size):
|
|
506
|
+
batch = metrics_data[i : i + batch_size]
|
|
507
|
+
|
|
508
|
+
request_items: Dict[str, List[Dict[str, Any]]] = {table_name: []}
|
|
509
|
+
|
|
510
|
+
for metric in batch:
|
|
511
|
+
request_items[table_name].append({"PutRequest": {"Item": metric}})
|
|
512
|
+
|
|
513
|
+
dynamodb_client.batch_write_item(RequestItems=request_items)
|
|
514
|
+
|
|
515
|
+
except Exception as e:
|
|
516
|
+
log_error(f"Failed to bulk upsert metrics: {e}")
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
# -- Query utils --
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def build_query_filter_expression(filters: Dict[str, Any]) -> tuple[Optional[str], Dict[str, str], Dict[str, Any]]:
|
|
523
|
+
"""Build DynamoDB query filter expression from filters dictionary.
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
filters: Dictionary of filter key-value pairs
|
|
527
|
+
|
|
528
|
+
Returns:
|
|
529
|
+
Tuple of (filter_expression, expression_attribute_names, expression_attribute_values)
|
|
530
|
+
"""
|
|
531
|
+
filter_expressions = []
|
|
532
|
+
expression_attribute_names = {}
|
|
533
|
+
expression_attribute_values = {}
|
|
534
|
+
|
|
535
|
+
for field, value in filters.items():
|
|
536
|
+
if value is not None:
|
|
537
|
+
filter_expressions.append(f"#{field} = :{field}")
|
|
538
|
+
expression_attribute_names[f"#{field}"] = field
|
|
539
|
+
expression_attribute_values[f":{field}"] = {"S": value}
|
|
540
|
+
|
|
541
|
+
filter_expression = " AND ".join(filter_expressions) if filter_expressions else None
|
|
542
|
+
return filter_expression, expression_attribute_names, expression_attribute_values
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def build_topic_filter_expression(topics: List[str]) -> tuple[str, Dict[str, Any]]:
|
|
546
|
+
"""Build DynamoDB filter expression for topics.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
topics: List of topics to filter by
|
|
550
|
+
|
|
551
|
+
Returns:
|
|
552
|
+
Tuple of (filter_expression, expression_attribute_values)
|
|
553
|
+
"""
|
|
554
|
+
topic_filters = []
|
|
555
|
+
expression_attribute_values = {}
|
|
556
|
+
|
|
557
|
+
for i, topic in enumerate(topics):
|
|
558
|
+
topic_key = f":topic_{i}"
|
|
559
|
+
topic_filters.append(f"contains(topics, {topic_key})")
|
|
560
|
+
expression_attribute_values[topic_key] = {"S": topic}
|
|
561
|
+
|
|
562
|
+
filter_expression = f"({' OR '.join(topic_filters)})"
|
|
563
|
+
return filter_expression, expression_attribute_values
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
def execute_query_with_pagination(
|
|
567
|
+
dynamodb_client,
|
|
568
|
+
table_name: str,
|
|
569
|
+
index_name: str,
|
|
570
|
+
key_condition_expression: str,
|
|
571
|
+
expression_attribute_names: Dict[str, str],
|
|
572
|
+
expression_attribute_values: Dict[str, Any],
|
|
573
|
+
filter_expression: Optional[str] = None,
|
|
574
|
+
sort_by: Optional[str] = None,
|
|
575
|
+
sort_order: Optional[str] = None,
|
|
576
|
+
limit: Optional[int] = None,
|
|
577
|
+
page: Optional[int] = None,
|
|
578
|
+
) -> List[Dict[str, Any]]:
|
|
579
|
+
"""Execute DynamoDB query with pagination support.
|
|
580
|
+
|
|
581
|
+
Args:
|
|
582
|
+
dynamodb_client: DynamoDB client
|
|
583
|
+
table_name: Table name
|
|
584
|
+
index_name: Index name for query
|
|
585
|
+
key_condition_expression: Key condition expression
|
|
586
|
+
expression_attribute_names: Expression attribute names
|
|
587
|
+
expression_attribute_values: Expression attribute values
|
|
588
|
+
filter_expression: Optional filter expression
|
|
589
|
+
sort_by: Field to sort by
|
|
590
|
+
sort_order: Sort order (asc/desc)
|
|
591
|
+
limit: Limit for pagination
|
|
592
|
+
page: Page number
|
|
593
|
+
|
|
594
|
+
Returns:
|
|
595
|
+
List of DynamoDB items
|
|
596
|
+
"""
|
|
597
|
+
query_kwargs = {
|
|
598
|
+
"TableName": table_name,
|
|
599
|
+
"IndexName": index_name,
|
|
600
|
+
"KeyConditionExpression": key_condition_expression,
|
|
601
|
+
"ExpressionAttributeValues": expression_attribute_values,
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if expression_attribute_names:
|
|
605
|
+
query_kwargs["ExpressionAttributeNames"] = expression_attribute_names
|
|
606
|
+
|
|
607
|
+
if filter_expression:
|
|
608
|
+
query_kwargs["FilterExpression"] = filter_expression
|
|
609
|
+
|
|
610
|
+
# Apply sorting at query level if sorting by created_at
|
|
611
|
+
if sort_by == "created_at":
|
|
612
|
+
query_kwargs["ScanIndexForward"] = sort_order != "desc" # type: ignore
|
|
613
|
+
|
|
614
|
+
# Apply limit at DynamoDB level if no pagination
|
|
615
|
+
if limit and not page:
|
|
616
|
+
query_kwargs["Limit"] = limit # type: ignore
|
|
617
|
+
|
|
618
|
+
items = []
|
|
619
|
+
response = dynamodb_client.query(**query_kwargs)
|
|
620
|
+
items.extend(response.get("Items", []))
|
|
621
|
+
|
|
622
|
+
# Handle pagination
|
|
623
|
+
while "LastEvaluatedKey" in response:
|
|
624
|
+
query_kwargs["ExclusiveStartKey"] = response["LastEvaluatedKey"]
|
|
625
|
+
response = dynamodb_client.query(**query_kwargs)
|
|
626
|
+
items.extend(response.get("Items", []))
|
|
627
|
+
|
|
628
|
+
return items
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def process_query_results(
|
|
632
|
+
items: List[Dict[str, Any]],
|
|
633
|
+
sort_by: Optional[str] = None,
|
|
634
|
+
sort_order: Optional[str] = None,
|
|
635
|
+
limit: Optional[int] = None,
|
|
636
|
+
page: Optional[int] = None,
|
|
637
|
+
deserialize_func: Optional[Callable] = None,
|
|
638
|
+
deserialize: bool = True,
|
|
639
|
+
) -> Union[List[Any], tuple[List[Any], int]]:
|
|
640
|
+
"""Process query results with sorting, pagination, and deserialization.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
items: List of DynamoDB items
|
|
644
|
+
sort_by: Field to sort by
|
|
645
|
+
sort_order: Sort order (asc/desc)
|
|
646
|
+
limit: Limit for pagination
|
|
647
|
+
page: Page number
|
|
648
|
+
deserialize_func: Function to deserialize items
|
|
649
|
+
deserialize: Whether to deserialize items
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
List of processed items or tuple of (items, total_count)
|
|
653
|
+
"""
|
|
654
|
+
# Convert DynamoDB items to data
|
|
655
|
+
processed_data = []
|
|
656
|
+
for item in items:
|
|
657
|
+
data = deserialize_from_dynamodb_item(item)
|
|
658
|
+
if data:
|
|
659
|
+
processed_data.append(data)
|
|
660
|
+
|
|
661
|
+
# Apply in-memory sorting for fields not handled by DynamoDB
|
|
662
|
+
if sort_by and sort_by != "created_at":
|
|
663
|
+
processed_data = apply_sorting(processed_data, sort_by, sort_order)
|
|
664
|
+
|
|
665
|
+
# Get total count before pagination
|
|
666
|
+
total_count = len(processed_data)
|
|
667
|
+
|
|
668
|
+
# Apply pagination
|
|
669
|
+
if page:
|
|
670
|
+
processed_data = apply_pagination(processed_data, limit, page)
|
|
671
|
+
|
|
672
|
+
if not deserialize or not deserialize_func:
|
|
673
|
+
return processed_data, total_count
|
|
674
|
+
|
|
675
|
+
# Deserialize items
|
|
676
|
+
deserialized_items = []
|
|
677
|
+
for data in processed_data:
|
|
678
|
+
try:
|
|
679
|
+
item = deserialize_func(data)
|
|
680
|
+
if item:
|
|
681
|
+
deserialized_items.append(item)
|
|
682
|
+
except Exception as e:
|
|
683
|
+
log_error(f"Failed to deserialize item: {e}")
|
|
684
|
+
|
|
685
|
+
return deserialized_items
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
# -- Cultural Knowledge util methods --
|
|
689
|
+
def serialize_cultural_knowledge_for_db(cultural_knowledge: CulturalKnowledge) -> Dict[str, Any]:
|
|
690
|
+
"""Serialize a CulturalKnowledge object for database storage.
|
|
691
|
+
|
|
692
|
+
Converts the model's separate content, categories, and notes fields
|
|
693
|
+
into a single JSON dict for the database content column.
|
|
694
|
+
DynamoDB supports nested maps/dicts natively.
|
|
695
|
+
|
|
696
|
+
Args:
|
|
697
|
+
cultural_knowledge (CulturalKnowledge): The cultural knowledge object to serialize.
|
|
698
|
+
|
|
699
|
+
Returns:
|
|
700
|
+
Dict[str, Any]: A dictionary with the content field as a dict containing content, categories, and notes.
|
|
701
|
+
"""
|
|
702
|
+
content_dict: Dict[str, Any] = {}
|
|
703
|
+
if cultural_knowledge.content is not None:
|
|
704
|
+
content_dict["content"] = cultural_knowledge.content
|
|
705
|
+
if cultural_knowledge.categories is not None:
|
|
706
|
+
content_dict["categories"] = cultural_knowledge.categories
|
|
707
|
+
if cultural_knowledge.notes is not None:
|
|
708
|
+
content_dict["notes"] = cultural_knowledge.notes
|
|
709
|
+
|
|
710
|
+
return content_dict if content_dict else {}
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def deserialize_cultural_knowledge_from_db(db_row: Dict[str, Any]) -> CulturalKnowledge:
|
|
714
|
+
"""Deserialize a database row to a CulturalKnowledge object.
|
|
715
|
+
|
|
716
|
+
The database stores content as a dict containing content, categories, and notes.
|
|
717
|
+
This method extracts those fields and converts them back to the model format.
|
|
718
|
+
|
|
719
|
+
Args:
|
|
720
|
+
db_row (Dict[str, Any]): The database row as a dictionary.
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
CulturalKnowledge: The cultural knowledge object.
|
|
724
|
+
"""
|
|
725
|
+
# Extract content, categories, and notes from the content field
|
|
726
|
+
content_json = db_row.get("content", {}) or {}
|
|
727
|
+
|
|
728
|
+
return CulturalKnowledge.from_dict(
|
|
729
|
+
{
|
|
730
|
+
"id": db_row.get("id"),
|
|
731
|
+
"name": db_row.get("name"),
|
|
732
|
+
"summary": db_row.get("summary"),
|
|
733
|
+
"content": content_json.get("content"),
|
|
734
|
+
"categories": content_json.get("categories"),
|
|
735
|
+
"notes": content_json.get("notes"),
|
|
736
|
+
"metadata": db_row.get("metadata"),
|
|
737
|
+
"input": db_row.get("input"),
|
|
738
|
+
"created_at": db_row.get("created_at"),
|
|
739
|
+
"updated_at": db_row.get("updated_at"),
|
|
740
|
+
"agent_id": db_row.get("agent_id"),
|
|
741
|
+
"team_id": db_row.get("team_id"),
|
|
742
|
+
}
|
|
743
|
+
)
|