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,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration handling for PraisonAI persistence layer.
|
|
3
|
+
|
|
4
|
+
Supports configuration via:
|
|
5
|
+
- Environment variables
|
|
6
|
+
- Config file (YAML/JSON)
|
|
7
|
+
- CLI arguments
|
|
8
|
+
- Direct Python API
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import logging
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from typing import Any, Dict, Optional
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Environment variable mappings
|
|
20
|
+
ENV_VARS = {
|
|
21
|
+
# Conversation stores
|
|
22
|
+
"conversation_store": "PRAISONAI_CONVERSATION_STORE",
|
|
23
|
+
"conversation_url": "PRAISONAI_CONVERSATION_URL",
|
|
24
|
+
# Knowledge stores
|
|
25
|
+
"knowledge_store": "PRAISONAI_KNOWLEDGE_STORE",
|
|
26
|
+
"knowledge_url": "PRAISONAI_KNOWLEDGE_URL",
|
|
27
|
+
# State stores
|
|
28
|
+
"state_store": "PRAISONAI_STATE_STORE",
|
|
29
|
+
"state_url": "PRAISONAI_STATE_URL",
|
|
30
|
+
# Common
|
|
31
|
+
"session_id": "PRAISONAI_SESSION_ID",
|
|
32
|
+
"user_id": "PRAISONAI_USER_ID",
|
|
33
|
+
# PostgreSQL
|
|
34
|
+
"postgres_url": "POSTGRES_URL",
|
|
35
|
+
"postgres_host": "POSTGRES_HOST",
|
|
36
|
+
"postgres_port": "POSTGRES_PORT",
|
|
37
|
+
"postgres_database": "POSTGRES_DATABASE",
|
|
38
|
+
"postgres_user": "POSTGRES_USER",
|
|
39
|
+
"postgres_password": "POSTGRES_PASSWORD",
|
|
40
|
+
# Qdrant
|
|
41
|
+
"qdrant_url": "QDRANT_URL",
|
|
42
|
+
"qdrant_host": "QDRANT_HOST",
|
|
43
|
+
"qdrant_port": "QDRANT_PORT",
|
|
44
|
+
"qdrant_api_key": "QDRANT_API_KEY",
|
|
45
|
+
# Redis
|
|
46
|
+
"redis_url": "REDIS_URL",
|
|
47
|
+
"redis_host": "REDIS_HOST",
|
|
48
|
+
"redis_port": "REDIS_PORT",
|
|
49
|
+
"redis_password": "REDIS_PASSWORD",
|
|
50
|
+
# Pinecone
|
|
51
|
+
"pinecone_api_key": "PINECONE_API_KEY",
|
|
52
|
+
"pinecone_environment": "PINECONE_ENVIRONMENT",
|
|
53
|
+
"pinecone_index": "PINECONE_INDEX",
|
|
54
|
+
# ChromaDB
|
|
55
|
+
"chroma_path": "CHROMA_DB_PATH",
|
|
56
|
+
"chroma_host": "CHROMA_HOST",
|
|
57
|
+
"chroma_port": "CHROMA_PORT",
|
|
58
|
+
# Weaviate
|
|
59
|
+
"weaviate_url": "WEAVIATE_URL",
|
|
60
|
+
"weaviate_api_key": "WEAVIATE_API_KEY",
|
|
61
|
+
# Milvus
|
|
62
|
+
"milvus_host": "MILVUS_HOST",
|
|
63
|
+
"milvus_port": "MILVUS_PORT",
|
|
64
|
+
"milvus_token": "MILVUS_TOKEN",
|
|
65
|
+
# MongoDB
|
|
66
|
+
"mongodb_url": "MONGODB_URL",
|
|
67
|
+
"mongodb_database": "MONGODB_DATABASE",
|
|
68
|
+
# DynamoDB
|
|
69
|
+
"aws_region": "AWS_REGION",
|
|
70
|
+
"dynamodb_table": "DYNAMODB_TABLE",
|
|
71
|
+
# Firestore
|
|
72
|
+
"google_credentials": "GOOGLE_APPLICATION_CREDENTIALS",
|
|
73
|
+
"firestore_project": "FIRESTORE_PROJECT",
|
|
74
|
+
# Upstash
|
|
75
|
+
"upstash_redis_url": "UPSTASH_REDIS_URL",
|
|
76
|
+
"upstash_redis_token": "UPSTASH_REDIS_TOKEN",
|
|
77
|
+
"upstash_vector_url": "UPSTASH_VECTOR_URL",
|
|
78
|
+
"upstash_vector_token": "UPSTASH_VECTOR_TOKEN",
|
|
79
|
+
# Supabase
|
|
80
|
+
"supabase_url": "SUPABASE_URL",
|
|
81
|
+
"supabase_key": "SUPABASE_KEY",
|
|
82
|
+
# SurrealDB
|
|
83
|
+
"surrealdb_url": "SURREALDB_URL",
|
|
84
|
+
"surrealdb_namespace": "SURREALDB_NS",
|
|
85
|
+
"surrealdb_database": "SURREALDB_DB",
|
|
86
|
+
# MySQL
|
|
87
|
+
"mysql_url": "MYSQL_URL",
|
|
88
|
+
"mysql_host": "MYSQL_HOST",
|
|
89
|
+
"mysql_port": "MYSQL_PORT",
|
|
90
|
+
"mysql_database": "MYSQL_DATABASE",
|
|
91
|
+
"mysql_user": "MYSQL_USER",
|
|
92
|
+
"mysql_password": "MYSQL_PASSWORD",
|
|
93
|
+
# SingleStore
|
|
94
|
+
"singlestore_url": "SINGLESTORE_URL",
|
|
95
|
+
# ClickHouse
|
|
96
|
+
"clickhouse_host": "CLICKHOUSE_HOST",
|
|
97
|
+
"clickhouse_port": "CLICKHOUSE_PORT",
|
|
98
|
+
"clickhouse_user": "CLICKHOUSE_USER",
|
|
99
|
+
"clickhouse_password": "CLICKHOUSE_PASSWORD",
|
|
100
|
+
# Cassandra
|
|
101
|
+
"cassandra_hosts": "CASSANDRA_HOSTS",
|
|
102
|
+
"cassandra_keyspace": "CASSANDRA_KEYSPACE",
|
|
103
|
+
# LanceDB
|
|
104
|
+
"lancedb_path": "LANCEDB_PATH",
|
|
105
|
+
"lancedb_uri": "LANCEDB_URI",
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# Supported backends
|
|
109
|
+
CONVERSATION_BACKENDS = ["postgres", "mysql", "sqlite", "singlestore", "supabase", "surrealdb"]
|
|
110
|
+
KNOWLEDGE_BACKENDS = ["qdrant", "pinecone", "chroma", "weaviate", "lancedb", "milvus", "pgvector", "redis", "cassandra", "clickhouse"]
|
|
111
|
+
STATE_BACKENDS = ["redis", "dynamodb", "firestore", "mongodb", "upstash", "memory"]
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class PersistenceConfig:
|
|
116
|
+
"""
|
|
117
|
+
Configuration for PraisonAI persistence layer.
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
config = PersistenceConfig(
|
|
121
|
+
conversation_store="postgres",
|
|
122
|
+
conversation_url="postgresql://localhost:5432/praisonai",
|
|
123
|
+
knowledge_store="qdrant",
|
|
124
|
+
knowledge_url="http://localhost:6333",
|
|
125
|
+
state_store="redis",
|
|
126
|
+
state_url="redis://localhost:6379",
|
|
127
|
+
)
|
|
128
|
+
"""
|
|
129
|
+
# Store backends
|
|
130
|
+
conversation_store: Optional[str] = None
|
|
131
|
+
conversation_url: Optional[str] = None
|
|
132
|
+
conversation_options: Dict[str, Any] = field(default_factory=dict)
|
|
133
|
+
|
|
134
|
+
knowledge_store: Optional[str] = None
|
|
135
|
+
knowledge_url: Optional[str] = None
|
|
136
|
+
knowledge_options: Dict[str, Any] = field(default_factory=dict)
|
|
137
|
+
|
|
138
|
+
state_store: Optional[str] = None
|
|
139
|
+
state_url: Optional[str] = None
|
|
140
|
+
state_options: Dict[str, Any] = field(default_factory=dict)
|
|
141
|
+
|
|
142
|
+
# Session/user context
|
|
143
|
+
session_id: Optional[str] = None
|
|
144
|
+
user_id: Optional[str] = None
|
|
145
|
+
|
|
146
|
+
# General options
|
|
147
|
+
auto_create_tables: bool = True
|
|
148
|
+
lazy_init: bool = True
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def from_env(cls) -> "PersistenceConfig":
|
|
152
|
+
"""Create config from environment variables."""
|
|
153
|
+
return cls(
|
|
154
|
+
conversation_store=os.getenv(ENV_VARS["conversation_store"]),
|
|
155
|
+
conversation_url=os.getenv(ENV_VARS["conversation_url"]) or os.getenv(ENV_VARS["postgres_url"]),
|
|
156
|
+
knowledge_store=os.getenv(ENV_VARS["knowledge_store"]),
|
|
157
|
+
knowledge_url=os.getenv(ENV_VARS["knowledge_url"]) or os.getenv(ENV_VARS["qdrant_url"]),
|
|
158
|
+
state_store=os.getenv(ENV_VARS["state_store"]),
|
|
159
|
+
state_url=os.getenv(ENV_VARS["state_url"]) or os.getenv(ENV_VARS["redis_url"]),
|
|
160
|
+
session_id=os.getenv(ENV_VARS["session_id"]),
|
|
161
|
+
user_id=os.getenv(ENV_VARS["user_id"]),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def from_dict(cls, data: Dict[str, Any]) -> "PersistenceConfig":
|
|
166
|
+
"""Create config from dictionary."""
|
|
167
|
+
return cls(**{k: v for k, v in data.items() if hasattr(cls, k)})
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def from_yaml(cls, path: str) -> "PersistenceConfig":
|
|
171
|
+
"""Load config from YAML file."""
|
|
172
|
+
try:
|
|
173
|
+
import yaml
|
|
174
|
+
except ImportError:
|
|
175
|
+
raise ImportError("PyYAML required for YAML config. Install with: pip install pyyaml")
|
|
176
|
+
|
|
177
|
+
with open(path) as f:
|
|
178
|
+
data = yaml.safe_load(f)
|
|
179
|
+
|
|
180
|
+
# Handle nested persistence config
|
|
181
|
+
if "persistence" in data:
|
|
182
|
+
data = data["persistence"]
|
|
183
|
+
|
|
184
|
+
return cls.from_dict(data)
|
|
185
|
+
|
|
186
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
187
|
+
"""Convert config to dictionary."""
|
|
188
|
+
return {
|
|
189
|
+
"conversation_store": self.conversation_store,
|
|
190
|
+
"conversation_url": self.conversation_url,
|
|
191
|
+
"conversation_options": self.conversation_options,
|
|
192
|
+
"knowledge_store": self.knowledge_store,
|
|
193
|
+
"knowledge_url": self.knowledge_url,
|
|
194
|
+
"knowledge_options": self.knowledge_options,
|
|
195
|
+
"state_store": self.state_store,
|
|
196
|
+
"state_url": self.state_url,
|
|
197
|
+
"state_options": self.state_options,
|
|
198
|
+
"session_id": self.session_id,
|
|
199
|
+
"user_id": self.user_id,
|
|
200
|
+
"auto_create_tables": self.auto_create_tables,
|
|
201
|
+
"lazy_init": self.lazy_init,
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
def validate(self) -> bool:
|
|
205
|
+
"""Validate configuration."""
|
|
206
|
+
valid = True
|
|
207
|
+
|
|
208
|
+
if self.conversation_store and self.conversation_store not in CONVERSATION_BACKENDS:
|
|
209
|
+
logger.error(f"Invalid conversation_store: {self.conversation_store}. "
|
|
210
|
+
f"Supported: {CONVERSATION_BACKENDS}")
|
|
211
|
+
valid = False
|
|
212
|
+
|
|
213
|
+
if self.knowledge_store and self.knowledge_store not in KNOWLEDGE_BACKENDS:
|
|
214
|
+
logger.error(f"Invalid knowledge_store: {self.knowledge_store}. "
|
|
215
|
+
f"Supported: {KNOWLEDGE_BACKENDS}")
|
|
216
|
+
valid = False
|
|
217
|
+
|
|
218
|
+
if self.state_store and self.state_store not in STATE_BACKENDS:
|
|
219
|
+
logger.error(f"Invalid state_store: {self.state_store}. "
|
|
220
|
+
f"Supported: {STATE_BACKENDS}")
|
|
221
|
+
valid = False
|
|
222
|
+
|
|
223
|
+
return valid
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def get_env_var(key: str, default: Optional[str] = None) -> Optional[str]:
|
|
227
|
+
"""Get environment variable by config key name."""
|
|
228
|
+
env_name = ENV_VARS.get(key, key.upper())
|
|
229
|
+
return os.getenv(env_name, default)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def list_available_backends() -> Dict[str, list]:
|
|
233
|
+
"""List all available backends by store type."""
|
|
234
|
+
return {
|
|
235
|
+
"conversation": CONVERSATION_BACKENDS,
|
|
236
|
+
"knowledge": KNOWLEDGE_BACKENDS,
|
|
237
|
+
"state": STATE_BACKENDS,
|
|
238
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ConversationStore implementations for session and message persistence.
|
|
3
|
+
|
|
4
|
+
Supported backends:
|
|
5
|
+
- PostgreSQL (postgres)
|
|
6
|
+
- MySQL (mysql)
|
|
7
|
+
- SQLite (sqlite)
|
|
8
|
+
- SingleStore (singlestore)
|
|
9
|
+
- Supabase (supabase)
|
|
10
|
+
- SurrealDB (surrealdb)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"ConversationStore",
|
|
17
|
+
"ConversationSession",
|
|
18
|
+
"ConversationMessage",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
def __getattr__(name: str):
|
|
22
|
+
if name in ("ConversationStore", "ConversationSession", "ConversationMessage"):
|
|
23
|
+
from .base import ConversationStore, ConversationSession, ConversationMessage
|
|
24
|
+
return locals()[name]
|
|
25
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async MySQL implementation of ConversationStore.
|
|
3
|
+
|
|
4
|
+
Requires: aiomysql
|
|
5
|
+
Install: pip install aiomysql
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from typing import List, Optional
|
|
13
|
+
|
|
14
|
+
from .base import ConversationStore, ConversationSession, ConversationMessage
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AsyncMySQLConversationStore(ConversationStore):
|
|
20
|
+
"""
|
|
21
|
+
Async MySQL conversation store using aiomysql.
|
|
22
|
+
|
|
23
|
+
Provides high-performance async database operations.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
store = AsyncMySQLConversationStore(
|
|
27
|
+
host="localhost",
|
|
28
|
+
user="root",
|
|
29
|
+
password="password",
|
|
30
|
+
database="praisonai"
|
|
31
|
+
)
|
|
32
|
+
await store.init()
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
url: Optional[str] = None,
|
|
38
|
+
host: str = "localhost",
|
|
39
|
+
port: int = 3306,
|
|
40
|
+
database: str = "praisonai",
|
|
41
|
+
user: str = "root",
|
|
42
|
+
password: str = "",
|
|
43
|
+
table_prefix: str = "praisonai_",
|
|
44
|
+
pool_size: int = 10,
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
Initialize async MySQL store.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
url: MySQL connection URL (takes precedence)
|
|
51
|
+
host: Database host
|
|
52
|
+
port: Database port
|
|
53
|
+
database: Database name
|
|
54
|
+
user: Database user
|
|
55
|
+
password: Database password
|
|
56
|
+
table_prefix: Prefix for table names
|
|
57
|
+
pool_size: Connection pool size
|
|
58
|
+
"""
|
|
59
|
+
self.url = url
|
|
60
|
+
self.host = host
|
|
61
|
+
self.port = port
|
|
62
|
+
self.database = database
|
|
63
|
+
self.user = user
|
|
64
|
+
self.password = password
|
|
65
|
+
self.table_prefix = table_prefix
|
|
66
|
+
self.pool_size = pool_size
|
|
67
|
+
self._pool = None
|
|
68
|
+
self._initialized = False
|
|
69
|
+
|
|
70
|
+
if url:
|
|
71
|
+
self._parse_url(url)
|
|
72
|
+
|
|
73
|
+
def _parse_url(self, url: str):
|
|
74
|
+
"""Parse MySQL URL into components."""
|
|
75
|
+
from urllib.parse import urlparse
|
|
76
|
+
parsed = urlparse(url)
|
|
77
|
+
self.host = parsed.hostname or "localhost"
|
|
78
|
+
self.port = parsed.port or 3306
|
|
79
|
+
self.database = parsed.path.lstrip('/') or "praisonai"
|
|
80
|
+
self.user = parsed.username or "root"
|
|
81
|
+
self.password = parsed.password or ""
|
|
82
|
+
|
|
83
|
+
async def init(self):
|
|
84
|
+
"""Initialize connection pool and create tables."""
|
|
85
|
+
if self._initialized:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
import aiomysql
|
|
90
|
+
except ImportError:
|
|
91
|
+
raise ImportError(
|
|
92
|
+
"aiomysql is required for async MySQL support. "
|
|
93
|
+
"Install with: pip install aiomysql"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
self._pool = await aiomysql.create_pool(
|
|
97
|
+
host=self.host,
|
|
98
|
+
port=self.port,
|
|
99
|
+
user=self.user,
|
|
100
|
+
password=self.password,
|
|
101
|
+
db=self.database,
|
|
102
|
+
minsize=1,
|
|
103
|
+
maxsize=self.pool_size,
|
|
104
|
+
autocommit=True
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
await self._create_tables()
|
|
108
|
+
self._initialized = True
|
|
109
|
+
|
|
110
|
+
async def _create_tables(self):
|
|
111
|
+
"""Create required tables if they don't exist."""
|
|
112
|
+
sessions_table = f"{self.table_prefix}sessions"
|
|
113
|
+
messages_table = f"{self.table_prefix}messages"
|
|
114
|
+
|
|
115
|
+
async with self._pool.acquire() as conn:
|
|
116
|
+
async with conn.cursor() as cur:
|
|
117
|
+
await cur.execute(f"""
|
|
118
|
+
CREATE TABLE IF NOT EXISTS {sessions_table} (
|
|
119
|
+
session_id VARCHAR(255) PRIMARY KEY,
|
|
120
|
+
user_id VARCHAR(255),
|
|
121
|
+
agent_id VARCHAR(255),
|
|
122
|
+
name VARCHAR(255),
|
|
123
|
+
metadata JSON,
|
|
124
|
+
created_at DOUBLE,
|
|
125
|
+
updated_at DOUBLE,
|
|
126
|
+
INDEX idx_user_id (user_id),
|
|
127
|
+
INDEX idx_agent_id (agent_id)
|
|
128
|
+
)
|
|
129
|
+
""")
|
|
130
|
+
|
|
131
|
+
await cur.execute(f"""
|
|
132
|
+
CREATE TABLE IF NOT EXISTS {messages_table} (
|
|
133
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
134
|
+
session_id VARCHAR(255),
|
|
135
|
+
role VARCHAR(50),
|
|
136
|
+
content TEXT,
|
|
137
|
+
metadata JSON,
|
|
138
|
+
created_at DOUBLE,
|
|
139
|
+
INDEX idx_session_id (session_id),
|
|
140
|
+
FOREIGN KEY (session_id) REFERENCES {sessions_table}(session_id) ON DELETE CASCADE
|
|
141
|
+
)
|
|
142
|
+
""")
|
|
143
|
+
|
|
144
|
+
async def async_create_session(self, session: ConversationSession) -> ConversationSession:
|
|
145
|
+
"""Create a new session asynchronously."""
|
|
146
|
+
if not self._initialized:
|
|
147
|
+
await self.init()
|
|
148
|
+
|
|
149
|
+
table = f"{self.table_prefix}sessions"
|
|
150
|
+
async with self._pool.acquire() as conn:
|
|
151
|
+
async with conn.cursor() as cur:
|
|
152
|
+
await cur.execute(f"""
|
|
153
|
+
INSERT INTO {table} (session_id, user_id, agent_id, name, metadata, created_at, updated_at)
|
|
154
|
+
VALUES (%s, %s, %s, %s, %s, %s, %s)
|
|
155
|
+
""", (session.session_id, session.user_id, session.agent_id, session.name,
|
|
156
|
+
json.dumps(session.metadata) if session.metadata else None,
|
|
157
|
+
session.created_at, session.updated_at))
|
|
158
|
+
|
|
159
|
+
return session
|
|
160
|
+
|
|
161
|
+
def create_session(self, session: ConversationSession) -> ConversationSession:
|
|
162
|
+
"""Sync wrapper for create_session."""
|
|
163
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
164
|
+
self.async_create_session(session)
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
async def async_get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
168
|
+
"""Get a session by ID asynchronously."""
|
|
169
|
+
if not self._initialized:
|
|
170
|
+
await self.init()
|
|
171
|
+
|
|
172
|
+
table = f"{self.table_prefix}sessions"
|
|
173
|
+
async with self._pool.acquire() as conn:
|
|
174
|
+
async with conn.cursor() as cur:
|
|
175
|
+
await cur.execute(f"""
|
|
176
|
+
SELECT session_id, user_id, agent_id, name, metadata, created_at, updated_at
|
|
177
|
+
FROM {table} WHERE session_id = %s
|
|
178
|
+
""", (session_id,))
|
|
179
|
+
row = await cur.fetchone()
|
|
180
|
+
|
|
181
|
+
if row:
|
|
182
|
+
return ConversationSession(
|
|
183
|
+
session_id=row[0],
|
|
184
|
+
user_id=row[1],
|
|
185
|
+
agent_id=row[2],
|
|
186
|
+
name=row[3],
|
|
187
|
+
metadata=json.loads(row[4]) if row[4] else None,
|
|
188
|
+
created_at=row[5],
|
|
189
|
+
updated_at=row[6]
|
|
190
|
+
)
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
194
|
+
"""Sync wrapper for get_session."""
|
|
195
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
196
|
+
self.async_get_session(session_id)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
async def async_update_session(self, session: ConversationSession) -> ConversationSession:
|
|
200
|
+
"""Update an existing session asynchronously."""
|
|
201
|
+
if not self._initialized:
|
|
202
|
+
await self.init()
|
|
203
|
+
|
|
204
|
+
session.updated_at = time.time()
|
|
205
|
+
table = f"{self.table_prefix}sessions"
|
|
206
|
+
|
|
207
|
+
async with self._pool.acquire() as conn:
|
|
208
|
+
async with conn.cursor() as cur:
|
|
209
|
+
await cur.execute(f"""
|
|
210
|
+
UPDATE {table}
|
|
211
|
+
SET name = %s, metadata = %s, updated_at = %s
|
|
212
|
+
WHERE session_id = %s
|
|
213
|
+
""", (session.name, json.dumps(session.metadata) if session.metadata else None,
|
|
214
|
+
session.updated_at, session.session_id))
|
|
215
|
+
|
|
216
|
+
return session
|
|
217
|
+
|
|
218
|
+
def update_session(self, session: ConversationSession) -> ConversationSession:
|
|
219
|
+
"""Sync wrapper for update_session."""
|
|
220
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
221
|
+
self.async_update_session(session)
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
async def async_delete_session(self, session_id: str) -> bool:
|
|
225
|
+
"""Delete a session asynchronously."""
|
|
226
|
+
if not self._initialized:
|
|
227
|
+
await self.init()
|
|
228
|
+
|
|
229
|
+
table = f"{self.table_prefix}sessions"
|
|
230
|
+
async with self._pool.acquire() as conn:
|
|
231
|
+
async with conn.cursor() as cur:
|
|
232
|
+
await cur.execute(f"""
|
|
233
|
+
DELETE FROM {table} WHERE session_id = %s
|
|
234
|
+
""", (session_id,))
|
|
235
|
+
return cur.rowcount > 0
|
|
236
|
+
|
|
237
|
+
def delete_session(self, session_id: str) -> bool:
|
|
238
|
+
"""Sync wrapper for delete_session."""
|
|
239
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
240
|
+
self.async_delete_session(session_id)
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
async def async_list_sessions(
|
|
244
|
+
self,
|
|
245
|
+
user_id: Optional[str] = None,
|
|
246
|
+
agent_id: Optional[str] = None,
|
|
247
|
+
limit: int = 100,
|
|
248
|
+
offset: int = 0
|
|
249
|
+
) -> List[ConversationSession]:
|
|
250
|
+
"""List sessions asynchronously."""
|
|
251
|
+
if not self._initialized:
|
|
252
|
+
await self.init()
|
|
253
|
+
|
|
254
|
+
table = f"{self.table_prefix}sessions"
|
|
255
|
+
conditions = []
|
|
256
|
+
params = []
|
|
257
|
+
|
|
258
|
+
if user_id:
|
|
259
|
+
conditions.append("user_id = %s")
|
|
260
|
+
params.append(user_id)
|
|
261
|
+
|
|
262
|
+
if agent_id:
|
|
263
|
+
conditions.append("agent_id = %s")
|
|
264
|
+
params.append(agent_id)
|
|
265
|
+
|
|
266
|
+
where_clause = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
|
267
|
+
params.extend([limit, offset])
|
|
268
|
+
|
|
269
|
+
async with self._pool.acquire() as conn:
|
|
270
|
+
async with conn.cursor() as cur:
|
|
271
|
+
await cur.execute(f"""
|
|
272
|
+
SELECT session_id, user_id, agent_id, name, metadata, created_at, updated_at
|
|
273
|
+
FROM {table} {where_clause}
|
|
274
|
+
ORDER BY updated_at DESC
|
|
275
|
+
LIMIT %s OFFSET %s
|
|
276
|
+
""", params)
|
|
277
|
+
rows = await cur.fetchall()
|
|
278
|
+
|
|
279
|
+
return [
|
|
280
|
+
ConversationSession(
|
|
281
|
+
session_id=row[0],
|
|
282
|
+
user_id=row[1],
|
|
283
|
+
agent_id=row[2],
|
|
284
|
+
name=row[3],
|
|
285
|
+
metadata=json.loads(row[4]) if row[4] else None,
|
|
286
|
+
created_at=row[5],
|
|
287
|
+
updated_at=row[6]
|
|
288
|
+
)
|
|
289
|
+
for row in rows
|
|
290
|
+
]
|
|
291
|
+
|
|
292
|
+
def list_sessions(
|
|
293
|
+
self,
|
|
294
|
+
user_id: Optional[str] = None,
|
|
295
|
+
agent_id: Optional[str] = None,
|
|
296
|
+
limit: int = 100,
|
|
297
|
+
offset: int = 0
|
|
298
|
+
) -> List[ConversationSession]:
|
|
299
|
+
"""Sync wrapper for list_sessions."""
|
|
300
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
301
|
+
self.async_list_sessions(user_id, agent_id, limit, offset)
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
async def async_add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
305
|
+
"""Add a message asynchronously."""
|
|
306
|
+
if not self._initialized:
|
|
307
|
+
await self.init()
|
|
308
|
+
|
|
309
|
+
message.session_id = session_id
|
|
310
|
+
table = f"{self.table_prefix}messages"
|
|
311
|
+
|
|
312
|
+
async with self._pool.acquire() as conn:
|
|
313
|
+
async with conn.cursor() as cur:
|
|
314
|
+
await cur.execute(f"""
|
|
315
|
+
INSERT INTO {table} (id, session_id, role, content, metadata, created_at)
|
|
316
|
+
VALUES (%s, %s, %s, %s, %s, %s)
|
|
317
|
+
""", (message.id, session_id, message.role, message.content,
|
|
318
|
+
json.dumps(message.metadata) if message.metadata else None,
|
|
319
|
+
message.created_at))
|
|
320
|
+
|
|
321
|
+
return message
|
|
322
|
+
|
|
323
|
+
def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
324
|
+
"""Sync wrapper for add_message."""
|
|
325
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
326
|
+
self.async_add_message(session_id, message)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
async def async_get_messages(
|
|
330
|
+
self,
|
|
331
|
+
session_id: str,
|
|
332
|
+
limit: Optional[int] = None,
|
|
333
|
+
before: Optional[float] = None,
|
|
334
|
+
after: Optional[float] = None
|
|
335
|
+
) -> List[ConversationMessage]:
|
|
336
|
+
"""Get messages asynchronously."""
|
|
337
|
+
if not self._initialized:
|
|
338
|
+
await self.init()
|
|
339
|
+
|
|
340
|
+
table = f"{self.table_prefix}messages"
|
|
341
|
+
conditions = ["session_id = %s"]
|
|
342
|
+
params = [session_id]
|
|
343
|
+
|
|
344
|
+
if before:
|
|
345
|
+
conditions.append("created_at < %s")
|
|
346
|
+
params.append(before)
|
|
347
|
+
|
|
348
|
+
if after:
|
|
349
|
+
conditions.append("created_at > %s")
|
|
350
|
+
params.append(after)
|
|
351
|
+
|
|
352
|
+
where_clause = f"WHERE {' AND '.join(conditions)}"
|
|
353
|
+
limit_clause = "LIMIT %s" if limit else ""
|
|
354
|
+
if limit:
|
|
355
|
+
params.append(limit)
|
|
356
|
+
|
|
357
|
+
async with self._pool.acquire() as conn:
|
|
358
|
+
async with conn.cursor() as cur:
|
|
359
|
+
await cur.execute(f"""
|
|
360
|
+
SELECT id, session_id, role, content, metadata, created_at
|
|
361
|
+
FROM {table} {where_clause}
|
|
362
|
+
ORDER BY created_at ASC {limit_clause}
|
|
363
|
+
""", params)
|
|
364
|
+
rows = await cur.fetchall()
|
|
365
|
+
|
|
366
|
+
return [
|
|
367
|
+
ConversationMessage(
|
|
368
|
+
id=row[0],
|
|
369
|
+
session_id=row[1],
|
|
370
|
+
role=row[2],
|
|
371
|
+
content=row[3],
|
|
372
|
+
metadata=json.loads(row[4]) if row[4] else None,
|
|
373
|
+
created_at=row[5]
|
|
374
|
+
)
|
|
375
|
+
for row in rows
|
|
376
|
+
]
|
|
377
|
+
|
|
378
|
+
def get_messages(
|
|
379
|
+
self,
|
|
380
|
+
session_id: str,
|
|
381
|
+
limit: Optional[int] = None,
|
|
382
|
+
before: Optional[float] = None,
|
|
383
|
+
after: Optional[float] = None
|
|
384
|
+
) -> List[ConversationMessage]:
|
|
385
|
+
"""Sync wrapper for get_messages."""
|
|
386
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
387
|
+
self.async_get_messages(session_id, limit, before, after)
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
async def async_delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
391
|
+
"""Delete messages asynchronously."""
|
|
392
|
+
if not self._initialized:
|
|
393
|
+
await self.init()
|
|
394
|
+
|
|
395
|
+
table = f"{self.table_prefix}messages"
|
|
396
|
+
|
|
397
|
+
async with self._pool.acquire() as conn:
|
|
398
|
+
async with conn.cursor() as cur:
|
|
399
|
+
if message_ids is None:
|
|
400
|
+
await cur.execute(f"""
|
|
401
|
+
DELETE FROM {table} WHERE session_id = %s
|
|
402
|
+
""", (session_id,))
|
|
403
|
+
else:
|
|
404
|
+
placeholders = ','.join(['%s'] * len(message_ids))
|
|
405
|
+
await cur.execute(f"""
|
|
406
|
+
DELETE FROM {table} WHERE session_id = %s AND id IN ({placeholders})
|
|
407
|
+
""", [session_id] + message_ids)
|
|
408
|
+
return cur.rowcount
|
|
409
|
+
|
|
410
|
+
def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
411
|
+
"""Sync wrapper for delete_messages."""
|
|
412
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
413
|
+
self.async_delete_messages(session_id, message_ids)
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
async def async_close(self) -> None:
|
|
417
|
+
"""Close the connection pool."""
|
|
418
|
+
if self._pool:
|
|
419
|
+
self._pool.close()
|
|
420
|
+
await self._pool.wait_closed()
|
|
421
|
+
self._pool = None
|
|
422
|
+
self._initialized = False
|
|
423
|
+
|
|
424
|
+
def close(self) -> None:
|
|
425
|
+
"""Sync wrapper for close."""
|
|
426
|
+
if self._pool:
|
|
427
|
+
asyncio.get_event_loop().run_until_complete(self.async_close())
|