agno 1.8.1__py3-none-any.whl → 2.0.0a1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +19 -27
- agno/agent/agent.py +2778 -4123
- agno/api/agent.py +9 -65
- agno/api/api.py +5 -46
- agno/api/evals.py +6 -17
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -41
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +5 -21
- agno/api/schemas/evals.py +7 -16
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +5 -21
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +11 -7
- agno/api/settings.py +53 -0
- agno/api/team.py +9 -64
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/db/__init__.py +24 -0
- agno/db/base.py +245 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +1749 -0
- agno/db/dynamo/schemas.py +278 -0
- agno/db/dynamo/utils.py +684 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1438 -0
- agno/db/firestore/schemas.py +130 -0
- agno/db/firestore/utils.py +278 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1001 -0
- agno/db/gcs_json/utils.py +194 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +888 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1051 -0
- agno/db/json/utils.py +196 -0
- agno/db/migrations/v1_to_v2.py +162 -0
- agno/db/mongo/__init__.py +3 -0
- agno/db/mongo/mongo.py +1417 -0
- agno/db/mongo/schemas.py +77 -0
- agno/db/mongo/utils.py +204 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +1719 -0
- agno/db/mysql/schemas.py +124 -0
- agno/db/mysql/utils.py +298 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1720 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +281 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1371 -0
- agno/db/redis/schemas.py +109 -0
- agno/db/redis/utils.py +288 -0
- agno/db/schemas/__init__.py +3 -0
- agno/db/schemas/evals.py +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +116 -0
- agno/db/singlestore/singlestore.py +1722 -0
- agno/db/singlestore/utils.py +327 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1680 -0
- agno/db/sqlite/utils.py +269 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +142 -43
- agno/eval/performance.py +88 -23
- agno/eval/reliability.py +73 -20
- agno/eval/utils.py +23 -13
- agno/integrations/discord/__init__.py +3 -0
- agno/{app → integrations}/discord/client.py +10 -10
- agno/knowledge/__init__.py +2 -2
- agno/{document → knowledge}/chunking/agentic.py +2 -2
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +3 -3
- agno/{document → knowledge}/chunking/markdown.py +2 -2
- agno/{document → knowledge}/chunking/recursive.py +2 -2
- agno/{document → knowledge}/chunking/row.py +2 -2
- agno/knowledge/chunking/semantic.py +59 -0
- agno/knowledge/chunking/strategy.py +121 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
- agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
- agno/{embedder → knowledge/embedder}/base.py +6 -0
- agno/{embedder → knowledge/embedder}/cohere.py +72 -1
- agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/{embedder → knowledge/embedder}/google.py +74 -1
- agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
- agno/{embedder → knowledge/embedder}/jina.py +48 -2
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +139 -0
- agno/{embedder → knowledge/embedder}/nebius.py +1 -1
- agno/{embedder → knowledge/embedder}/ollama.py +54 -3
- agno/knowledge/embedder/openai.py +223 -0
- agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
- agno/knowledge/knowledge.py +1515 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
- agno/knowledge/reader/base.py +88 -0
- agno/{document → knowledge}/reader/csv_reader.py +68 -15
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/knowledge/reader/gcs_reader.py +67 -0
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +36 -9
- agno/{document → knowledge}/reader/pdf_reader.py +79 -21
- agno/knowledge/reader/reader_factory.py +275 -0
- agno/knowledge/reader/s3_reader.py +171 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +84 -0
- agno/knowledge/reader/web_search_reader.py +389 -0
- agno/{document → knowledge}/reader/website_reader.py +37 -10
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/{reranker → knowledge/reranker}/infinity.py +2 -2
- agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
- agno/knowledge/types.py +30 -0
- agno/knowledge/utils.py +169 -0
- agno/memory/__init__.py +2 -10
- agno/memory/manager.py +1003 -148
- agno/models/aimlapi/__init__.py +2 -2
- agno/models/aimlapi/aimlapi.py +6 -6
- agno/models/anthropic/claude.py +129 -82
- agno/models/aws/bedrock.py +107 -175
- agno/models/aws/claude.py +64 -18
- agno/models/azure/ai_foundry.py +73 -23
- agno/models/base.py +347 -287
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +100 -42
- agno/models/groq/groq.py +97 -35
- agno/models/huggingface/huggingface.py +92 -27
- agno/models/ibm/watsonx.py +72 -13
- agno/models/litellm/chat.py +85 -13
- agno/models/message.py +38 -144
- agno/models/meta/llama.py +85 -49
- agno/models/metrics.py +120 -0
- agno/models/mistral/mistral.py +90 -21
- agno/models/ollama/__init__.py +0 -2
- agno/models/ollama/chat.py +84 -46
- agno/models/openai/chat.py +121 -23
- agno/models/openai/responses.py +178 -105
- agno/models/perplexity/perplexity.py +26 -2
- agno/models/portkey/portkey.py +0 -7
- agno/models/response.py +14 -8
- agno/models/utils.py +20 -0
- agno/models/vercel/__init__.py +2 -2
- agno/models/vercel/v0.py +1 -1
- agno/models/vllm/__init__.py +2 -2
- agno/models/vllm/vllm.py +3 -3
- agno/models/xai/xai.py +10 -10
- agno/os/__init__.py +3 -0
- agno/os/app.py +393 -0
- agno/os/auth.py +47 -0
- agno/os/config.py +103 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +31 -0
- agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
- agno/{app → os/interfaces}/agui/utils.py +65 -28
- agno/os/interfaces/base.py +21 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
- agno/os/interfaces/slack/slack.py +33 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
- agno/os/interfaces/whatsapp/whatsapp.py +30 -0
- agno/os/router.py +843 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +204 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +161 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +413 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +179 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +58 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +163 -0
- agno/os/schema.py +892 -0
- agno/{app/playground → os}/settings.py +8 -15
- agno/os/utils.py +270 -0
- agno/reasoning/azure_ai_foundry.py +4 -4
- agno/reasoning/deepseek.py +4 -4
- agno/reasoning/default.py +6 -11
- agno/reasoning/groq.py +4 -4
- agno/reasoning/helpers.py +4 -6
- agno/reasoning/ollama.py +4 -4
- agno/reasoning/openai.py +4 -4
- agno/run/{response.py → agent.py} +144 -72
- agno/run/base.py +44 -58
- agno/run/cancel.py +83 -0
- agno/run/team.py +133 -77
- agno/run/workflow.py +537 -12
- agno/session/__init__.py +10 -0
- agno/session/agent.py +244 -0
- agno/session/summary.py +225 -0
- agno/session/team.py +262 -0
- agno/{storage/session/v2 → session}/workflow.py +47 -24
- agno/team/__init__.py +15 -16
- agno/team/team.py +2961 -4253
- agno/tools/agentql.py +14 -5
- agno/tools/airflow.py +9 -4
- agno/tools/api.py +7 -3
- agno/tools/apify.py +2 -46
- agno/tools/arxiv.py +8 -3
- agno/tools/aws_lambda.py +7 -5
- agno/tools/aws_ses.py +7 -1
- agno/tools/baidusearch.py +4 -1
- agno/tools/bitbucket.py +4 -4
- agno/tools/brandfetch.py +14 -11
- agno/tools/bravesearch.py +4 -1
- agno/tools/brightdata.py +42 -22
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +18 -13
- agno/tools/{clickup_tool.py → clickup.py} +12 -25
- agno/tools/confluence.py +8 -8
- agno/tools/crawl4ai.py +7 -1
- agno/tools/csv_toolkit.py +9 -8
- agno/tools/dalle.py +18 -11
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +16 -7
- agno/tools/discord.py +11 -8
- agno/tools/docker.py +30 -42
- agno/tools/duckdb.py +34 -53
- agno/tools/duckduckgo.py +8 -7
- agno/tools/e2b.py +61 -61
- agno/tools/eleven_labs.py +35 -28
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +29 -29
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +68 -17
- agno/tools/giphy.py +22 -10
- agno/tools/github.py +48 -126
- agno/tools/gmail.py +45 -61
- agno/tools/google_bigquery.py +7 -6
- agno/tools/google_maps.py +11 -26
- agno/tools/googlesearch.py +7 -2
- agno/tools/googlesheets.py +21 -17
- agno/tools/hackernews.py +9 -5
- agno/tools/jina.py +5 -4
- agno/tools/jira.py +18 -9
- agno/tools/knowledge.py +31 -32
- agno/tools/linear.py +18 -33
- agno/tools/linkup.py +5 -1
- agno/tools/local_file_system.py +8 -5
- agno/tools/lumalab.py +31 -19
- agno/tools/mem0.py +18 -12
- agno/tools/memori.py +14 -10
- agno/tools/mlx_transcribe.py +3 -2
- agno/tools/models/azure_openai.py +32 -14
- agno/tools/models/gemini.py +58 -31
- agno/tools/models/groq.py +29 -20
- agno/tools/models/nebius.py +27 -11
- agno/tools/models_labs.py +39 -15
- agno/tools/moviepy_video.py +7 -6
- agno/tools/neo4j.py +10 -8
- agno/tools/newspaper.py +7 -2
- agno/tools/newspaper4k.py +8 -3
- agno/tools/openai.py +57 -26
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +62 -46
- agno/tools/openweather.py +14 -12
- agno/tools/pandas.py +11 -3
- agno/tools/postgres.py +4 -12
- agno/tools/pubmed.py +4 -1
- agno/tools/python.py +9 -22
- agno/tools/reasoning.py +35 -27
- agno/tools/reddit.py +11 -26
- agno/tools/replicate.py +54 -41
- agno/tools/resend.py +4 -1
- agno/tools/scrapegraph.py +15 -14
- agno/tools/searxng.py +10 -23
- agno/tools/serpapi.py +6 -3
- agno/tools/serper.py +13 -4
- agno/tools/shell.py +9 -2
- agno/tools/slack.py +12 -11
- agno/tools/sleep.py +3 -2
- agno/tools/spider.py +24 -4
- agno/tools/sql.py +7 -6
- agno/tools/tavily.py +6 -4
- agno/tools/telegram.py +12 -4
- agno/tools/todoist.py +11 -31
- agno/tools/toolkit.py +1 -1
- agno/tools/trafilatura.py +22 -6
- agno/tools/trello.py +9 -22
- agno/tools/twilio.py +10 -3
- agno/tools/user_control_flow.py +6 -1
- agno/tools/valyu.py +34 -5
- agno/tools/visualization.py +19 -28
- agno/tools/webbrowser.py +4 -3
- agno/tools/webex.py +11 -7
- agno/tools/website.py +15 -46
- agno/tools/webtools.py +12 -4
- agno/tools/whatsapp.py +5 -9
- agno/tools/wikipedia.py +20 -13
- agno/tools/x.py +14 -13
- agno/tools/yfinance.py +13 -40
- agno/tools/youtube.py +26 -20
- agno/tools/zendesk.py +7 -2
- agno/tools/zep.py +10 -7
- agno/tools/zoom.py +10 -9
- agno/utils/common.py +1 -19
- agno/utils/events.py +95 -118
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +2 -2
- agno/utils/mcp.py +11 -5
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/claude.py +6 -4
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1565 -0
- agno/utils/print_response/workflow.py +1451 -0
- agno/utils/prompts.py +14 -14
- agno/utils/reasoning.py +87 -0
- agno/utils/response.py +42 -42
- agno/utils/string.py +8 -22
- agno/utils/team.py +50 -0
- agno/utils/timer.py +2 -2
- agno/vectordb/base.py +33 -21
- agno/vectordb/cassandra/cassandra.py +287 -23
- agno/vectordb/chroma/chromadb.py +482 -59
- agno/vectordb/clickhouse/clickhousedb.py +270 -63
- agno/vectordb/couchbase/couchbase.py +309 -29
- agno/vectordb/lancedb/lance_db.py +360 -21
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +145 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +374 -0
- agno/vectordb/llamaindex/llamaindexdb.py +127 -0
- agno/vectordb/milvus/milvus.py +242 -32
- agno/vectordb/mongodb/mongodb.py +200 -24
- agno/vectordb/pgvector/pgvector.py +319 -37
- agno/vectordb/pineconedb/pineconedb.py +221 -27
- agno/vectordb/qdrant/qdrant.py +334 -14
- agno/vectordb/singlestore/singlestore.py +286 -29
- agno/vectordb/surrealdb/surrealdb.py +187 -7
- agno/vectordb/upstashdb/upstashdb.py +342 -26
- agno/vectordb/weaviate/weaviate.py +227 -165
- agno/workflow/__init__.py +17 -13
- agno/workflow/{v2/condition.py → condition.py} +135 -32
- agno/workflow/{v2/loop.py → loop.py} +115 -28
- agno/workflow/{v2/parallel.py → parallel.py} +138 -108
- agno/workflow/{v2/router.py → router.py} +133 -32
- agno/workflow/{v2/step.py → step.py} +200 -42
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2394 -696
- agno-2.0.0a1.dist-info/METADATA +355 -0
- agno-2.0.0a1.dist-info/RECORD +514 -0
- agno/agent/metrics.py +0 -107
- agno/api/app.py +0 -35
- agno/api/playground.py +0 -92
- agno/api/schemas/app.py +0 -12
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -35
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workflows.py +0 -33
- agno/api/workspace.py +0 -175
- agno/app/agui/__init__.py +0 -3
- agno/app/agui/app.py +0 -17
- agno/app/agui/sync_router.py +0 -120
- agno/app/base.py +0 -186
- agno/app/discord/__init__.py +0 -3
- agno/app/fastapi/__init__.py +0 -3
- agno/app/fastapi/app.py +0 -107
- agno/app/fastapi/async_router.py +0 -457
- agno/app/fastapi/sync_router.py +0 -448
- agno/app/playground/app.py +0 -228
- agno/app/playground/async_router.py +0 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- agno/app/playground/utils.py +0 -46
- agno/app/settings.py +0 -15
- agno/app/slack/__init__.py +0 -3
- agno/app/slack/app.py +0 -19
- agno/app/slack/sync_router.py +0 -92
- agno/app/utils.py +0 -54
- agno/app/whatsapp/__init__.py +0 -3
- agno/app/whatsapp/app.py +0 -15
- agno/app/whatsapp/sync_router.py +0 -197
- agno/cli/auth_server.py +0 -249
- agno/cli/config.py +0 -274
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -357
- agno/cli/settings.py +0 -96
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -5
- agno/document/chunking/semantic.py +0 -45
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -5
- agno/document/reader/base.py +0 -47
- agno/document/reader/docx_reader.py +0 -60
- agno/document/reader/gcs/pdf_reader.py +0 -44
- agno/document/reader/s3/pdf_reader.py +0 -59
- agno/document/reader/s3/text_reader.py +0 -63
- agno/document/reader/url_reader.py +0 -59
- agno/document/reader/youtube_reader.py +0 -58
- agno/embedder/__init__.py +0 -5
- agno/embedder/langdb.py +0 -80
- agno/embedder/mistral.py +0 -82
- agno/embedder/openai.py +0 -78
- agno/file/__init__.py +0 -5
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -702
- agno/knowledge/arxiv.py +0 -33
- agno/knowledge/combined.py +0 -36
- agno/knowledge/csv.py +0 -144
- agno/knowledge/csv_url.py +0 -124
- agno/knowledge/document.py +0 -223
- agno/knowledge/docx.py +0 -137
- agno/knowledge/firecrawl.py +0 -34
- agno/knowledge/gcs/__init__.py +0 -0
- agno/knowledge/gcs/base.py +0 -39
- agno/knowledge/gcs/pdf.py +0 -125
- agno/knowledge/json.py +0 -137
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/light_rag.py +0 -273
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/markdown.py +0 -154
- agno/knowledge/pdf.py +0 -164
- agno/knowledge/pdf_bytes.py +0 -42
- agno/knowledge/pdf_url.py +0 -148
- agno/knowledge/s3/__init__.py +0 -0
- agno/knowledge/s3/base.py +0 -64
- agno/knowledge/s3/pdf.py +0 -33
- agno/knowledge/s3/text.py +0 -34
- agno/knowledge/text.py +0 -141
- agno/knowledge/url.py +0 -46
- agno/knowledge/website.py +0 -179
- agno/knowledge/wikipedia.py +0 -32
- agno/knowledge/youtube.py +0 -35
- agno/memory/agent.py +0 -423
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -5
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -22
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -201
- agno/memory/summary.py +0 -19
- agno/memory/team.py +0 -415
- agno/memory/v2/__init__.py +0 -2
- agno/memory/v2/db/__init__.py +0 -1
- agno/memory/v2/db/base.py +0 -42
- agno/memory/v2/db/firestore.py +0 -339
- agno/memory/v2/db/mongodb.py +0 -196
- agno/memory/v2/db/postgres.py +0 -214
- agno/memory/v2/db/redis.py +0 -187
- agno/memory/v2/db/schema.py +0 -54
- agno/memory/v2/db/sqlite.py +0 -209
- agno/memory/v2/manager.py +0 -437
- agno/memory/v2/memory.py +0 -1097
- agno/memory/v2/schema.py +0 -55
- agno/memory/v2/summarizer.py +0 -215
- agno/memory/workflow.py +0 -38
- agno/models/ollama/tools.py +0 -430
- agno/models/qwen/__init__.py +0 -5
- agno/playground/__init__.py +0 -10
- agno/playground/deploy.py +0 -3
- agno/playground/playground.py +0 -3
- agno/playground/serve.py +0 -3
- agno/playground/settings.py +0 -3
- agno/reranker/__init__.py +0 -0
- agno/run/v2/__init__.py +0 -0
- agno/run/v2/workflow.py +0 -567
- agno/storage/__init__.py +0 -0
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/dynamodb.py +0 -1
- agno/storage/agent/json.py +0 -1
- agno/storage/agent/mongodb.py +0 -1
- agno/storage/agent/postgres.py +0 -1
- agno/storage/agent/singlestore.py +0 -1
- agno/storage/agent/sqlite.py +0 -1
- agno/storage/agent/yaml.py +0 -1
- agno/storage/base.py +0 -60
- agno/storage/dynamodb.py +0 -673
- agno/storage/firestore.py +0 -297
- agno/storage/gcs_json.py +0 -261
- agno/storage/in_memory.py +0 -234
- agno/storage/json.py +0 -237
- agno/storage/mongodb.py +0 -328
- agno/storage/mysql.py +0 -685
- agno/storage/postgres.py +0 -682
- agno/storage/redis.py +0 -336
- agno/storage/session/__init__.py +0 -16
- agno/storage/session/agent.py +0 -64
- agno/storage/session/team.py +0 -63
- agno/storage/session/v2/__init__.py +0 -5
- agno/storage/session/workflow.py +0 -61
- agno/storage/singlestore.py +0 -606
- agno/storage/sqlite.py +0 -646
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/mongodb.py +0 -1
- agno/storage/workflow/postgres.py +0 -1
- agno/storage/workflow/sqlite.py +0 -1
- agno/storage/yaml.py +0 -241
- agno/tools/thinking.py +0 -73
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/workflow/v2/__init__.py +0 -21
- agno/workflow/v2/types.py +0 -357
- agno/workflow/v2/workflow.py +0 -3312
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -52
- agno/workspace/operator.py +0 -757
- agno/workspace/settings.py +0 -158
- agno-1.8.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- /agno/{app → db/migrations}/__init__.py +0 -0
- /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{cli → integrations}/__init__.py +0 -0
- /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
- /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
- /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
- /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
- /agno/{app → os/interfaces}/slack/security.py +0 -0
- /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
- /agno/{file/local → utils/print_response}/__init__.py +0 -0
- /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/memory/v2/memory.py
DELETED
|
@@ -1,1097 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from copy import deepcopy
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from os import getenv
|
|
6
|
-
from typing import Any, Dict, List, Literal, Optional, Type, Union
|
|
7
|
-
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
9
|
-
|
|
10
|
-
from agno.media import AudioArtifact, ImageArtifact, VideoArtifact
|
|
11
|
-
from agno.memory.v2.db.base import MemoryDb
|
|
12
|
-
from agno.memory.v2.db.schema import MemoryRow
|
|
13
|
-
from agno.memory.v2.manager import MemoryManager
|
|
14
|
-
from agno.memory.v2.schema import SessionSummary, UserMemory
|
|
15
|
-
from agno.memory.v2.summarizer import SessionSummarizer
|
|
16
|
-
from agno.models.base import Model
|
|
17
|
-
from agno.models.message import Message
|
|
18
|
-
from agno.run.base import RunStatus
|
|
19
|
-
from agno.run.response import RunResponse
|
|
20
|
-
from agno.run.team import TeamRunResponse
|
|
21
|
-
from agno.utils.log import log_debug, log_warning, logger, set_log_level_to_debug, set_log_level_to_info
|
|
22
|
-
from agno.utils.prompts import get_json_output_prompt
|
|
23
|
-
from agno.utils.string import parse_response_model_str
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
class MemorySearchResponse(BaseModel):
|
|
27
|
-
"""Model for Memory Search Response."""
|
|
28
|
-
|
|
29
|
-
memory_ids: List[str] = Field(
|
|
30
|
-
..., description="The IDs of the memories that are most semantically similar to the query."
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@dataclass
|
|
35
|
-
class TeamMemberInteraction:
|
|
36
|
-
member_name: str
|
|
37
|
-
task: str
|
|
38
|
-
response: Union[RunResponse, TeamRunResponse]
|
|
39
|
-
|
|
40
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
41
|
-
return {
|
|
42
|
-
"member_name": self.member_name,
|
|
43
|
-
"task": self.task,
|
|
44
|
-
"response": self.response.to_dict(),
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
@classmethod
|
|
48
|
-
def from_dict(cls, data: Dict[str, Any]) -> "TeamMemberInteraction":
|
|
49
|
-
if data["response"].get("agent_id"):
|
|
50
|
-
return cls(
|
|
51
|
-
member_name=data["member_name"], task=data["task"], response=RunResponse.from_dict(data["response"])
|
|
52
|
-
)
|
|
53
|
-
else:
|
|
54
|
-
return cls(
|
|
55
|
-
member_name=data["member_name"], task=data["task"], response=TeamRunResponse.from_dict(data["response"])
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@dataclass
|
|
60
|
-
class TeamContext:
|
|
61
|
-
# List of team member interaction, represented as a request and a response
|
|
62
|
-
member_interactions: List[TeamMemberInteraction] = field(default_factory=list)
|
|
63
|
-
text: Optional[str] = None
|
|
64
|
-
|
|
65
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
66
|
-
return {
|
|
67
|
-
"member_interactions": [interaction.to_dict() for interaction in self.member_interactions],
|
|
68
|
-
"text": self.text,
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@classmethod
|
|
72
|
-
def from_dict(cls, data: Dict[str, Any]) -> "TeamContext":
|
|
73
|
-
return cls(
|
|
74
|
-
member_interactions=[
|
|
75
|
-
TeamMemberInteraction.from_dict(interaction) for interaction in data["member_interactions"]
|
|
76
|
-
],
|
|
77
|
-
text=data["text"],
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@dataclass
|
|
82
|
-
class Memory:
|
|
83
|
-
# Model used for memories and summaries
|
|
84
|
-
model: Optional[Model] = None
|
|
85
|
-
|
|
86
|
-
# Memories per memory ID per user
|
|
87
|
-
memories: Optional[Dict[str, Dict[str, UserMemory]]] = None
|
|
88
|
-
# Manager to manage memories
|
|
89
|
-
memory_manager: Optional[MemoryManager] = None
|
|
90
|
-
|
|
91
|
-
# Session summaries per session per user
|
|
92
|
-
summaries: Optional[Dict[str, Dict[str, SessionSummary]]] = None
|
|
93
|
-
# Summarizer to generate session summaries
|
|
94
|
-
summary_manager: Optional[SessionSummarizer] = None
|
|
95
|
-
|
|
96
|
-
db: Optional[MemoryDb] = None
|
|
97
|
-
|
|
98
|
-
# runs per session
|
|
99
|
-
runs: Optional[Dict[str, List[Union[RunResponse, TeamRunResponse]]]] = None
|
|
100
|
-
|
|
101
|
-
# Team context per session
|
|
102
|
-
team_context: Optional[Dict[str, TeamContext]] = None
|
|
103
|
-
|
|
104
|
-
# Whether to delete memories
|
|
105
|
-
delete_memories: bool = False
|
|
106
|
-
# Whether to clear memories
|
|
107
|
-
clear_memories: bool = False
|
|
108
|
-
|
|
109
|
-
debug_mode: bool = False
|
|
110
|
-
version: int = 2
|
|
111
|
-
|
|
112
|
-
def __init__(
|
|
113
|
-
self,
|
|
114
|
-
model: Optional[Model] = None,
|
|
115
|
-
memory_manager: Optional[MemoryManager] = None,
|
|
116
|
-
summarizer: Optional[SessionSummarizer] = None,
|
|
117
|
-
db: Optional[MemoryDb] = None,
|
|
118
|
-
memories: Optional[Dict[str, Dict[str, UserMemory]]] = None,
|
|
119
|
-
summaries: Optional[Dict[str, Dict[str, SessionSummary]]] = None,
|
|
120
|
-
runs: Optional[Dict[str, List[Union[RunResponse, TeamRunResponse]]]] = None,
|
|
121
|
-
debug_mode: bool = False,
|
|
122
|
-
delete_memories: bool = False,
|
|
123
|
-
clear_memories: bool = False,
|
|
124
|
-
):
|
|
125
|
-
self.memories = memories or {}
|
|
126
|
-
self.summaries = summaries or {}
|
|
127
|
-
self.runs = runs or {}
|
|
128
|
-
|
|
129
|
-
self.debug_mode = debug_mode
|
|
130
|
-
|
|
131
|
-
self.delete_memories = delete_memories
|
|
132
|
-
self.clear_memories = clear_memories
|
|
133
|
-
|
|
134
|
-
self.model = model
|
|
135
|
-
|
|
136
|
-
if self.model is not None and isinstance(self.model, str):
|
|
137
|
-
raise ValueError("Model must be a Model object, not a string")
|
|
138
|
-
|
|
139
|
-
self.memory_manager = memory_manager
|
|
140
|
-
|
|
141
|
-
self.summary_manager = summarizer
|
|
142
|
-
|
|
143
|
-
self.db = db
|
|
144
|
-
|
|
145
|
-
# We are making memories
|
|
146
|
-
if self.model is not None:
|
|
147
|
-
if self.memory_manager is None:
|
|
148
|
-
self.memory_manager = MemoryManager(model=deepcopy(self.model))
|
|
149
|
-
# Set the model on the memory manager if it is not set
|
|
150
|
-
if self.memory_manager.model is None:
|
|
151
|
-
self.memory_manager.model = deepcopy(self.model)
|
|
152
|
-
|
|
153
|
-
# We are making session summaries
|
|
154
|
-
if self.model is not None:
|
|
155
|
-
if self.summary_manager is None:
|
|
156
|
-
self.summary_manager = SessionSummarizer(model=deepcopy(self.model))
|
|
157
|
-
# Set the model on the summary_manager if it is not set
|
|
158
|
-
elif self.summary_manager.model is None:
|
|
159
|
-
self.summary_manager.model = deepcopy(self.model)
|
|
160
|
-
|
|
161
|
-
self.debug_mode = debug_mode
|
|
162
|
-
|
|
163
|
-
def set_model(self, model: Model) -> None:
|
|
164
|
-
if self.memory_manager is None:
|
|
165
|
-
self.memory_manager = MemoryManager(model=deepcopy(model))
|
|
166
|
-
if self.memory_manager.model is None:
|
|
167
|
-
self.memory_manager.model = deepcopy(model)
|
|
168
|
-
if self.summary_manager is None:
|
|
169
|
-
self.summary_manager = SessionSummarizer(model=deepcopy(model))
|
|
170
|
-
if self.summary_manager.model is None:
|
|
171
|
-
self.summary_manager.model = deepcopy(model)
|
|
172
|
-
|
|
173
|
-
def get_model(self) -> Model:
|
|
174
|
-
if self.model is None:
|
|
175
|
-
try:
|
|
176
|
-
from agno.models.openai import OpenAIChat
|
|
177
|
-
except ModuleNotFoundError as e:
|
|
178
|
-
logger.exception(e)
|
|
179
|
-
logger.error(
|
|
180
|
-
"Agno uses `openai` as the default model provider. Please provide a `model` or install `openai`."
|
|
181
|
-
)
|
|
182
|
-
exit(1)
|
|
183
|
-
self.model = OpenAIChat(id="gpt-4o")
|
|
184
|
-
return self.model
|
|
185
|
-
|
|
186
|
-
def refresh_from_db(self, user_id: Optional[str] = None):
|
|
187
|
-
if self.db:
|
|
188
|
-
# If no user_id is provided, read all memories
|
|
189
|
-
if user_id is None:
|
|
190
|
-
all_memories = self.db.read_memories()
|
|
191
|
-
else:
|
|
192
|
-
all_memories = self.db.read_memories(user_id=user_id)
|
|
193
|
-
|
|
194
|
-
# Reset the memories
|
|
195
|
-
self.memories = {}
|
|
196
|
-
for memory in all_memories:
|
|
197
|
-
if memory.user_id is not None and memory.id is not None:
|
|
198
|
-
self.memories.setdefault(memory.user_id, {})[memory.id] = UserMemory.from_dict(memory.memory)
|
|
199
|
-
|
|
200
|
-
def set_log_level(self):
|
|
201
|
-
if self.debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
|
|
202
|
-
self.debug_mode = True
|
|
203
|
-
set_log_level_to_debug()
|
|
204
|
-
else:
|
|
205
|
-
set_log_level_to_info()
|
|
206
|
-
|
|
207
|
-
def initialize(self, user_id: Optional[str] = None):
|
|
208
|
-
self.set_log_level()
|
|
209
|
-
self.refresh_from_db(user_id=user_id)
|
|
210
|
-
|
|
211
|
-
def to_dict(self) -> Dict[str, Any]:
|
|
212
|
-
_memory_dict = {}
|
|
213
|
-
# Add summary if it exists
|
|
214
|
-
if self.summaries is not None:
|
|
215
|
-
_memory_dict["summaries"] = {
|
|
216
|
-
user_id: {session_id: summary.to_dict() for session_id, summary in session_summaries.items()}
|
|
217
|
-
for user_id, session_summaries in self.summaries.items()
|
|
218
|
-
}
|
|
219
|
-
# Add memories if they exist
|
|
220
|
-
if self.memories is not None:
|
|
221
|
-
_memory_dict["memories"] = {
|
|
222
|
-
user_id: {memory_id: memory.to_dict() for memory_id, memory in user_memories.items()}
|
|
223
|
-
for user_id, user_memories in self.memories.items()
|
|
224
|
-
}
|
|
225
|
-
# Add runs if they exist
|
|
226
|
-
if self.runs is not None:
|
|
227
|
-
_memory_dict["runs"] = {}
|
|
228
|
-
for session_id, runs in self.runs.items():
|
|
229
|
-
if session_id is not None:
|
|
230
|
-
_memory_dict["runs"][session_id] = [run.to_dict() for run in runs] # type: ignore
|
|
231
|
-
|
|
232
|
-
if self.team_context is not None:
|
|
233
|
-
_memory_dict["team_context"] = {}
|
|
234
|
-
for session_id, team_context in self.team_context.items():
|
|
235
|
-
if session_id is not None:
|
|
236
|
-
_memory_dict["team_context"][session_id] = team_context.to_dict()
|
|
237
|
-
|
|
238
|
-
_memory_dict = {k: v for k, v in _memory_dict.items() if v is not None}
|
|
239
|
-
return _memory_dict
|
|
240
|
-
|
|
241
|
-
# -*- Public Functions
|
|
242
|
-
def get_user_memories(self, user_id: Optional[str] = None) -> List[UserMemory]:
|
|
243
|
-
"""Get the user memories for a given user id"""
|
|
244
|
-
if user_id is None:
|
|
245
|
-
user_id = "default"
|
|
246
|
-
|
|
247
|
-
self.refresh_from_db(user_id=user_id)
|
|
248
|
-
|
|
249
|
-
if self.memories is None:
|
|
250
|
-
return []
|
|
251
|
-
return list(self.memories.get(user_id, {}).values())
|
|
252
|
-
|
|
253
|
-
def get_session_summaries(self, user_id: Optional[str] = None) -> List[SessionSummary]:
|
|
254
|
-
"""Get the session summaries for a given user id"""
|
|
255
|
-
if user_id is None:
|
|
256
|
-
user_id = "default"
|
|
257
|
-
self.refresh_from_db(user_id=user_id)
|
|
258
|
-
if self.summaries is None:
|
|
259
|
-
return []
|
|
260
|
-
return list(self.summaries.get(user_id, {}).values())
|
|
261
|
-
|
|
262
|
-
def get_user_memory(self, memory_id: str, user_id: Optional[str] = None) -> Optional[UserMemory]:
|
|
263
|
-
"""Get the user memory for a given user id"""
|
|
264
|
-
if user_id is None:
|
|
265
|
-
user_id = "default"
|
|
266
|
-
self.refresh_from_db(user_id=user_id)
|
|
267
|
-
if self.memories is None:
|
|
268
|
-
return None
|
|
269
|
-
return self.memories.get(user_id, {}).get(memory_id, None)
|
|
270
|
-
|
|
271
|
-
def get_session_summary(self, session_id: str, user_id: Optional[str] = None) -> Optional[SessionSummary]:
|
|
272
|
-
"""Get the session summary for a given user id"""
|
|
273
|
-
if user_id is None:
|
|
274
|
-
user_id = "default"
|
|
275
|
-
if self.summaries is None:
|
|
276
|
-
return None
|
|
277
|
-
return self.summaries.get(user_id, {}).get(session_id, None)
|
|
278
|
-
|
|
279
|
-
def add_user_memory(
|
|
280
|
-
self,
|
|
281
|
-
memory: UserMemory,
|
|
282
|
-
user_id: Optional[str] = None,
|
|
283
|
-
refresh_from_db: bool = True,
|
|
284
|
-
) -> str:
|
|
285
|
-
"""Add a user memory for a given user id
|
|
286
|
-
Args:
|
|
287
|
-
memory (UserMemory): The memory to add
|
|
288
|
-
user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
|
|
289
|
-
Returns:
|
|
290
|
-
str: The id of the memory
|
|
291
|
-
"""
|
|
292
|
-
from uuid import uuid4
|
|
293
|
-
|
|
294
|
-
memory_id = memory.memory_id or str(uuid4())
|
|
295
|
-
if memory.memory_id is None:
|
|
296
|
-
memory.memory_id = memory_id
|
|
297
|
-
if user_id is None:
|
|
298
|
-
user_id = "default"
|
|
299
|
-
|
|
300
|
-
# Refresh from the DB
|
|
301
|
-
if refresh_from_db:
|
|
302
|
-
self.refresh_from_db(user_id=user_id)
|
|
303
|
-
|
|
304
|
-
if not memory.last_updated:
|
|
305
|
-
memory.last_updated = datetime.now()
|
|
306
|
-
|
|
307
|
-
self.memories.setdefault(user_id, {})[memory_id] = memory # type: ignore
|
|
308
|
-
if self.db:
|
|
309
|
-
self._upsert_db_memory(
|
|
310
|
-
memory=MemoryRow(
|
|
311
|
-
id=memory_id,
|
|
312
|
-
user_id=user_id,
|
|
313
|
-
memory=memory.to_dict(),
|
|
314
|
-
last_updated=memory.last_updated or datetime.now(),
|
|
315
|
-
)
|
|
316
|
-
)
|
|
317
|
-
|
|
318
|
-
return memory_id
|
|
319
|
-
|
|
320
|
-
def replace_user_memory(
|
|
321
|
-
self,
|
|
322
|
-
memory_id: str,
|
|
323
|
-
memory: UserMemory,
|
|
324
|
-
user_id: Optional[str] = None,
|
|
325
|
-
refresh_from_db: bool = True,
|
|
326
|
-
) -> Optional[str]:
|
|
327
|
-
"""Replace a user memory for a given user id
|
|
328
|
-
Args:
|
|
329
|
-
memory_id (str): The id of the memory to replace
|
|
330
|
-
memory (UserMemory): The memory to add
|
|
331
|
-
user_id (Optional[str]): The user id to add the memory to. If not provided, the memory is added to the "default" user.
|
|
332
|
-
Returns:
|
|
333
|
-
str: The id of the memory
|
|
334
|
-
"""
|
|
335
|
-
|
|
336
|
-
if user_id is None:
|
|
337
|
-
user_id = "default"
|
|
338
|
-
|
|
339
|
-
# Refresh from the DB
|
|
340
|
-
if refresh_from_db:
|
|
341
|
-
self.refresh_from_db(user_id=user_id)
|
|
342
|
-
|
|
343
|
-
if not memory.last_updated:
|
|
344
|
-
memory.last_updated = datetime.now()
|
|
345
|
-
|
|
346
|
-
if memory_id not in self.memories[user_id]: # type: ignore
|
|
347
|
-
log_warning(f"Memory {memory_id} not found for user {user_id}")
|
|
348
|
-
return None
|
|
349
|
-
|
|
350
|
-
self.memories.setdefault(user_id, {})[memory_id] = memory # type: ignore
|
|
351
|
-
if self.db:
|
|
352
|
-
self._upsert_db_memory(
|
|
353
|
-
memory=MemoryRow(
|
|
354
|
-
id=memory_id,
|
|
355
|
-
user_id=user_id,
|
|
356
|
-
memory=memory.to_dict(),
|
|
357
|
-
last_updated=memory.last_updated or datetime.now(),
|
|
358
|
-
)
|
|
359
|
-
)
|
|
360
|
-
|
|
361
|
-
return memory_id
|
|
362
|
-
|
|
363
|
-
def delete_user_memory(
|
|
364
|
-
self,
|
|
365
|
-
memory_id: str,
|
|
366
|
-
user_id: Optional[str] = None,
|
|
367
|
-
refresh_from_db: bool = True,
|
|
368
|
-
) -> None:
|
|
369
|
-
"""Delete a user memory for a given user id
|
|
370
|
-
Args:
|
|
371
|
-
memory_id (str): The id of the memory to delete
|
|
372
|
-
user_id (Optional[str]): The user id to delete the memory from. If not provided, the memory is deleted from the "default" user.
|
|
373
|
-
"""
|
|
374
|
-
if user_id is None:
|
|
375
|
-
user_id = "default"
|
|
376
|
-
|
|
377
|
-
# Refresh from the DB
|
|
378
|
-
if refresh_from_db:
|
|
379
|
-
self.refresh_from_db(user_id=user_id)
|
|
380
|
-
|
|
381
|
-
if memory_id not in self.memories[user_id]: # type: ignore
|
|
382
|
-
log_warning(f"Memory {memory_id} not found for user {user_id}")
|
|
383
|
-
return None
|
|
384
|
-
|
|
385
|
-
del self.memories[user_id][memory_id] # type: ignore
|
|
386
|
-
if self.db:
|
|
387
|
-
self._delete_db_memory(memory_id=memory_id)
|
|
388
|
-
|
|
389
|
-
def delete_session_summary(self, user_id: str, session_id: str) -> None:
|
|
390
|
-
"""Delete a session summary for a given user id
|
|
391
|
-
Args:
|
|
392
|
-
user_id (str): The user id to delete the memory from
|
|
393
|
-
session_id (str): The id of the session to delete
|
|
394
|
-
"""
|
|
395
|
-
del self.summaries[user_id][session_id] # type: ignore
|
|
396
|
-
|
|
397
|
-
def get_runs(self, session_id: str) -> List[Union[RunResponse, TeamRunResponse]]:
|
|
398
|
-
"""Get all runs for a given session id"""
|
|
399
|
-
if self.runs is None:
|
|
400
|
-
return []
|
|
401
|
-
return self.runs.get(session_id, [])
|
|
402
|
-
|
|
403
|
-
# -*- Agent Functions
|
|
404
|
-
def create_session_summary(self, session_id: str, user_id: Optional[str] = None) -> Optional[SessionSummary]:
|
|
405
|
-
"""Creates a summary of the session"""
|
|
406
|
-
|
|
407
|
-
if not self.summary_manager:
|
|
408
|
-
raise ValueError("Summarizer not initialized")
|
|
409
|
-
|
|
410
|
-
self.set_log_level()
|
|
411
|
-
|
|
412
|
-
if user_id is None:
|
|
413
|
-
user_id = "default"
|
|
414
|
-
|
|
415
|
-
summary_response = self.summary_manager.run(conversation=self.get_messages_for_session(session_id=session_id))
|
|
416
|
-
if summary_response is None:
|
|
417
|
-
return None
|
|
418
|
-
session_summary = SessionSummary(
|
|
419
|
-
summary=summary_response.summary, topics=summary_response.topics, last_updated=datetime.now()
|
|
420
|
-
)
|
|
421
|
-
self.summaries.setdefault(user_id, {})[session_id] = session_summary # type: ignore
|
|
422
|
-
|
|
423
|
-
return session_summary
|
|
424
|
-
|
|
425
|
-
async def acreate_session_summary(self, session_id: str, user_id: Optional[str] = None) -> Optional[SessionSummary]:
|
|
426
|
-
"""Creates a summary of the session"""
|
|
427
|
-
if not self.summary_manager:
|
|
428
|
-
raise ValueError("Summarizer not initialized")
|
|
429
|
-
|
|
430
|
-
self.set_log_level()
|
|
431
|
-
|
|
432
|
-
if user_id is None:
|
|
433
|
-
user_id = "default"
|
|
434
|
-
|
|
435
|
-
summary_response = await self.summary_manager.arun(
|
|
436
|
-
conversation=self.get_messages_for_session(session_id=session_id)
|
|
437
|
-
)
|
|
438
|
-
if summary_response is None:
|
|
439
|
-
return None
|
|
440
|
-
session_summary = SessionSummary(
|
|
441
|
-
summary=summary_response.summary, topics=summary_response.topics, last_updated=datetime.now()
|
|
442
|
-
)
|
|
443
|
-
self.summaries.setdefault(user_id, {})[session_id] = session_summary # type: ignore
|
|
444
|
-
return session_summary
|
|
445
|
-
|
|
446
|
-
def create_user_memories(
|
|
447
|
-
self,
|
|
448
|
-
message: Optional[str] = None,
|
|
449
|
-
messages: Optional[List[Message]] = None,
|
|
450
|
-
user_id: Optional[str] = None,
|
|
451
|
-
refresh_from_db: bool = True,
|
|
452
|
-
) -> str:
|
|
453
|
-
"""Creates memories from multiple messages and adds them to the memory db."""
|
|
454
|
-
self.set_log_level()
|
|
455
|
-
if not messages and not message:
|
|
456
|
-
raise ValueError("You must provide either a message or a list of messages")
|
|
457
|
-
|
|
458
|
-
if message:
|
|
459
|
-
messages = [Message(role="user", content=message)]
|
|
460
|
-
|
|
461
|
-
if not messages or not isinstance(messages, list):
|
|
462
|
-
raise ValueError("Invalid messages list")
|
|
463
|
-
|
|
464
|
-
if not self.memory_manager:
|
|
465
|
-
raise ValueError("Memory manager not initialized")
|
|
466
|
-
|
|
467
|
-
if self.db is None:
|
|
468
|
-
log_warning("MemoryDb not provided.")
|
|
469
|
-
return "Please provide a db to store memories"
|
|
470
|
-
|
|
471
|
-
if user_id is None:
|
|
472
|
-
user_id = "default"
|
|
473
|
-
|
|
474
|
-
if refresh_from_db:
|
|
475
|
-
self.refresh_from_db(user_id=user_id)
|
|
476
|
-
|
|
477
|
-
existing_memories = self.memories.get(user_id, {}) # type: ignore
|
|
478
|
-
existing_memories = [
|
|
479
|
-
{"memory_id": memory_id, "memory": memory.memory} for memory_id, memory in existing_memories.items()
|
|
480
|
-
]
|
|
481
|
-
response = self.memory_manager.create_or_update_memories( # type: ignore
|
|
482
|
-
messages=messages,
|
|
483
|
-
existing_memories=existing_memories,
|
|
484
|
-
user_id=user_id,
|
|
485
|
-
db=self.db,
|
|
486
|
-
delete_memories=self.delete_memories,
|
|
487
|
-
clear_memories=self.clear_memories,
|
|
488
|
-
)
|
|
489
|
-
|
|
490
|
-
# We refresh from the DB
|
|
491
|
-
self.refresh_from_db(user_id=user_id)
|
|
492
|
-
return response
|
|
493
|
-
|
|
494
|
-
async def acreate_user_memories(
|
|
495
|
-
self,
|
|
496
|
-
message: Optional[str] = None,
|
|
497
|
-
messages: Optional[List[Message]] = None,
|
|
498
|
-
user_id: Optional[str] = None,
|
|
499
|
-
refresh_from_db: bool = True,
|
|
500
|
-
) -> str:
|
|
501
|
-
"""Creates memories from multiple messages and adds them to the memory db."""
|
|
502
|
-
self.set_log_level()
|
|
503
|
-
if not messages and not message:
|
|
504
|
-
raise ValueError("You must provide either a message or a list of messages")
|
|
505
|
-
|
|
506
|
-
if message:
|
|
507
|
-
messages = [Message(role="user", content=message)]
|
|
508
|
-
|
|
509
|
-
if not messages or not isinstance(messages, list):
|
|
510
|
-
raise ValueError("Invalid messages list")
|
|
511
|
-
|
|
512
|
-
if not self.memory_manager:
|
|
513
|
-
raise ValueError("Memory manager not initialized")
|
|
514
|
-
|
|
515
|
-
if self.db is None:
|
|
516
|
-
log_warning("MemoryDb not provided.")
|
|
517
|
-
return "Please provide a db to store memories"
|
|
518
|
-
|
|
519
|
-
if user_id is None:
|
|
520
|
-
user_id = "default"
|
|
521
|
-
|
|
522
|
-
if refresh_from_db:
|
|
523
|
-
self.refresh_from_db(user_id=user_id)
|
|
524
|
-
|
|
525
|
-
existing_memories = self.memories.get(user_id, {}) # type: ignore
|
|
526
|
-
existing_memories = [
|
|
527
|
-
{"memory_id": memory_id, "memory": memory.memory} for memory_id, memory in existing_memories.items()
|
|
528
|
-
]
|
|
529
|
-
|
|
530
|
-
response = await self.memory_manager.acreate_or_update_memories( # type: ignore
|
|
531
|
-
messages=messages,
|
|
532
|
-
existing_memories=existing_memories,
|
|
533
|
-
user_id=user_id,
|
|
534
|
-
db=self.db,
|
|
535
|
-
delete_memories=self.delete_memories,
|
|
536
|
-
clear_memories=self.clear_memories,
|
|
537
|
-
)
|
|
538
|
-
|
|
539
|
-
# We refresh from the DB
|
|
540
|
-
self.refresh_from_db()
|
|
541
|
-
|
|
542
|
-
return response
|
|
543
|
-
|
|
544
|
-
def update_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
|
|
545
|
-
"""Updates the memory with a task"""
|
|
546
|
-
if not self.memory_manager:
|
|
547
|
-
raise ValueError("Memory manager not initialized")
|
|
548
|
-
|
|
549
|
-
if not self.db:
|
|
550
|
-
log_warning("MemoryDb not provided.")
|
|
551
|
-
return "Please provide a db to store memories"
|
|
552
|
-
|
|
553
|
-
if user_id is None:
|
|
554
|
-
user_id = "default"
|
|
555
|
-
|
|
556
|
-
self.refresh_from_db(user_id=user_id)
|
|
557
|
-
|
|
558
|
-
existing_memories = self.memories.get(user_id, {}) # type: ignore
|
|
559
|
-
existing_memories = [
|
|
560
|
-
{"memory_id": memory_id, "memory": memory.memory} for memory_id, memory in existing_memories.items()
|
|
561
|
-
]
|
|
562
|
-
# The memory manager updates the DB directly
|
|
563
|
-
response = self.memory_manager.run_memory_task( # type: ignore
|
|
564
|
-
task=task,
|
|
565
|
-
existing_memories=existing_memories,
|
|
566
|
-
user_id=user_id,
|
|
567
|
-
db=self.db,
|
|
568
|
-
delete_memories=self.delete_memories,
|
|
569
|
-
clear_memories=self.clear_memories,
|
|
570
|
-
)
|
|
571
|
-
|
|
572
|
-
# We refresh from the DB
|
|
573
|
-
self.refresh_from_db(user_id=user_id)
|
|
574
|
-
|
|
575
|
-
return response
|
|
576
|
-
|
|
577
|
-
async def aupdate_memory_task(self, task: str, user_id: Optional[str] = None) -> str:
|
|
578
|
-
"""Updates the memory with a task"""
|
|
579
|
-
self.set_log_level()
|
|
580
|
-
if not self.memory_manager:
|
|
581
|
-
raise ValueError("Memory manager not initialized")
|
|
582
|
-
|
|
583
|
-
if not self.db:
|
|
584
|
-
log_warning("MemoryDb not provided.")
|
|
585
|
-
return "Please provide a db to store memories"
|
|
586
|
-
|
|
587
|
-
if user_id is None:
|
|
588
|
-
user_id = "default"
|
|
589
|
-
|
|
590
|
-
self.refresh_from_db(user_id=user_id)
|
|
591
|
-
|
|
592
|
-
existing_memories = self.memories.get(user_id, {}) # type: ignore
|
|
593
|
-
existing_memories = [
|
|
594
|
-
{"memory_id": memory_id, "memory": memory.memory} for memory_id, memory in existing_memories.items()
|
|
595
|
-
]
|
|
596
|
-
# The memory manager updates the DB directly
|
|
597
|
-
response = await self.memory_manager.arun_memory_task( # type: ignore
|
|
598
|
-
task=task,
|
|
599
|
-
existing_memories=existing_memories,
|
|
600
|
-
user_id=user_id,
|
|
601
|
-
db=self.db,
|
|
602
|
-
delete_memories=self.delete_memories,
|
|
603
|
-
clear_memories=self.clear_memories,
|
|
604
|
-
)
|
|
605
|
-
|
|
606
|
-
# We refresh from the DB
|
|
607
|
-
self.refresh_from_db(user_id=user_id)
|
|
608
|
-
|
|
609
|
-
return response
|
|
610
|
-
|
|
611
|
-
# -*- DB Functions
|
|
612
|
-
def _upsert_db_memory(self, memory: MemoryRow) -> str:
|
|
613
|
-
"""Use this function to add a memory to the database."""
|
|
614
|
-
try:
|
|
615
|
-
if not self.db:
|
|
616
|
-
raise ValueError("Memory db not initialized")
|
|
617
|
-
self.db.upsert_memory(memory)
|
|
618
|
-
return "Memory added successfully"
|
|
619
|
-
except Exception as e:
|
|
620
|
-
logger.warning(f"Error storing memory in db: {e}")
|
|
621
|
-
return f"Error adding memory: {e}"
|
|
622
|
-
|
|
623
|
-
def _delete_db_memory(self, memory_id: str) -> str:
|
|
624
|
-
"""Use this function to delete a memory from the database."""
|
|
625
|
-
try:
|
|
626
|
-
if not self.db:
|
|
627
|
-
raise ValueError("Memory db not initialized")
|
|
628
|
-
self.db.delete_memory(memory_id=memory_id)
|
|
629
|
-
return "Memory deleted successfully"
|
|
630
|
-
except Exception as e:
|
|
631
|
-
logger.warning(f"Error deleting memory in db: {e}")
|
|
632
|
-
return f"Error deleting memory: {e}"
|
|
633
|
-
|
|
634
|
-
# -*- Utility Functions
|
|
635
|
-
def get_messages_for_session(
|
|
636
|
-
self,
|
|
637
|
-
session_id: str,
|
|
638
|
-
user_role: str = "user",
|
|
639
|
-
assistant_role: Optional[List[str]] = None,
|
|
640
|
-
skip_history_messages: bool = True,
|
|
641
|
-
) -> List[Message]:
|
|
642
|
-
"""Returns a list of messages for the session that iterate through user message and assistant response."""
|
|
643
|
-
|
|
644
|
-
if assistant_role is None:
|
|
645
|
-
assistant_role = ["assistant", "model", "CHATBOT"]
|
|
646
|
-
|
|
647
|
-
final_messages: List[Message] = []
|
|
648
|
-
session_runs = self.runs.get(session_id, []) if self.runs else []
|
|
649
|
-
for run_response in session_runs:
|
|
650
|
-
if run_response and run_response.messages:
|
|
651
|
-
user_message_from_run = None
|
|
652
|
-
assistant_message_from_run = None
|
|
653
|
-
|
|
654
|
-
# Start from the beginning to look for the user message
|
|
655
|
-
for message in run_response.messages:
|
|
656
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
657
|
-
continue
|
|
658
|
-
if message.role == user_role:
|
|
659
|
-
user_message_from_run = message
|
|
660
|
-
break
|
|
661
|
-
|
|
662
|
-
# Start from the end to look for the assistant response
|
|
663
|
-
for message in run_response.messages[::-1]:
|
|
664
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
665
|
-
continue
|
|
666
|
-
if message.role in assistant_role:
|
|
667
|
-
assistant_message_from_run = message
|
|
668
|
-
break
|
|
669
|
-
|
|
670
|
-
if user_message_from_run and assistant_message_from_run:
|
|
671
|
-
final_messages.append(user_message_from_run)
|
|
672
|
-
final_messages.append(assistant_message_from_run)
|
|
673
|
-
return final_messages
|
|
674
|
-
|
|
675
|
-
def add_run(self, session_id: str, run: Union[RunResponse, TeamRunResponse]) -> None:
|
|
676
|
-
"""Adds a RunResponse to the runs list."""
|
|
677
|
-
if not self.runs:
|
|
678
|
-
self.runs = {}
|
|
679
|
-
|
|
680
|
-
if session_id not in self.runs:
|
|
681
|
-
self.runs[session_id] = []
|
|
682
|
-
|
|
683
|
-
# Check if run already exists with the same run_id
|
|
684
|
-
if hasattr(run, "run_id") and run.run_id:
|
|
685
|
-
run_id = run.run_id
|
|
686
|
-
# Look for existing run with same ID
|
|
687
|
-
for i, existing_run in enumerate(self.runs[session_id]):
|
|
688
|
-
if hasattr(existing_run, "run_id") and existing_run.run_id == run_id:
|
|
689
|
-
# Replace existing run
|
|
690
|
-
self.runs[session_id][i] = run
|
|
691
|
-
log_debug(f"Replaced existing run with run_id {run_id} in memory")
|
|
692
|
-
return
|
|
693
|
-
|
|
694
|
-
self.runs[session_id].append(run)
|
|
695
|
-
log_debug("Added RunResponse to Memory")
|
|
696
|
-
|
|
697
|
-
def get_messages_from_last_n_runs(
|
|
698
|
-
self,
|
|
699
|
-
session_id: str,
|
|
700
|
-
agent_id: Optional[str] = None,
|
|
701
|
-
team_id: Optional[str] = None,
|
|
702
|
-
last_n: Optional[int] = None,
|
|
703
|
-
skip_role: Optional[str] = None,
|
|
704
|
-
skip_status: Optional[List[RunStatus]] = None,
|
|
705
|
-
skip_history_messages: bool = True,
|
|
706
|
-
) -> List[Message]:
|
|
707
|
-
"""Returns the messages from the last_n runs, excluding previously tagged history messages.
|
|
708
|
-
Args:
|
|
709
|
-
session_id: The session id to get the messages from.
|
|
710
|
-
agent_id: The id of the agent to get the messages from.
|
|
711
|
-
team_id: The id of the team to get the messages from.
|
|
712
|
-
last_n: The number of runs to return from the end of the conversation. Defaults to all runs.
|
|
713
|
-
skip_role: Skip messages with this role.
|
|
714
|
-
skip_status: Skip messages with this status.
|
|
715
|
-
skip_history_messages: Skip messages that were tagged as history in previous runs.
|
|
716
|
-
Returns:
|
|
717
|
-
A list of Messages from the specified runs, excluding history messages.
|
|
718
|
-
"""
|
|
719
|
-
if not self.runs:
|
|
720
|
-
return []
|
|
721
|
-
|
|
722
|
-
if skip_status is None:
|
|
723
|
-
skip_status = [RunStatus.paused, RunStatus.cancelled, RunStatus.error]
|
|
724
|
-
|
|
725
|
-
session_runs = self.runs.get(session_id, [])
|
|
726
|
-
# Filter by agent_id and team_id
|
|
727
|
-
if agent_id:
|
|
728
|
-
session_runs = [run for run in session_runs if hasattr(run, "agent_id") and run.agent_id == agent_id] # type: ignore
|
|
729
|
-
if team_id:
|
|
730
|
-
session_runs = [run for run in session_runs if hasattr(run, "team_id") and run.team_id == team_id] # type: ignore
|
|
731
|
-
|
|
732
|
-
# Filter by status
|
|
733
|
-
session_runs = [run for run in session_runs if hasattr(run, "status") and run.status not in skip_status] # type: ignore
|
|
734
|
-
|
|
735
|
-
# Filter by last_n
|
|
736
|
-
runs_to_process = session_runs[-last_n:] if last_n is not None else session_runs
|
|
737
|
-
messages_from_history = []
|
|
738
|
-
system_message = None
|
|
739
|
-
for run_response in runs_to_process:
|
|
740
|
-
if not (run_response and run_response.messages):
|
|
741
|
-
continue
|
|
742
|
-
|
|
743
|
-
for message in run_response.messages:
|
|
744
|
-
# Skip messages with specified role
|
|
745
|
-
if skip_role and message.role == skip_role:
|
|
746
|
-
continue
|
|
747
|
-
# Skip messages that were tagged as history in previous runs
|
|
748
|
-
if hasattr(message, "from_history") and message.from_history and skip_history_messages:
|
|
749
|
-
continue
|
|
750
|
-
if message.role == "system":
|
|
751
|
-
# Only add the system message once
|
|
752
|
-
if system_message is None:
|
|
753
|
-
system_message = message
|
|
754
|
-
messages_from_history.append(system_message)
|
|
755
|
-
else:
|
|
756
|
-
messages_from_history.append(message)
|
|
757
|
-
|
|
758
|
-
log_debug(f"Getting messages from previous runs: {len(messages_from_history)}")
|
|
759
|
-
return messages_from_history
|
|
760
|
-
|
|
761
|
-
def get_tool_calls(self, session_id: str, num_calls: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
762
|
-
"""Returns a list of tool calls from the messages"""
|
|
763
|
-
|
|
764
|
-
tool_calls = []
|
|
765
|
-
session_runs = self.runs.get(session_id, []) if self.runs else []
|
|
766
|
-
for run_response in session_runs[::-1]:
|
|
767
|
-
if run_response and run_response.messages:
|
|
768
|
-
for message in run_response.messages:
|
|
769
|
-
if message.tool_calls:
|
|
770
|
-
for tool_call in message.tool_calls:
|
|
771
|
-
tool_calls.append(tool_call)
|
|
772
|
-
if num_calls and len(tool_calls) >= num_calls:
|
|
773
|
-
return tool_calls
|
|
774
|
-
return tool_calls
|
|
775
|
-
|
|
776
|
-
def search_user_memories(
|
|
777
|
-
self,
|
|
778
|
-
query: Optional[str] = None,
|
|
779
|
-
limit: Optional[int] = None,
|
|
780
|
-
retrieval_method: Optional[Literal["last_n", "first_n", "agentic"]] = None,
|
|
781
|
-
user_id: Optional[str] = None,
|
|
782
|
-
refresh_from_db: bool = True,
|
|
783
|
-
) -> List[UserMemory]:
|
|
784
|
-
"""Search through user memories using the specified retrieval method.
|
|
785
|
-
|
|
786
|
-
Args:
|
|
787
|
-
query: The search query for agentic search. Required if retrieval_method is "agentic".
|
|
788
|
-
limit: Maximum number of memories to return. Defaults to self.retrieval_limit if not specified. Optional.
|
|
789
|
-
retrieval_method: The method to use for retrieving memories. Defaults to self.retrieval if not specified.
|
|
790
|
-
- "last_n": Return the most recent memories
|
|
791
|
-
- "first_n": Return the oldest memories
|
|
792
|
-
- "agentic": Return memories most similar to the query, but using an agentic approach
|
|
793
|
-
user_id: The user to search for. Optional.
|
|
794
|
-
|
|
795
|
-
Returns:
|
|
796
|
-
A list of UserMemory objects matching the search criteria.
|
|
797
|
-
"""
|
|
798
|
-
|
|
799
|
-
if user_id is None:
|
|
800
|
-
user_id = "default"
|
|
801
|
-
|
|
802
|
-
self.set_log_level()
|
|
803
|
-
|
|
804
|
-
if refresh_from_db:
|
|
805
|
-
self.refresh_from_db(user_id=user_id)
|
|
806
|
-
|
|
807
|
-
if not self.memories:
|
|
808
|
-
return []
|
|
809
|
-
|
|
810
|
-
# Use default retrieval method if not specified
|
|
811
|
-
retrieval_method = retrieval_method
|
|
812
|
-
# Use default limit if not specified
|
|
813
|
-
limit = limit
|
|
814
|
-
|
|
815
|
-
# Handle different retrieval methods
|
|
816
|
-
if retrieval_method == "agentic":
|
|
817
|
-
if not query:
|
|
818
|
-
raise ValueError("Query is required for agentic search")
|
|
819
|
-
|
|
820
|
-
return self._search_user_memories_agentic(user_id=user_id, query=query, limit=limit)
|
|
821
|
-
|
|
822
|
-
elif retrieval_method == "first_n":
|
|
823
|
-
return self._get_first_n_memories(user_id=user_id, limit=limit)
|
|
824
|
-
|
|
825
|
-
else: # Default to last_n
|
|
826
|
-
return self._get_last_n_memories(user_id=user_id, limit=limit)
|
|
827
|
-
|
|
828
|
-
def get_response_format(self) -> Union[Dict[str, Any], Type[BaseModel]]:
|
|
829
|
-
model = self.get_model()
|
|
830
|
-
if model.supports_native_structured_outputs:
|
|
831
|
-
return MemorySearchResponse
|
|
832
|
-
|
|
833
|
-
elif model.supports_json_schema_outputs:
|
|
834
|
-
return {
|
|
835
|
-
"type": "json_schema",
|
|
836
|
-
"json_schema": {
|
|
837
|
-
"name": MemorySearchResponse.__name__,
|
|
838
|
-
"schema": MemorySearchResponse.model_json_schema(),
|
|
839
|
-
},
|
|
840
|
-
}
|
|
841
|
-
else:
|
|
842
|
-
return {"type": "json_object"}
|
|
843
|
-
|
|
844
|
-
def _search_user_memories_agentic(self, user_id: str, query: str, limit: Optional[int] = None) -> List[UserMemory]:
|
|
845
|
-
"""Search through user memories using agentic search."""
|
|
846
|
-
if not self.memories:
|
|
847
|
-
return []
|
|
848
|
-
|
|
849
|
-
model = self.get_model()
|
|
850
|
-
|
|
851
|
-
response_format = self.get_response_format()
|
|
852
|
-
|
|
853
|
-
log_debug("Searching for memories", center=True)
|
|
854
|
-
|
|
855
|
-
# Get all memories as a list
|
|
856
|
-
user_memories: Dict[str, UserMemory] = self.memories[user_id]
|
|
857
|
-
system_message_str = "Your task is to search through user memories and return the IDs of the memories that are related to the query.\n"
|
|
858
|
-
system_message_str += "\n<user_memories>\n"
|
|
859
|
-
for memory in user_memories.values():
|
|
860
|
-
system_message_str += f"ID: {memory.memory_id}\n"
|
|
861
|
-
system_message_str += f"Memory: {memory.memory}\n"
|
|
862
|
-
if memory.topics:
|
|
863
|
-
system_message_str += f"Topics: {','.join(memory.topics)}\n"
|
|
864
|
-
system_message_str += "\n"
|
|
865
|
-
system_message_str = system_message_str.strip()
|
|
866
|
-
system_message_str += "\n</user_memories>\n\n"
|
|
867
|
-
system_message_str += "REMEMBER: Only return the IDs of the memories that are related to the query."
|
|
868
|
-
|
|
869
|
-
if response_format == {"type": "json_object"}:
|
|
870
|
-
system_message_str += "\n" + get_json_output_prompt(MemorySearchResponse) # type: ignore
|
|
871
|
-
|
|
872
|
-
messages_for_model = [
|
|
873
|
-
Message(role="system", content=system_message_str),
|
|
874
|
-
Message(
|
|
875
|
-
role="user",
|
|
876
|
-
content=f"Return the IDs of the memories related to the following query: {query}",
|
|
877
|
-
),
|
|
878
|
-
]
|
|
879
|
-
|
|
880
|
-
# Generate a response from the Model (includes running function calls)
|
|
881
|
-
response = model.response(messages=messages_for_model, response_format=response_format)
|
|
882
|
-
log_debug("Search for memories complete", center=True)
|
|
883
|
-
|
|
884
|
-
memory_search: Optional[MemorySearchResponse] = None
|
|
885
|
-
# If the model natively supports structured outputs, the parsed value is already in the structured format
|
|
886
|
-
if (
|
|
887
|
-
model.supports_native_structured_outputs
|
|
888
|
-
and response.parsed is not None
|
|
889
|
-
and isinstance(response.parsed, MemorySearchResponse)
|
|
890
|
-
):
|
|
891
|
-
memory_search = response.parsed
|
|
892
|
-
|
|
893
|
-
# Otherwise convert the response to the structured format
|
|
894
|
-
if isinstance(response.content, str):
|
|
895
|
-
try:
|
|
896
|
-
memory_search = parse_response_model_str(response.content, MemorySearchResponse) # type: ignore
|
|
897
|
-
|
|
898
|
-
# Update RunResponse
|
|
899
|
-
if memory_search is None:
|
|
900
|
-
log_warning("Failed to convert memory_search response to MemorySearchResponse")
|
|
901
|
-
return []
|
|
902
|
-
except Exception as e:
|
|
903
|
-
log_warning(f"Failed to convert memory_search response to MemorySearchResponse: {e}")
|
|
904
|
-
return []
|
|
905
|
-
|
|
906
|
-
memories_to_return = []
|
|
907
|
-
if memory_search:
|
|
908
|
-
for memory_id in memory_search.memory_ids:
|
|
909
|
-
memories_to_return.append(user_memories[memory_id])
|
|
910
|
-
return memories_to_return[:limit]
|
|
911
|
-
|
|
912
|
-
def _get_last_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
|
|
913
|
-
"""Get the most recent user memories.
|
|
914
|
-
|
|
915
|
-
Args:
|
|
916
|
-
limit: Maximum number of memories to return.
|
|
917
|
-
|
|
918
|
-
Returns:
|
|
919
|
-
A list of the most recent UserMemory objects.
|
|
920
|
-
"""
|
|
921
|
-
if not self.memories:
|
|
922
|
-
return []
|
|
923
|
-
|
|
924
|
-
memories_dict = self.memories.get(user_id, {})
|
|
925
|
-
|
|
926
|
-
# Sort memories by last_updated timestamp if available
|
|
927
|
-
if memories_dict:
|
|
928
|
-
# Convert to list of values for sorting
|
|
929
|
-
memories_list = list(memories_dict.values())
|
|
930
|
-
|
|
931
|
-
# Sort memories by last_updated timestamp (newest first)
|
|
932
|
-
# If last_updated is None, place at the beginning of the list
|
|
933
|
-
sorted_memories_list = sorted(
|
|
934
|
-
memories_list,
|
|
935
|
-
key=lambda memory: memory.last_updated or datetime.min,
|
|
936
|
-
)
|
|
937
|
-
else:
|
|
938
|
-
sorted_memories_list = []
|
|
939
|
-
|
|
940
|
-
if limit is not None and limit > 0:
|
|
941
|
-
sorted_memories_list = sorted_memories_list[-limit:]
|
|
942
|
-
|
|
943
|
-
return sorted_memories_list
|
|
944
|
-
|
|
945
|
-
def _get_first_n_memories(self, user_id: str, limit: Optional[int] = None) -> List[UserMemory]:
|
|
946
|
-
"""Get the oldest user memories.
|
|
947
|
-
|
|
948
|
-
Args:
|
|
949
|
-
limit: Maximum number of memories to return.
|
|
950
|
-
|
|
951
|
-
Returns:
|
|
952
|
-
A list of the oldest UserMemory objects.
|
|
953
|
-
"""
|
|
954
|
-
if not self.memories:
|
|
955
|
-
return []
|
|
956
|
-
|
|
957
|
-
memories_dict = self.memories.get(user_id, {})
|
|
958
|
-
# Sort memories by last_updated timestamp if available
|
|
959
|
-
if memories_dict:
|
|
960
|
-
# Convert to list of values for sorting
|
|
961
|
-
memories_list = list(memories_dict.values())
|
|
962
|
-
|
|
963
|
-
# Sort memories by last_updated timestamp (oldest first)
|
|
964
|
-
# If last_updated is None, place at the end of the list
|
|
965
|
-
sorted_memories_list = sorted(
|
|
966
|
-
memories_list,
|
|
967
|
-
key=lambda memory: memory.last_updated or datetime.max,
|
|
968
|
-
)
|
|
969
|
-
|
|
970
|
-
else:
|
|
971
|
-
sorted_memories_list = []
|
|
972
|
-
|
|
973
|
-
if limit is not None and limit > 0:
|
|
974
|
-
sorted_memories_list = sorted_memories_list[:limit]
|
|
975
|
-
|
|
976
|
-
return sorted_memories_list
|
|
977
|
-
|
|
978
|
-
def clear(self) -> None:
|
|
979
|
-
"""Clears the memory."""
|
|
980
|
-
if self.db:
|
|
981
|
-
self.db.clear()
|
|
982
|
-
self.memories = {}
|
|
983
|
-
self.summaries = {}
|
|
984
|
-
self.runs = {}
|
|
985
|
-
|
|
986
|
-
# -*- Team Functions
|
|
987
|
-
def add_interaction_to_team_context(
|
|
988
|
-
self, session_id: str, member_name: str, task: str, run_response: Union[RunResponse, TeamRunResponse]
|
|
989
|
-
) -> None:
|
|
990
|
-
if self.team_context is None:
|
|
991
|
-
self.team_context = {}
|
|
992
|
-
if session_id not in self.team_context:
|
|
993
|
-
self.team_context[session_id] = TeamContext()
|
|
994
|
-
self.team_context[session_id].member_interactions.append(
|
|
995
|
-
TeamMemberInteraction(
|
|
996
|
-
member_name=member_name,
|
|
997
|
-
task=task,
|
|
998
|
-
response=run_response,
|
|
999
|
-
)
|
|
1000
|
-
)
|
|
1001
|
-
log_debug(f"Updated team context with member name: {member_name}")
|
|
1002
|
-
|
|
1003
|
-
def set_team_context_text(self, session_id: str, text: Union[dict, str]) -> None:
|
|
1004
|
-
if self.team_context is None:
|
|
1005
|
-
self.team_context = {}
|
|
1006
|
-
if session_id not in self.team_context:
|
|
1007
|
-
self.team_context[session_id] = TeamContext()
|
|
1008
|
-
if isinstance(text, dict):
|
|
1009
|
-
if self.team_context[session_id].text is not None:
|
|
1010
|
-
try:
|
|
1011
|
-
current_context = json.loads(self.team_context[session_id].text) # type: ignore
|
|
1012
|
-
except Exception:
|
|
1013
|
-
current_context = {}
|
|
1014
|
-
else:
|
|
1015
|
-
current_context = {}
|
|
1016
|
-
current_context.update(text)
|
|
1017
|
-
self.team_context[session_id].text = json.dumps(current_context)
|
|
1018
|
-
else:
|
|
1019
|
-
# If string then we overwrite the current context
|
|
1020
|
-
self.team_context[session_id].text = text
|
|
1021
|
-
|
|
1022
|
-
def get_team_context_str(self, session_id: str) -> str:
|
|
1023
|
-
if not self.team_context:
|
|
1024
|
-
return ""
|
|
1025
|
-
session_team_context = self.team_context.get(session_id, None)
|
|
1026
|
-
if session_team_context and session_team_context.text:
|
|
1027
|
-
return f"<team context>\n{session_team_context.text}\n</team context>\n"
|
|
1028
|
-
return ""
|
|
1029
|
-
|
|
1030
|
-
def get_team_member_interactions_str(self, session_id: str) -> str:
|
|
1031
|
-
if not self.team_context:
|
|
1032
|
-
return ""
|
|
1033
|
-
team_member_interactions_str = ""
|
|
1034
|
-
session_team_context = self.team_context.get(session_id, None)
|
|
1035
|
-
if session_team_context and session_team_context.member_interactions:
|
|
1036
|
-
team_member_interactions_str += "<member interactions>\n"
|
|
1037
|
-
|
|
1038
|
-
for interaction in session_team_context.member_interactions:
|
|
1039
|
-
response_dict = interaction.response.to_dict()
|
|
1040
|
-
response_content = (
|
|
1041
|
-
response_dict.get("content")
|
|
1042
|
-
or ",".join([tool.get("content", "") for tool in response_dict.get("tools", [])])
|
|
1043
|
-
or ""
|
|
1044
|
-
)
|
|
1045
|
-
team_member_interactions_str += f"Member: {interaction.member_name}\n"
|
|
1046
|
-
team_member_interactions_str += f"Task: {interaction.task}\n"
|
|
1047
|
-
team_member_interactions_str += f"Response: {response_content}\n"
|
|
1048
|
-
team_member_interactions_str += "\n"
|
|
1049
|
-
team_member_interactions_str += "</member interactions>\n"
|
|
1050
|
-
return team_member_interactions_str
|
|
1051
|
-
|
|
1052
|
-
def get_team_context_images(self, session_id: str) -> List[ImageArtifact]:
|
|
1053
|
-
if not self.team_context:
|
|
1054
|
-
return []
|
|
1055
|
-
images = []
|
|
1056
|
-
session_team_context = self.team_context.get(session_id, None)
|
|
1057
|
-
if session_team_context and session_team_context.member_interactions:
|
|
1058
|
-
for interaction in session_team_context.member_interactions:
|
|
1059
|
-
if interaction.response.images:
|
|
1060
|
-
images.extend(interaction.response.images)
|
|
1061
|
-
return images
|
|
1062
|
-
|
|
1063
|
-
def get_team_context_videos(self, session_id: str) -> List[VideoArtifact]:
|
|
1064
|
-
if not self.team_context:
|
|
1065
|
-
return []
|
|
1066
|
-
videos = []
|
|
1067
|
-
session_team_context = self.team_context.get(session_id, None)
|
|
1068
|
-
if session_team_context and session_team_context.member_interactions:
|
|
1069
|
-
for interaction in session_team_context.member_interactions:
|
|
1070
|
-
if interaction.response.videos:
|
|
1071
|
-
videos.extend(interaction.response.videos)
|
|
1072
|
-
return videos
|
|
1073
|
-
|
|
1074
|
-
def get_team_context_audio(self, session_id: str) -> List[AudioArtifact]:
|
|
1075
|
-
if not self.team_context:
|
|
1076
|
-
return []
|
|
1077
|
-
audio = []
|
|
1078
|
-
session_team_context = self.team_context.get(session_id, None)
|
|
1079
|
-
if session_team_context and session_team_context.member_interactions:
|
|
1080
|
-
for interaction in session_team_context.member_interactions:
|
|
1081
|
-
if interaction.response.audio:
|
|
1082
|
-
audio.extend(interaction.response.audio)
|
|
1083
|
-
return audio
|
|
1084
|
-
|
|
1085
|
-
def __deepcopy__(self, memo):
|
|
1086
|
-
from copy import deepcopy
|
|
1087
|
-
|
|
1088
|
-
# Create a new instance without calling __init__
|
|
1089
|
-
copied_obj = self.__class__.__new__(self.__class__)
|
|
1090
|
-
memo[id(self)] = copied_obj
|
|
1091
|
-
|
|
1092
|
-
# Copy attributes, reusing specific objects
|
|
1093
|
-
shared_objects = {"db", "memory_manager", "summary_manager", "team_context"}
|
|
1094
|
-
for k, v in self.__dict__.items():
|
|
1095
|
-
setattr(copied_obj, k, v if k in shared_objects else deepcopy(v, memo))
|
|
1096
|
-
|
|
1097
|
-
return copied_obj
|