PraisonAI 3.0.0__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.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PersistenceOrchestrator - Central coordinator for all persistence operations.
|
|
3
|
+
|
|
4
|
+
Hooks into agent lifecycle to provide automatic conversation persistence,
|
|
5
|
+
knowledge retrieval, and state management.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from .conversation.base import ConversationStore, ConversationSession, ConversationMessage
|
|
14
|
+
from .knowledge.base import KnowledgeStore, KnowledgeDocument
|
|
15
|
+
from .state.base import StateStore
|
|
16
|
+
from .config import PersistenceConfig
|
|
17
|
+
from .factory import create_stores_from_config
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PersistenceOrchestrator:
|
|
26
|
+
"""
|
|
27
|
+
Central coordinator for all persistence operations.
|
|
28
|
+
|
|
29
|
+
Lives in wrapper layer, hooks into agent lifecycle to provide:
|
|
30
|
+
- Automatic session loading/creation at agent start
|
|
31
|
+
- Message persistence during conversation
|
|
32
|
+
- Session metadata updates at agent end
|
|
33
|
+
- Knowledge retrieval for RAG
|
|
34
|
+
- State caching
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
orchestrator = PersistenceOrchestrator(
|
|
38
|
+
conversation_store=PostgresConversationStore(...),
|
|
39
|
+
knowledge_store=QdrantKnowledgeStore(...),
|
|
40
|
+
state_store=RedisStateStore(...),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Hook into agent lifecycle
|
|
44
|
+
history = orchestrator.on_agent_start(agent, session_id="sess-123")
|
|
45
|
+
# ... agent runs ...
|
|
46
|
+
orchestrator.on_message(session_id, user_message)
|
|
47
|
+
orchestrator.on_message(session_id, assistant_message)
|
|
48
|
+
orchestrator.on_agent_end(agent, session_id)
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
conversation_store: Optional[ConversationStore] = None,
|
|
54
|
+
knowledge_store: Optional[KnowledgeStore] = None,
|
|
55
|
+
state_store: Optional[StateStore] = None,
|
|
56
|
+
config: Optional[PersistenceConfig] = None,
|
|
57
|
+
):
|
|
58
|
+
"""
|
|
59
|
+
Initialize the orchestrator.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
conversation_store: Store for session/message persistence
|
|
63
|
+
knowledge_store: Store for vector embeddings
|
|
64
|
+
state_store: Store for key-value state
|
|
65
|
+
config: Configuration (alternative to passing stores directly)
|
|
66
|
+
"""
|
|
67
|
+
if config:
|
|
68
|
+
stores = create_stores_from_config(config)
|
|
69
|
+
self.conversation = stores["conversation"] or conversation_store
|
|
70
|
+
self.knowledge = stores["knowledge"] or knowledge_store
|
|
71
|
+
self.state = stores["state"] or state_store
|
|
72
|
+
self._config = config
|
|
73
|
+
else:
|
|
74
|
+
self.conversation = conversation_store
|
|
75
|
+
self.knowledge = knowledge_store
|
|
76
|
+
self.state = state_store
|
|
77
|
+
self._config = None
|
|
78
|
+
|
|
79
|
+
self._current_session: Optional[ConversationSession] = None
|
|
80
|
+
self._session_cache: Dict[str, ConversationSession] = {}
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def from_config(cls, config: PersistenceConfig) -> "PersistenceOrchestrator":
|
|
84
|
+
"""Create orchestrator from configuration."""
|
|
85
|
+
return cls(config=config)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def from_env(cls) -> "PersistenceOrchestrator":
|
|
89
|
+
"""Create orchestrator from environment variables."""
|
|
90
|
+
config = PersistenceConfig.from_env()
|
|
91
|
+
return cls(config=config)
|
|
92
|
+
|
|
93
|
+
# =========================================================================
|
|
94
|
+
# Agent Lifecycle Hooks
|
|
95
|
+
# =========================================================================
|
|
96
|
+
|
|
97
|
+
def on_agent_start(
|
|
98
|
+
self,
|
|
99
|
+
agent: Any,
|
|
100
|
+
session_id: Optional[str] = None,
|
|
101
|
+
user_id: Optional[str] = None,
|
|
102
|
+
resume: bool = True,
|
|
103
|
+
) -> List[ConversationMessage]:
|
|
104
|
+
"""
|
|
105
|
+
Called before agent run. Loads or creates session.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
agent: The agent instance
|
|
109
|
+
session_id: Session ID (generated if not provided)
|
|
110
|
+
user_id: User ID for session
|
|
111
|
+
resume: Whether to load existing session history
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
List of previous messages if resuming, empty list otherwise
|
|
115
|
+
"""
|
|
116
|
+
if not session_id:
|
|
117
|
+
session_id = str(uuid.uuid4())
|
|
118
|
+
|
|
119
|
+
if not self.conversation:
|
|
120
|
+
logger.debug("No conversation store configured, skipping session load")
|
|
121
|
+
return []
|
|
122
|
+
|
|
123
|
+
# Try to load existing session
|
|
124
|
+
session = None
|
|
125
|
+
if resume:
|
|
126
|
+
session = self.conversation.get_session(session_id)
|
|
127
|
+
|
|
128
|
+
if session:
|
|
129
|
+
logger.info(f"Resuming session: {session_id}")
|
|
130
|
+
self._current_session = session
|
|
131
|
+
self._session_cache[session_id] = session
|
|
132
|
+
|
|
133
|
+
# Load previous messages
|
|
134
|
+
messages = self.conversation.get_messages(session_id)
|
|
135
|
+
return messages
|
|
136
|
+
else:
|
|
137
|
+
# Create new session
|
|
138
|
+
agent_id = getattr(agent, "name", None) or getattr(agent, "agent_id", None)
|
|
139
|
+
session = ConversationSession(
|
|
140
|
+
session_id=session_id,
|
|
141
|
+
user_id=user_id,
|
|
142
|
+
agent_id=agent_id,
|
|
143
|
+
name=f"Session {session_id[:8]}",
|
|
144
|
+
metadata={"agent_type": type(agent).__name__},
|
|
145
|
+
)
|
|
146
|
+
self.conversation.create_session(session)
|
|
147
|
+
logger.info(f"Created new session: {session_id}")
|
|
148
|
+
self._current_session = session
|
|
149
|
+
self._session_cache[session_id] = session
|
|
150
|
+
return []
|
|
151
|
+
|
|
152
|
+
def on_message(
|
|
153
|
+
self,
|
|
154
|
+
session_id: str,
|
|
155
|
+
role: str,
|
|
156
|
+
content: str,
|
|
157
|
+
tool_calls: Optional[List[Dict]] = None,
|
|
158
|
+
tool_call_id: Optional[str] = None,
|
|
159
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
160
|
+
) -> Optional[ConversationMessage]:
|
|
161
|
+
"""
|
|
162
|
+
Called after each message (user or assistant).
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
session_id: Session ID
|
|
166
|
+
role: Message role (user, assistant, system, tool)
|
|
167
|
+
content: Message content
|
|
168
|
+
tool_calls: Tool calls (for assistant messages)
|
|
169
|
+
tool_call_id: Tool call ID (for tool response messages)
|
|
170
|
+
metadata: Additional metadata
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
The persisted message, or None if no store configured
|
|
174
|
+
"""
|
|
175
|
+
if not self.conversation:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
message = ConversationMessage(
|
|
179
|
+
id=str(uuid.uuid4()),
|
|
180
|
+
session_id=session_id,
|
|
181
|
+
role=role,
|
|
182
|
+
content=content,
|
|
183
|
+
tool_calls=tool_calls,
|
|
184
|
+
tool_call_id=tool_call_id,
|
|
185
|
+
metadata=metadata,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self.conversation.add_message(session_id, message)
|
|
189
|
+
logger.debug(f"Persisted {role} message to session {session_id}")
|
|
190
|
+
return message
|
|
191
|
+
|
|
192
|
+
def on_agent_end(
|
|
193
|
+
self,
|
|
194
|
+
agent: Any,
|
|
195
|
+
session_id: str,
|
|
196
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
197
|
+
) -> None:
|
|
198
|
+
"""
|
|
199
|
+
Called after agent run. Updates session metadata.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
agent: The agent instance
|
|
203
|
+
session_id: Session ID
|
|
204
|
+
metadata: Additional metadata to store
|
|
205
|
+
"""
|
|
206
|
+
if not self.conversation:
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
session = self._session_cache.get(session_id) or self.conversation.get_session(session_id)
|
|
210
|
+
if session:
|
|
211
|
+
session.updated_at = time.time()
|
|
212
|
+
if metadata:
|
|
213
|
+
session.metadata = {**(session.metadata or {}), **metadata}
|
|
214
|
+
self.conversation.update_session(session)
|
|
215
|
+
logger.debug(f"Updated session metadata: {session_id}")
|
|
216
|
+
|
|
217
|
+
# =========================================================================
|
|
218
|
+
# Knowledge Retrieval
|
|
219
|
+
# =========================================================================
|
|
220
|
+
|
|
221
|
+
def retrieve_knowledge(
|
|
222
|
+
self,
|
|
223
|
+
query_embedding: List[float],
|
|
224
|
+
collection: str = "default",
|
|
225
|
+
limit: int = 5,
|
|
226
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
227
|
+
) -> List[KnowledgeDocument]:
|
|
228
|
+
"""
|
|
229
|
+
Retrieve relevant documents for RAG.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
query_embedding: Query vector embedding
|
|
233
|
+
collection: Collection name
|
|
234
|
+
limit: Max documents to return
|
|
235
|
+
filters: Metadata filters
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
List of relevant documents
|
|
239
|
+
"""
|
|
240
|
+
if not self.knowledge:
|
|
241
|
+
logger.debug("No knowledge store configured")
|
|
242
|
+
return []
|
|
243
|
+
|
|
244
|
+
return self.knowledge.search(
|
|
245
|
+
collection=collection,
|
|
246
|
+
query_embedding=query_embedding,
|
|
247
|
+
limit=limit,
|
|
248
|
+
filters=filters,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
def add_knowledge(
|
|
252
|
+
self,
|
|
253
|
+
documents: List[KnowledgeDocument],
|
|
254
|
+
collection: str = "default",
|
|
255
|
+
) -> List[str]:
|
|
256
|
+
"""
|
|
257
|
+
Add documents to knowledge store.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
documents: Documents with embeddings
|
|
261
|
+
collection: Collection name
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
List of document IDs
|
|
265
|
+
"""
|
|
266
|
+
if not self.knowledge:
|
|
267
|
+
raise ValueError("No knowledge store configured")
|
|
268
|
+
|
|
269
|
+
return self.knowledge.upsert(collection, documents)
|
|
270
|
+
|
|
271
|
+
# =========================================================================
|
|
272
|
+
# State Management
|
|
273
|
+
# =========================================================================
|
|
274
|
+
|
|
275
|
+
def get_state(self, key: str) -> Optional[Any]:
|
|
276
|
+
"""Get state value."""
|
|
277
|
+
if not self.state:
|
|
278
|
+
return None
|
|
279
|
+
return self.state.get(key)
|
|
280
|
+
|
|
281
|
+
def set_state(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
|
|
282
|
+
"""Set state value with optional TTL."""
|
|
283
|
+
if not self.state:
|
|
284
|
+
return
|
|
285
|
+
self.state.set(key, value, ttl)
|
|
286
|
+
|
|
287
|
+
def delete_state(self, key: str) -> bool:
|
|
288
|
+
"""Delete state value."""
|
|
289
|
+
if not self.state:
|
|
290
|
+
return False
|
|
291
|
+
return self.state.delete(key)
|
|
292
|
+
|
|
293
|
+
# =========================================================================
|
|
294
|
+
# Session Management
|
|
295
|
+
# =========================================================================
|
|
296
|
+
|
|
297
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
298
|
+
"""Get a session by ID."""
|
|
299
|
+
if not self.conversation:
|
|
300
|
+
return None
|
|
301
|
+
return self.conversation.get_session(session_id)
|
|
302
|
+
|
|
303
|
+
def list_sessions(
|
|
304
|
+
self,
|
|
305
|
+
user_id: Optional[str] = None,
|
|
306
|
+
limit: int = 100,
|
|
307
|
+
) -> List[ConversationSession]:
|
|
308
|
+
"""List sessions for a user."""
|
|
309
|
+
if not self.conversation:
|
|
310
|
+
return []
|
|
311
|
+
return self.conversation.list_sessions(user_id=user_id, limit=limit)
|
|
312
|
+
|
|
313
|
+
def delete_session(self, session_id: str) -> bool:
|
|
314
|
+
"""Delete a session and all its messages."""
|
|
315
|
+
if not self.conversation:
|
|
316
|
+
return False
|
|
317
|
+
|
|
318
|
+
if session_id in self._session_cache:
|
|
319
|
+
del self._session_cache[session_id]
|
|
320
|
+
|
|
321
|
+
return self.conversation.delete_session(session_id)
|
|
322
|
+
|
|
323
|
+
def get_messages(
|
|
324
|
+
self,
|
|
325
|
+
session_id: str,
|
|
326
|
+
limit: Optional[int] = None,
|
|
327
|
+
) -> List[ConversationMessage]:
|
|
328
|
+
"""Get messages from a session."""
|
|
329
|
+
if not self.conversation:
|
|
330
|
+
return []
|
|
331
|
+
return self.conversation.get_messages(session_id, limit=limit)
|
|
332
|
+
|
|
333
|
+
# =========================================================================
|
|
334
|
+
# Context Building
|
|
335
|
+
# =========================================================================
|
|
336
|
+
|
|
337
|
+
def build_context(
|
|
338
|
+
self,
|
|
339
|
+
session_id: str,
|
|
340
|
+
query_embedding: Optional[List[float]] = None,
|
|
341
|
+
history_limit: int = 20,
|
|
342
|
+
knowledge_limit: int = 5,
|
|
343
|
+
knowledge_collection: str = "default",
|
|
344
|
+
) -> Dict[str, Any]:
|
|
345
|
+
"""
|
|
346
|
+
Build context for agent including history and knowledge.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
session_id: Session ID
|
|
350
|
+
query_embedding: Query embedding for knowledge retrieval
|
|
351
|
+
history_limit: Max history messages
|
|
352
|
+
knowledge_limit: Max knowledge documents
|
|
353
|
+
knowledge_collection: Knowledge collection name
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
Dict with 'history' and 'knowledge' keys
|
|
357
|
+
"""
|
|
358
|
+
context = {
|
|
359
|
+
"history": [],
|
|
360
|
+
"knowledge": [],
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
# Get conversation history
|
|
364
|
+
if self.conversation:
|
|
365
|
+
messages = self.conversation.get_messages(session_id, limit=history_limit)
|
|
366
|
+
context["history"] = [
|
|
367
|
+
{"role": m.role, "content": m.content}
|
|
368
|
+
for m in messages
|
|
369
|
+
]
|
|
370
|
+
|
|
371
|
+
# Get relevant knowledge
|
|
372
|
+
if self.knowledge and query_embedding:
|
|
373
|
+
docs = self.knowledge.search(
|
|
374
|
+
collection=knowledge_collection,
|
|
375
|
+
query_embedding=query_embedding,
|
|
376
|
+
limit=knowledge_limit,
|
|
377
|
+
)
|
|
378
|
+
context["knowledge"] = [
|
|
379
|
+
{"content": d.content, "metadata": d.metadata}
|
|
380
|
+
for d in docs
|
|
381
|
+
]
|
|
382
|
+
|
|
383
|
+
return context
|
|
384
|
+
|
|
385
|
+
# =========================================================================
|
|
386
|
+
# Cleanup
|
|
387
|
+
# =========================================================================
|
|
388
|
+
|
|
389
|
+
def close(self) -> None:
|
|
390
|
+
"""Close all stores and release resources."""
|
|
391
|
+
if self.conversation:
|
|
392
|
+
self.conversation.close()
|
|
393
|
+
if self.knowledge:
|
|
394
|
+
self.knowledge.close()
|
|
395
|
+
if self.state:
|
|
396
|
+
self.state.close()
|
|
397
|
+
|
|
398
|
+
self._session_cache.clear()
|
|
399
|
+
logger.info("Persistence orchestrator closed")
|
|
400
|
+
|
|
401
|
+
def __enter__(self):
|
|
402
|
+
return self
|
|
403
|
+
|
|
404
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
405
|
+
self.close()
|
|
406
|
+
return False
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
StateStore implementations for fast key-value state and caching.
|
|
3
|
+
|
|
4
|
+
Supported backends:
|
|
5
|
+
- Redis (redis)
|
|
6
|
+
- DynamoDB (dynamodb)
|
|
7
|
+
- Firestore (firestore)
|
|
8
|
+
- MongoDB (mongodb)
|
|
9
|
+
- Upstash (upstash)
|
|
10
|
+
- In-memory / JSON file (memory)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"StateStore",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def __getattr__(name: str):
|
|
18
|
+
if name == "StateStore":
|
|
19
|
+
from .base import StateStore
|
|
20
|
+
return StateStore
|
|
21
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async MongoDB implementation of StateStore.
|
|
3
|
+
|
|
4
|
+
Requires: motor
|
|
5
|
+
Install: pip install motor
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from .base import StateStore
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class AsyncMongoDBStateStore(StateStore):
|
|
19
|
+
"""
|
|
20
|
+
Async MongoDB state store using motor.
|
|
21
|
+
|
|
22
|
+
Provides high-performance async database operations.
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
store = AsyncMongoDBStateStore(
|
|
26
|
+
url="mongodb://localhost:27017",
|
|
27
|
+
database="praisonai"
|
|
28
|
+
)
|
|
29
|
+
await store.init()
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
url: str = "mongodb://localhost:27017",
|
|
35
|
+
database: str = "praisonai",
|
|
36
|
+
collection: str = "state",
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Initialize async MongoDB store.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
url: MongoDB connection URL
|
|
43
|
+
database: Database name
|
|
44
|
+
collection: Collection name
|
|
45
|
+
"""
|
|
46
|
+
self.url = url
|
|
47
|
+
self.database_name = database
|
|
48
|
+
self.collection_name = collection
|
|
49
|
+
self._client = None
|
|
50
|
+
self._db = None
|
|
51
|
+
self._collection = None
|
|
52
|
+
self._initialized = False
|
|
53
|
+
|
|
54
|
+
async def init(self):
|
|
55
|
+
"""Initialize connection."""
|
|
56
|
+
if self._initialized:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
from motor.motor_asyncio import AsyncIOMotorClient
|
|
61
|
+
except ImportError:
|
|
62
|
+
raise ImportError(
|
|
63
|
+
"motor is required for async MongoDB support. "
|
|
64
|
+
"Install with: pip install motor"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
self._client = AsyncIOMotorClient(self.url)
|
|
68
|
+
self._db = self._client[self.database_name]
|
|
69
|
+
self._collection = self._db[self.collection_name]
|
|
70
|
+
|
|
71
|
+
# Create TTL index for expiration
|
|
72
|
+
await self._collection.create_index("expires_at", expireAfterSeconds=0)
|
|
73
|
+
|
|
74
|
+
self._initialized = True
|
|
75
|
+
|
|
76
|
+
async def async_get(self, key: str) -> Optional[Dict[str, Any]]:
|
|
77
|
+
"""Get state by key asynchronously."""
|
|
78
|
+
if not self._initialized:
|
|
79
|
+
await self.init()
|
|
80
|
+
|
|
81
|
+
doc = await self._collection.find_one({"_id": key})
|
|
82
|
+
if doc:
|
|
83
|
+
# Check TTL manually (in case index hasn't cleaned up yet)
|
|
84
|
+
if "expires_at" in doc and doc["expires_at"]:
|
|
85
|
+
if time.time() > doc["expires_at"]:
|
|
86
|
+
await self.async_delete(key)
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
# Remove internal fields
|
|
90
|
+
result = dict(doc)
|
|
91
|
+
result.pop("_id", None)
|
|
92
|
+
result.pop("expires_at", None)
|
|
93
|
+
return result.get("value", result)
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
def get(self, key: str) -> Optional[Dict[str, Any]]:
|
|
97
|
+
"""Sync wrapper for get."""
|
|
98
|
+
return asyncio.get_event_loop().run_until_complete(self.async_get(key))
|
|
99
|
+
|
|
100
|
+
async def async_set(self, key: str, value: Dict[str, Any], ttl: Optional[int] = None) -> bool:
|
|
101
|
+
"""Set state by key with optional TTL asynchronously."""
|
|
102
|
+
if not self._initialized:
|
|
103
|
+
await self.init()
|
|
104
|
+
|
|
105
|
+
doc = {
|
|
106
|
+
"_id": key,
|
|
107
|
+
"value": value,
|
|
108
|
+
"updated_at": time.time(),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if ttl:
|
|
112
|
+
doc["expires_at"] = time.time() + ttl
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
await self._collection.replace_one(
|
|
116
|
+
{"_id": key},
|
|
117
|
+
doc,
|
|
118
|
+
upsert=True
|
|
119
|
+
)
|
|
120
|
+
return True
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(f"Error setting state {key}: {e}")
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
def set(self, key: str, value: Dict[str, Any], ttl: Optional[int] = None) -> bool:
|
|
126
|
+
"""Sync wrapper for set."""
|
|
127
|
+
return asyncio.get_event_loop().run_until_complete(self.async_set(key, value, ttl))
|
|
128
|
+
|
|
129
|
+
async def async_delete(self, key: str) -> bool:
|
|
130
|
+
"""Delete state by key asynchronously."""
|
|
131
|
+
if not self._initialized:
|
|
132
|
+
await self.init()
|
|
133
|
+
|
|
134
|
+
result = await self._collection.delete_one({"_id": key})
|
|
135
|
+
return result.deleted_count > 0
|
|
136
|
+
|
|
137
|
+
def delete(self, key: str) -> bool:
|
|
138
|
+
"""Sync wrapper for delete."""
|
|
139
|
+
return asyncio.get_event_loop().run_until_complete(self.async_delete(key))
|
|
140
|
+
|
|
141
|
+
async def async_exists(self, key: str) -> bool:
|
|
142
|
+
"""Check if key exists asynchronously."""
|
|
143
|
+
if not self._initialized:
|
|
144
|
+
await self.init()
|
|
145
|
+
|
|
146
|
+
count = await self._collection.count_documents({"_id": key}, limit=1)
|
|
147
|
+
return count > 0
|
|
148
|
+
|
|
149
|
+
def exists(self, key: str) -> bool:
|
|
150
|
+
"""Sync wrapper for exists."""
|
|
151
|
+
return asyncio.get_event_loop().run_until_complete(self.async_exists(key))
|
|
152
|
+
|
|
153
|
+
async def async_list_keys(self, prefix: Optional[str] = None) -> List[str]:
|
|
154
|
+
"""List all keys with optional prefix filter asynchronously."""
|
|
155
|
+
if not self._initialized:
|
|
156
|
+
await self.init()
|
|
157
|
+
|
|
158
|
+
query = {}
|
|
159
|
+
if prefix:
|
|
160
|
+
query["_id"] = {"$regex": f"^{prefix}"}
|
|
161
|
+
|
|
162
|
+
cursor = self._collection.find(query, {"_id": 1})
|
|
163
|
+
keys = []
|
|
164
|
+
async for doc in cursor:
|
|
165
|
+
keys.append(doc["_id"])
|
|
166
|
+
return keys
|
|
167
|
+
|
|
168
|
+
def list_keys(self, prefix: Optional[str] = None) -> List[str]:
|
|
169
|
+
"""Sync wrapper for list_keys."""
|
|
170
|
+
return asyncio.get_event_loop().run_until_complete(self.async_list_keys(prefix))
|
|
171
|
+
|
|
172
|
+
async def async_clear(self, prefix: Optional[str] = None) -> int:
|
|
173
|
+
"""Clear all keys with optional prefix filter asynchronously."""
|
|
174
|
+
if not self._initialized:
|
|
175
|
+
await self.init()
|
|
176
|
+
|
|
177
|
+
query = {}
|
|
178
|
+
if prefix:
|
|
179
|
+
query["_id"] = {"$regex": f"^{prefix}"}
|
|
180
|
+
|
|
181
|
+
result = await self._collection.delete_many(query)
|
|
182
|
+
return result.deleted_count
|
|
183
|
+
|
|
184
|
+
def clear(self, prefix: Optional[str] = None) -> int:
|
|
185
|
+
"""Sync wrapper for clear."""
|
|
186
|
+
return asyncio.get_event_loop().run_until_complete(self.async_clear(prefix))
|
|
187
|
+
|
|
188
|
+
async def async_close(self) -> None:
|
|
189
|
+
"""Close the connection."""
|
|
190
|
+
if self._client:
|
|
191
|
+
self._client.close()
|
|
192
|
+
self._client = None
|
|
193
|
+
self._db = None
|
|
194
|
+
self._collection = None
|
|
195
|
+
self._initialized = False
|
|
196
|
+
|
|
197
|
+
def close(self) -> None:
|
|
198
|
+
"""Sync wrapper for close."""
|
|
199
|
+
if self._client:
|
|
200
|
+
asyncio.get_event_loop().run_until_complete(self.async_close())
|