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,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async SQLite implementation of ConversationStore.
|
|
3
|
+
|
|
4
|
+
Requires: aiosqlite
|
|
5
|
+
Install: pip install aiosqlite
|
|
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 AsyncSQLiteConversationStore(ConversationStore):
|
|
20
|
+
"""
|
|
21
|
+
Async SQLite conversation store using aiosqlite.
|
|
22
|
+
|
|
23
|
+
Provides async database operations for SQLite.
|
|
24
|
+
|
|
25
|
+
Example:
|
|
26
|
+
store = AsyncSQLiteConversationStore(path="./conversations.db")
|
|
27
|
+
await store.init()
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
path: str = "praisonai_conversations.db",
|
|
33
|
+
table_prefix: str = "praisonai_",
|
|
34
|
+
):
|
|
35
|
+
"""
|
|
36
|
+
Initialize async SQLite store.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
path: Path to SQLite database file
|
|
40
|
+
table_prefix: Prefix for table names
|
|
41
|
+
"""
|
|
42
|
+
self.path = path
|
|
43
|
+
self.table_prefix = table_prefix
|
|
44
|
+
self._conn = None
|
|
45
|
+
self._initialized = False
|
|
46
|
+
|
|
47
|
+
async def init(self):
|
|
48
|
+
"""Initialize connection and create tables."""
|
|
49
|
+
if self._initialized:
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
import aiosqlite
|
|
54
|
+
except ImportError:
|
|
55
|
+
raise ImportError(
|
|
56
|
+
"aiosqlite is required for async SQLite support. "
|
|
57
|
+
"Install with: pip install aiosqlite"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
self._aiosqlite = aiosqlite
|
|
61
|
+
self._conn = await aiosqlite.connect(self.path)
|
|
62
|
+
self._conn.row_factory = aiosqlite.Row
|
|
63
|
+
await self._create_tables()
|
|
64
|
+
self._initialized = True
|
|
65
|
+
|
|
66
|
+
async def _create_tables(self):
|
|
67
|
+
"""Create required tables if they don't exist."""
|
|
68
|
+
sessions_table = f"{self.table_prefix}sessions"
|
|
69
|
+
messages_table = f"{self.table_prefix}messages"
|
|
70
|
+
|
|
71
|
+
await self._conn.execute(f"""
|
|
72
|
+
CREATE TABLE IF NOT EXISTS {sessions_table} (
|
|
73
|
+
session_id TEXT PRIMARY KEY,
|
|
74
|
+
user_id TEXT,
|
|
75
|
+
agent_id TEXT,
|
|
76
|
+
name TEXT,
|
|
77
|
+
metadata TEXT,
|
|
78
|
+
created_at REAL,
|
|
79
|
+
updated_at REAL
|
|
80
|
+
)
|
|
81
|
+
""")
|
|
82
|
+
|
|
83
|
+
await self._conn.execute(f"""
|
|
84
|
+
CREATE TABLE IF NOT EXISTS {messages_table} (
|
|
85
|
+
id TEXT PRIMARY KEY,
|
|
86
|
+
session_id TEXT REFERENCES {sessions_table}(session_id) ON DELETE CASCADE,
|
|
87
|
+
role TEXT,
|
|
88
|
+
content TEXT,
|
|
89
|
+
metadata TEXT,
|
|
90
|
+
created_at REAL
|
|
91
|
+
)
|
|
92
|
+
""")
|
|
93
|
+
|
|
94
|
+
await self._conn.execute(f"""
|
|
95
|
+
CREATE INDEX IF NOT EXISTS idx_{messages_table}_session
|
|
96
|
+
ON {messages_table}(session_id)
|
|
97
|
+
""")
|
|
98
|
+
|
|
99
|
+
await self._conn.commit()
|
|
100
|
+
|
|
101
|
+
async def async_create_session(self, session: ConversationSession) -> ConversationSession:
|
|
102
|
+
"""Create a new session asynchronously."""
|
|
103
|
+
if not self._initialized:
|
|
104
|
+
await self.init()
|
|
105
|
+
|
|
106
|
+
table = f"{self.table_prefix}sessions"
|
|
107
|
+
await self._conn.execute(f"""
|
|
108
|
+
INSERT INTO {table} (session_id, user_id, agent_id, name, metadata, created_at, updated_at)
|
|
109
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
110
|
+
""", (session.session_id, session.user_id, session.agent_id, session.name,
|
|
111
|
+
json.dumps(session.metadata) if session.metadata else None,
|
|
112
|
+
session.created_at, session.updated_at))
|
|
113
|
+
await self._conn.commit()
|
|
114
|
+
|
|
115
|
+
return session
|
|
116
|
+
|
|
117
|
+
def create_session(self, session: ConversationSession) -> ConversationSession:
|
|
118
|
+
"""Sync wrapper for create_session."""
|
|
119
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
120
|
+
self.async_create_session(session)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
async def async_get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
124
|
+
"""Get a session by ID asynchronously."""
|
|
125
|
+
if not self._initialized:
|
|
126
|
+
await self.init()
|
|
127
|
+
|
|
128
|
+
table = f"{self.table_prefix}sessions"
|
|
129
|
+
async with self._conn.execute(f"""
|
|
130
|
+
SELECT * FROM {table} WHERE session_id = ?
|
|
131
|
+
""", (session_id,)) as cursor:
|
|
132
|
+
row = await cursor.fetchone()
|
|
133
|
+
|
|
134
|
+
if row:
|
|
135
|
+
return ConversationSession(
|
|
136
|
+
session_id=row['session_id'],
|
|
137
|
+
user_id=row['user_id'],
|
|
138
|
+
agent_id=row['agent_id'],
|
|
139
|
+
name=row['name'],
|
|
140
|
+
metadata=json.loads(row['metadata']) if row['metadata'] else None,
|
|
141
|
+
created_at=row['created_at'],
|
|
142
|
+
updated_at=row['updated_at']
|
|
143
|
+
)
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
147
|
+
"""Sync wrapper for get_session."""
|
|
148
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
149
|
+
self.async_get_session(session_id)
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
async def async_update_session(self, session: ConversationSession) -> ConversationSession:
|
|
153
|
+
"""Update an existing session asynchronously."""
|
|
154
|
+
if not self._initialized:
|
|
155
|
+
await self.init()
|
|
156
|
+
|
|
157
|
+
session.updated_at = time.time()
|
|
158
|
+
table = f"{self.table_prefix}sessions"
|
|
159
|
+
|
|
160
|
+
await self._conn.execute(f"""
|
|
161
|
+
UPDATE {table}
|
|
162
|
+
SET name = ?, metadata = ?, updated_at = ?
|
|
163
|
+
WHERE session_id = ?
|
|
164
|
+
""", (session.name, json.dumps(session.metadata) if session.metadata else None,
|
|
165
|
+
session.updated_at, session.session_id))
|
|
166
|
+
await self._conn.commit()
|
|
167
|
+
|
|
168
|
+
return session
|
|
169
|
+
|
|
170
|
+
def update_session(self, session: ConversationSession) -> ConversationSession:
|
|
171
|
+
"""Sync wrapper for update_session."""
|
|
172
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
173
|
+
self.async_update_session(session)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
async def async_delete_session(self, session_id: str) -> bool:
|
|
177
|
+
"""Delete a session asynchronously."""
|
|
178
|
+
if not self._initialized:
|
|
179
|
+
await self.init()
|
|
180
|
+
|
|
181
|
+
table = f"{self.table_prefix}sessions"
|
|
182
|
+
cursor = await self._conn.execute(f"""
|
|
183
|
+
DELETE FROM {table} WHERE session_id = ?
|
|
184
|
+
""", (session_id,))
|
|
185
|
+
await self._conn.commit()
|
|
186
|
+
|
|
187
|
+
return cursor.rowcount > 0
|
|
188
|
+
|
|
189
|
+
def delete_session(self, session_id: str) -> bool:
|
|
190
|
+
"""Sync wrapper for delete_session."""
|
|
191
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
192
|
+
self.async_delete_session(session_id)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
async def async_list_sessions(
|
|
196
|
+
self,
|
|
197
|
+
user_id: Optional[str] = None,
|
|
198
|
+
agent_id: Optional[str] = None,
|
|
199
|
+
limit: int = 100,
|
|
200
|
+
offset: int = 0
|
|
201
|
+
) -> List[ConversationSession]:
|
|
202
|
+
"""List sessions asynchronously."""
|
|
203
|
+
if not self._initialized:
|
|
204
|
+
await self.init()
|
|
205
|
+
|
|
206
|
+
table = f"{self.table_prefix}sessions"
|
|
207
|
+
conditions = []
|
|
208
|
+
params = []
|
|
209
|
+
|
|
210
|
+
if user_id:
|
|
211
|
+
conditions.append("user_id = ?")
|
|
212
|
+
params.append(user_id)
|
|
213
|
+
|
|
214
|
+
if agent_id:
|
|
215
|
+
conditions.append("agent_id = ?")
|
|
216
|
+
params.append(agent_id)
|
|
217
|
+
|
|
218
|
+
where_clause = f"WHERE {' AND '.join(conditions)}" if conditions else ""
|
|
219
|
+
params.extend([limit, offset])
|
|
220
|
+
|
|
221
|
+
async with self._conn.execute(f"""
|
|
222
|
+
SELECT * FROM {table} {where_clause}
|
|
223
|
+
ORDER BY updated_at DESC
|
|
224
|
+
LIMIT ? OFFSET ?
|
|
225
|
+
""", params) as cursor:
|
|
226
|
+
rows = await cursor.fetchall()
|
|
227
|
+
|
|
228
|
+
return [
|
|
229
|
+
ConversationSession(
|
|
230
|
+
session_id=row['session_id'],
|
|
231
|
+
user_id=row['user_id'],
|
|
232
|
+
agent_id=row['agent_id'],
|
|
233
|
+
name=row['name'],
|
|
234
|
+
metadata=json.loads(row['metadata']) if row['metadata'] else None,
|
|
235
|
+
created_at=row['created_at'],
|
|
236
|
+
updated_at=row['updated_at']
|
|
237
|
+
)
|
|
238
|
+
for row in rows
|
|
239
|
+
]
|
|
240
|
+
|
|
241
|
+
def list_sessions(
|
|
242
|
+
self,
|
|
243
|
+
user_id: Optional[str] = None,
|
|
244
|
+
agent_id: Optional[str] = None,
|
|
245
|
+
limit: int = 100,
|
|
246
|
+
offset: int = 0
|
|
247
|
+
) -> List[ConversationSession]:
|
|
248
|
+
"""Sync wrapper for list_sessions."""
|
|
249
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
250
|
+
self.async_list_sessions(user_id, agent_id, limit, offset)
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
async def async_add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
254
|
+
"""Add a message asynchronously."""
|
|
255
|
+
if not self._initialized:
|
|
256
|
+
await self.init()
|
|
257
|
+
|
|
258
|
+
message.session_id = session_id
|
|
259
|
+
table = f"{self.table_prefix}messages"
|
|
260
|
+
|
|
261
|
+
await self._conn.execute(f"""
|
|
262
|
+
INSERT INTO {table} (id, session_id, role, content, metadata, created_at)
|
|
263
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
264
|
+
""", (message.id, session_id, message.role, message.content,
|
|
265
|
+
json.dumps(message.metadata) if message.metadata else None,
|
|
266
|
+
message.created_at))
|
|
267
|
+
await self._conn.commit()
|
|
268
|
+
|
|
269
|
+
return message
|
|
270
|
+
|
|
271
|
+
def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
272
|
+
"""Sync wrapper for add_message."""
|
|
273
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
274
|
+
self.async_add_message(session_id, message)
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
async def async_get_messages(
|
|
278
|
+
self,
|
|
279
|
+
session_id: str,
|
|
280
|
+
limit: Optional[int] = None,
|
|
281
|
+
before: Optional[float] = None,
|
|
282
|
+
after: Optional[float] = None
|
|
283
|
+
) -> List[ConversationMessage]:
|
|
284
|
+
"""Get messages asynchronously."""
|
|
285
|
+
if not self._initialized:
|
|
286
|
+
await self.init()
|
|
287
|
+
|
|
288
|
+
table = f"{self.table_prefix}messages"
|
|
289
|
+
conditions = ["session_id = ?"]
|
|
290
|
+
params = [session_id]
|
|
291
|
+
|
|
292
|
+
if before:
|
|
293
|
+
conditions.append("created_at < ?")
|
|
294
|
+
params.append(before)
|
|
295
|
+
|
|
296
|
+
if after:
|
|
297
|
+
conditions.append("created_at > ?")
|
|
298
|
+
params.append(after)
|
|
299
|
+
|
|
300
|
+
where_clause = f"WHERE {' AND '.join(conditions)}"
|
|
301
|
+
limit_clause = f"LIMIT ?" if limit else ""
|
|
302
|
+
if limit:
|
|
303
|
+
params.append(limit)
|
|
304
|
+
|
|
305
|
+
async with self._conn.execute(f"""
|
|
306
|
+
SELECT * FROM {table} {where_clause}
|
|
307
|
+
ORDER BY created_at ASC {limit_clause}
|
|
308
|
+
""", params) as cursor:
|
|
309
|
+
rows = await cursor.fetchall()
|
|
310
|
+
|
|
311
|
+
return [
|
|
312
|
+
ConversationMessage(
|
|
313
|
+
id=row['id'],
|
|
314
|
+
session_id=row['session_id'],
|
|
315
|
+
role=row['role'],
|
|
316
|
+
content=row['content'],
|
|
317
|
+
metadata=json.loads(row['metadata']) if row['metadata'] else None,
|
|
318
|
+
created_at=row['created_at']
|
|
319
|
+
)
|
|
320
|
+
for row in rows
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
def get_messages(
|
|
324
|
+
self,
|
|
325
|
+
session_id: str,
|
|
326
|
+
limit: Optional[int] = None,
|
|
327
|
+
before: Optional[float] = None,
|
|
328
|
+
after: Optional[float] = None
|
|
329
|
+
) -> List[ConversationMessage]:
|
|
330
|
+
"""Sync wrapper for get_messages."""
|
|
331
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
332
|
+
self.async_get_messages(session_id, limit, before, after)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
async def async_delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
336
|
+
"""Delete messages asynchronously."""
|
|
337
|
+
if not self._initialized:
|
|
338
|
+
await self.init()
|
|
339
|
+
|
|
340
|
+
table = f"{self.table_prefix}messages"
|
|
341
|
+
|
|
342
|
+
if message_ids is None:
|
|
343
|
+
cursor = await self._conn.execute(f"""
|
|
344
|
+
DELETE FROM {table} WHERE session_id = ?
|
|
345
|
+
""", (session_id,))
|
|
346
|
+
else:
|
|
347
|
+
placeholders = ','.join(['?' for _ in message_ids])
|
|
348
|
+
cursor = await self._conn.execute(f"""
|
|
349
|
+
DELETE FROM {table} WHERE session_id = ? AND id IN ({placeholders})
|
|
350
|
+
""", [session_id] + message_ids)
|
|
351
|
+
|
|
352
|
+
await self._conn.commit()
|
|
353
|
+
return cursor.rowcount
|
|
354
|
+
|
|
355
|
+
def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
356
|
+
"""Sync wrapper for delete_messages."""
|
|
357
|
+
return asyncio.get_event_loop().run_until_complete(
|
|
358
|
+
self.async_delete_messages(session_id, message_ids)
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
async def async_close(self) -> None:
|
|
362
|
+
"""Close the connection."""
|
|
363
|
+
if self._conn:
|
|
364
|
+
await self._conn.close()
|
|
365
|
+
self._conn = None
|
|
366
|
+
self._initialized = False
|
|
367
|
+
|
|
368
|
+
def close(self) -> None:
|
|
369
|
+
"""Sync wrapper for close."""
|
|
370
|
+
if self._conn:
|
|
371
|
+
asyncio.get_event_loop().run_until_complete(self.async_close())
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base interfaces for ConversationStore.
|
|
3
|
+
|
|
4
|
+
ConversationStore handles session and message persistence for conversation history.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ConversationMessage:
|
|
16
|
+
"""A single message in a conversation."""
|
|
17
|
+
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
18
|
+
session_id: str = ""
|
|
19
|
+
role: str = "" # user, assistant, system, tool
|
|
20
|
+
content: str = ""
|
|
21
|
+
tool_calls: Optional[List[Dict[str, Any]]] = None
|
|
22
|
+
tool_call_id: Optional[str] = None
|
|
23
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
24
|
+
created_at: float = field(default_factory=time.time)
|
|
25
|
+
|
|
26
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
27
|
+
return {
|
|
28
|
+
"id": self.id,
|
|
29
|
+
"session_id": self.session_id,
|
|
30
|
+
"role": self.role,
|
|
31
|
+
"content": self.content,
|
|
32
|
+
"tool_calls": self.tool_calls,
|
|
33
|
+
"tool_call_id": self.tool_call_id,
|
|
34
|
+
"metadata": self.metadata,
|
|
35
|
+
"created_at": self.created_at,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ConversationMessage":
|
|
40
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class ConversationSession:
|
|
45
|
+
"""A conversation session containing messages."""
|
|
46
|
+
session_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
|
47
|
+
user_id: Optional[str] = None
|
|
48
|
+
agent_id: Optional[str] = None
|
|
49
|
+
name: Optional[str] = None
|
|
50
|
+
state: Optional[Dict[str, Any]] = None
|
|
51
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
52
|
+
created_at: float = field(default_factory=time.time)
|
|
53
|
+
updated_at: float = field(default_factory=time.time)
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
56
|
+
return {
|
|
57
|
+
"session_id": self.session_id,
|
|
58
|
+
"user_id": self.user_id,
|
|
59
|
+
"agent_id": self.agent_id,
|
|
60
|
+
"name": self.name,
|
|
61
|
+
"state": self.state,
|
|
62
|
+
"metadata": self.metadata,
|
|
63
|
+
"created_at": self.created_at,
|
|
64
|
+
"updated_at": self.updated_at,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_dict(cls, data: Dict[str, Any]) -> "ConversationSession":
|
|
69
|
+
return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ConversationStore(ABC):
|
|
73
|
+
"""
|
|
74
|
+
Abstract base class for conversation persistence.
|
|
75
|
+
|
|
76
|
+
Implementations handle session and message storage for different backends:
|
|
77
|
+
- PostgreSQL, MySQL, SQLite (relational)
|
|
78
|
+
- SingleStore, Supabase, SurrealDB (hybrid)
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
def create_session(self, session: ConversationSession) -> ConversationSession:
|
|
83
|
+
"""Create a new session."""
|
|
84
|
+
raise NotImplementedError
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
88
|
+
"""Get a session by ID."""
|
|
89
|
+
raise NotImplementedError
|
|
90
|
+
|
|
91
|
+
@abstractmethod
|
|
92
|
+
def update_session(self, session: ConversationSession) -> ConversationSession:
|
|
93
|
+
"""Update an existing session."""
|
|
94
|
+
raise NotImplementedError
|
|
95
|
+
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def delete_session(self, session_id: str) -> bool:
|
|
98
|
+
"""Delete a session and all its messages."""
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
def list_sessions(
|
|
103
|
+
self,
|
|
104
|
+
user_id: Optional[str] = None,
|
|
105
|
+
agent_id: Optional[str] = None,
|
|
106
|
+
limit: int = 100,
|
|
107
|
+
offset: int = 0
|
|
108
|
+
) -> List[ConversationSession]:
|
|
109
|
+
"""List sessions, optionally filtered by user or agent."""
|
|
110
|
+
raise NotImplementedError
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
114
|
+
"""Add a message to a session."""
|
|
115
|
+
raise NotImplementedError
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def get_messages(
|
|
119
|
+
self,
|
|
120
|
+
session_id: str,
|
|
121
|
+
limit: Optional[int] = None,
|
|
122
|
+
before: Optional[float] = None,
|
|
123
|
+
after: Optional[float] = None
|
|
124
|
+
) -> List[ConversationMessage]:
|
|
125
|
+
"""Get messages from a session."""
|
|
126
|
+
raise NotImplementedError
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
130
|
+
"""Delete messages. If message_ids is None, delete all messages in session."""
|
|
131
|
+
raise NotImplementedError
|
|
132
|
+
|
|
133
|
+
def upsert_session(self, session: ConversationSession) -> ConversationSession:
|
|
134
|
+
"""Create or update a session."""
|
|
135
|
+
existing = self.get_session(session.session_id)
|
|
136
|
+
if existing:
|
|
137
|
+
session.updated_at = time.time()
|
|
138
|
+
return self.update_session(session)
|
|
139
|
+
return self.create_session(session)
|
|
140
|
+
|
|
141
|
+
@abstractmethod
|
|
142
|
+
def close(self) -> None:
|
|
143
|
+
"""Close the store and release resources."""
|
|
144
|
+
raise NotImplementedError
|
|
145
|
+
|
|
146
|
+
def __enter__(self):
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
150
|
+
self.close()
|
|
151
|
+
return False
|