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,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
JSON file-based implementation of ConversationStore.
|
|
3
|
+
|
|
4
|
+
Simple file-based storage for development and testing.
|
|
5
|
+
No external dependencies required.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Dict, List, Optional
|
|
13
|
+
import threading
|
|
14
|
+
|
|
15
|
+
from .base import ConversationStore, ConversationSession, ConversationMessage
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class JSONConversationStore(ConversationStore):
|
|
21
|
+
"""
|
|
22
|
+
JSON file-based conversation store.
|
|
23
|
+
|
|
24
|
+
Stores sessions and messages in JSON files.
|
|
25
|
+
Suitable for development, testing, and small-scale deployments.
|
|
26
|
+
|
|
27
|
+
Example:
|
|
28
|
+
store = JSONConversationStore(path="./data/conversations")
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
path: str = "./praisonai_conversations",
|
|
34
|
+
pretty: bool = True,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Initialize JSON conversation store.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
path: Directory path for JSON files
|
|
41
|
+
pretty: Use pretty-printed JSON (default: True)
|
|
42
|
+
"""
|
|
43
|
+
self.path = Path(path)
|
|
44
|
+
self.pretty = pretty
|
|
45
|
+
self._lock = threading.Lock()
|
|
46
|
+
|
|
47
|
+
# Create directory if it doesn't exist
|
|
48
|
+
self.path.mkdir(parents=True, exist_ok=True)
|
|
49
|
+
|
|
50
|
+
# Sessions index file
|
|
51
|
+
self._index_file = self.path / "_sessions_index.json"
|
|
52
|
+
self._load_index()
|
|
53
|
+
|
|
54
|
+
def _load_index(self):
|
|
55
|
+
"""Load or create sessions index."""
|
|
56
|
+
if self._index_file.exists():
|
|
57
|
+
with open(self._index_file, 'r') as f:
|
|
58
|
+
self._index = json.load(f)
|
|
59
|
+
else:
|
|
60
|
+
self._index = {"sessions": {}}
|
|
61
|
+
self._save_index()
|
|
62
|
+
|
|
63
|
+
def _save_index(self):
|
|
64
|
+
"""Save sessions index."""
|
|
65
|
+
with open(self._index_file, 'w') as f:
|
|
66
|
+
if self.pretty:
|
|
67
|
+
json.dump(self._index, f, indent=2, default=str)
|
|
68
|
+
else:
|
|
69
|
+
json.dump(self._index, f, default=str)
|
|
70
|
+
|
|
71
|
+
def _session_file(self, session_id: str) -> Path:
|
|
72
|
+
"""Get path to session file."""
|
|
73
|
+
return self.path / f"{session_id}.json"
|
|
74
|
+
|
|
75
|
+
def _load_session_data(self, session_id: str) -> Optional[Dict]:
|
|
76
|
+
"""Load session data from file."""
|
|
77
|
+
file_path = self._session_file(session_id)
|
|
78
|
+
if file_path.exists():
|
|
79
|
+
with open(file_path, 'r') as f:
|
|
80
|
+
return json.load(f)
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
def _save_session_data(self, session_id: str, data: Dict):
|
|
84
|
+
"""Save session data to file."""
|
|
85
|
+
file_path = self._session_file(session_id)
|
|
86
|
+
with open(file_path, 'w') as f:
|
|
87
|
+
if self.pretty:
|
|
88
|
+
json.dump(data, f, indent=2, default=str)
|
|
89
|
+
else:
|
|
90
|
+
json.dump(data, f, default=str)
|
|
91
|
+
|
|
92
|
+
def create_session(self, session: ConversationSession) -> ConversationSession:
|
|
93
|
+
"""Create a new session."""
|
|
94
|
+
with self._lock:
|
|
95
|
+
data = {
|
|
96
|
+
"session": session.to_dict(),
|
|
97
|
+
"messages": []
|
|
98
|
+
}
|
|
99
|
+
self._save_session_data(session.session_id, data)
|
|
100
|
+
|
|
101
|
+
# Update index
|
|
102
|
+
self._index["sessions"][session.session_id] = {
|
|
103
|
+
"user_id": session.user_id,
|
|
104
|
+
"agent_id": session.agent_id,
|
|
105
|
+
"name": session.name,
|
|
106
|
+
"created_at": session.created_at,
|
|
107
|
+
"updated_at": session.updated_at,
|
|
108
|
+
}
|
|
109
|
+
self._save_index()
|
|
110
|
+
|
|
111
|
+
return session
|
|
112
|
+
|
|
113
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
114
|
+
"""Get a session by ID."""
|
|
115
|
+
data = self._load_session_data(session_id)
|
|
116
|
+
if data and "session" in data:
|
|
117
|
+
return ConversationSession.from_dict(data["session"])
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def update_session(self, session: ConversationSession) -> ConversationSession:
|
|
121
|
+
"""Update an existing session."""
|
|
122
|
+
with self._lock:
|
|
123
|
+
data = self._load_session_data(session.session_id)
|
|
124
|
+
if not data:
|
|
125
|
+
raise ValueError(f"Session {session.session_id} not found")
|
|
126
|
+
|
|
127
|
+
session.updated_at = time.time()
|
|
128
|
+
data["session"] = session.to_dict()
|
|
129
|
+
self._save_session_data(session.session_id, data)
|
|
130
|
+
|
|
131
|
+
# Update index
|
|
132
|
+
self._index["sessions"][session.session_id].update({
|
|
133
|
+
"name": session.name,
|
|
134
|
+
"updated_at": session.updated_at,
|
|
135
|
+
})
|
|
136
|
+
self._save_index()
|
|
137
|
+
|
|
138
|
+
return session
|
|
139
|
+
|
|
140
|
+
def delete_session(self, session_id: str) -> bool:
|
|
141
|
+
"""Delete a session and all its messages."""
|
|
142
|
+
with self._lock:
|
|
143
|
+
file_path = self._session_file(session_id)
|
|
144
|
+
if file_path.exists():
|
|
145
|
+
file_path.unlink()
|
|
146
|
+
|
|
147
|
+
# Update index
|
|
148
|
+
if session_id in self._index["sessions"]:
|
|
149
|
+
del self._index["sessions"][session_id]
|
|
150
|
+
self._save_index()
|
|
151
|
+
|
|
152
|
+
return True
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
def list_sessions(
|
|
156
|
+
self,
|
|
157
|
+
user_id: Optional[str] = None,
|
|
158
|
+
agent_id: Optional[str] = None,
|
|
159
|
+
limit: int = 100,
|
|
160
|
+
offset: int = 0
|
|
161
|
+
) -> List[ConversationSession]:
|
|
162
|
+
"""List sessions, optionally filtered by user or agent."""
|
|
163
|
+
sessions = []
|
|
164
|
+
|
|
165
|
+
for sid, info in self._index["sessions"].items():
|
|
166
|
+
if user_id and info.get("user_id") != user_id:
|
|
167
|
+
continue
|
|
168
|
+
if agent_id and info.get("agent_id") != agent_id:
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
session = self.get_session(sid)
|
|
172
|
+
if session:
|
|
173
|
+
sessions.append(session)
|
|
174
|
+
|
|
175
|
+
# Sort by updated_at descending
|
|
176
|
+
sessions.sort(key=lambda s: s.updated_at, reverse=True)
|
|
177
|
+
|
|
178
|
+
return sessions[offset:offset + limit]
|
|
179
|
+
|
|
180
|
+
def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
181
|
+
"""Add a message to a session."""
|
|
182
|
+
with self._lock:
|
|
183
|
+
data = self._load_session_data(session_id)
|
|
184
|
+
if not data:
|
|
185
|
+
raise ValueError(f"Session {session_id} not found")
|
|
186
|
+
|
|
187
|
+
message.session_id = session_id
|
|
188
|
+
data["messages"].append(message.to_dict())
|
|
189
|
+
|
|
190
|
+
# Update session timestamp
|
|
191
|
+
data["session"]["updated_at"] = time.time()
|
|
192
|
+
|
|
193
|
+
self._save_session_data(session_id, data)
|
|
194
|
+
|
|
195
|
+
# Update index
|
|
196
|
+
if session_id in self._index["sessions"]:
|
|
197
|
+
self._index["sessions"][session_id]["updated_at"] = time.time()
|
|
198
|
+
self._save_index()
|
|
199
|
+
|
|
200
|
+
return message
|
|
201
|
+
|
|
202
|
+
def get_messages(
|
|
203
|
+
self,
|
|
204
|
+
session_id: str,
|
|
205
|
+
limit: Optional[int] = None,
|
|
206
|
+
before: Optional[float] = None,
|
|
207
|
+
after: Optional[float] = None
|
|
208
|
+
) -> List[ConversationMessage]:
|
|
209
|
+
"""Get messages from a session."""
|
|
210
|
+
data = self._load_session_data(session_id)
|
|
211
|
+
if not data:
|
|
212
|
+
return []
|
|
213
|
+
|
|
214
|
+
messages = [ConversationMessage.from_dict(m) for m in data.get("messages", [])]
|
|
215
|
+
|
|
216
|
+
# Apply filters
|
|
217
|
+
if before:
|
|
218
|
+
messages = [m for m in messages if m.created_at < before]
|
|
219
|
+
if after:
|
|
220
|
+
messages = [m for m in messages if m.created_at > after]
|
|
221
|
+
|
|
222
|
+
# Sort by created_at
|
|
223
|
+
messages.sort(key=lambda m: m.created_at)
|
|
224
|
+
|
|
225
|
+
if limit:
|
|
226
|
+
messages = messages[-limit:]
|
|
227
|
+
|
|
228
|
+
return messages
|
|
229
|
+
|
|
230
|
+
def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
231
|
+
"""Delete messages. If message_ids is None, delete all messages in session."""
|
|
232
|
+
with self._lock:
|
|
233
|
+
data = self._load_session_data(session_id)
|
|
234
|
+
if not data:
|
|
235
|
+
return 0
|
|
236
|
+
|
|
237
|
+
if message_ids is None:
|
|
238
|
+
count = len(data["messages"])
|
|
239
|
+
data["messages"] = []
|
|
240
|
+
else:
|
|
241
|
+
original_count = len(data["messages"])
|
|
242
|
+
data["messages"] = [m for m in data["messages"] if m.get("id") not in message_ids]
|
|
243
|
+
count = original_count - len(data["messages"])
|
|
244
|
+
|
|
245
|
+
self._save_session_data(session_id, data)
|
|
246
|
+
return count
|
|
247
|
+
|
|
248
|
+
def close(self) -> None:
|
|
249
|
+
"""Close the store."""
|
|
250
|
+
pass # No resources to release
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MySQL implementation of ConversationStore.
|
|
3
|
+
|
|
4
|
+
Requires: mysql-connector-python or pymysql
|
|
5
|
+
Install: pip install mysql-connector-python
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Any, List, Optional
|
|
11
|
+
|
|
12
|
+
from .base import ConversationStore, ConversationSession, ConversationMessage
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MySQLConversationStore(ConversationStore):
|
|
18
|
+
"""
|
|
19
|
+
MySQL-based conversation store.
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
store = MySQLConversationStore(
|
|
23
|
+
url="mysql://user:password@localhost:3306/praisonai"
|
|
24
|
+
)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
SCHEMA_VERSION = "1.0.0"
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
url: Optional[str] = None,
|
|
32
|
+
host: str = "localhost",
|
|
33
|
+
port: int = 3306,
|
|
34
|
+
database: str = "praisonai",
|
|
35
|
+
user: str = "root",
|
|
36
|
+
password: str = "",
|
|
37
|
+
table_prefix: str = "praison_",
|
|
38
|
+
auto_create_tables: bool = True,
|
|
39
|
+
pool_size: int = 5,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Initialize MySQL conversation store.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
url: Full connection URL (overrides individual params)
|
|
46
|
+
host: Database host
|
|
47
|
+
port: Database port
|
|
48
|
+
database: Database name
|
|
49
|
+
user: Database user
|
|
50
|
+
password: Database password
|
|
51
|
+
table_prefix: Prefix for table names
|
|
52
|
+
auto_create_tables: Create tables if they don't exist
|
|
53
|
+
pool_size: Connection pool size
|
|
54
|
+
"""
|
|
55
|
+
try:
|
|
56
|
+
import mysql.connector
|
|
57
|
+
from mysql.connector import pooling
|
|
58
|
+
except ImportError:
|
|
59
|
+
raise ImportError(
|
|
60
|
+
"mysql-connector-python is required for MySQL support. "
|
|
61
|
+
"Install with: pip install mysql-connector-python"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self._mysql = mysql.connector
|
|
65
|
+
self.table_prefix = table_prefix
|
|
66
|
+
self.sessions_table = f"{table_prefix}sessions"
|
|
67
|
+
self.messages_table = f"{table_prefix}messages"
|
|
68
|
+
|
|
69
|
+
# Parse URL if provided
|
|
70
|
+
if url:
|
|
71
|
+
# Parse mysql://user:pass@host:port/database
|
|
72
|
+
from urllib.parse import urlparse
|
|
73
|
+
parsed = urlparse(url)
|
|
74
|
+
host = parsed.hostname or host
|
|
75
|
+
port = parsed.port or port
|
|
76
|
+
database = parsed.path.lstrip("/") or database
|
|
77
|
+
user = parsed.username or user
|
|
78
|
+
password = parsed.password or password
|
|
79
|
+
|
|
80
|
+
# Create connection pool
|
|
81
|
+
self._pool = pooling.MySQLConnectionPool(
|
|
82
|
+
pool_name="praison_mysql_pool",
|
|
83
|
+
pool_size=pool_size,
|
|
84
|
+
host=host,
|
|
85
|
+
port=port,
|
|
86
|
+
database=database,
|
|
87
|
+
user=user,
|
|
88
|
+
password=password,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if auto_create_tables:
|
|
92
|
+
self._create_tables()
|
|
93
|
+
|
|
94
|
+
def _get_conn(self):
|
|
95
|
+
"""Get a connection from the pool."""
|
|
96
|
+
return self._pool.get_connection()
|
|
97
|
+
|
|
98
|
+
def _create_tables(self) -> None:
|
|
99
|
+
"""Create tables if they don't exist."""
|
|
100
|
+
conn = self._get_conn()
|
|
101
|
+
try:
|
|
102
|
+
cur = conn.cursor()
|
|
103
|
+
|
|
104
|
+
# Sessions table
|
|
105
|
+
cur.execute(f"""
|
|
106
|
+
CREATE TABLE IF NOT EXISTS {self.sessions_table} (
|
|
107
|
+
session_id VARCHAR(255) PRIMARY KEY,
|
|
108
|
+
user_id VARCHAR(255),
|
|
109
|
+
agent_id VARCHAR(255),
|
|
110
|
+
name VARCHAR(255),
|
|
111
|
+
state JSON,
|
|
112
|
+
metadata JSON,
|
|
113
|
+
created_at DOUBLE,
|
|
114
|
+
updated_at DOUBLE,
|
|
115
|
+
INDEX idx_user (user_id),
|
|
116
|
+
INDEX idx_agent (agent_id),
|
|
117
|
+
INDEX idx_updated (updated_at DESC)
|
|
118
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
119
|
+
""")
|
|
120
|
+
|
|
121
|
+
# Messages table
|
|
122
|
+
cur.execute(f"""
|
|
123
|
+
CREATE TABLE IF NOT EXISTS {self.messages_table} (
|
|
124
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
125
|
+
session_id VARCHAR(255) NOT NULL,
|
|
126
|
+
role VARCHAR(50) NOT NULL,
|
|
127
|
+
content TEXT,
|
|
128
|
+
tool_calls JSON,
|
|
129
|
+
tool_call_id VARCHAR(255),
|
|
130
|
+
metadata JSON,
|
|
131
|
+
created_at DOUBLE,
|
|
132
|
+
INDEX idx_session (session_id),
|
|
133
|
+
INDEX idx_created (session_id, created_at),
|
|
134
|
+
FOREIGN KEY (session_id) REFERENCES {self.sessions_table}(session_id) ON DELETE CASCADE
|
|
135
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
|
|
136
|
+
""")
|
|
137
|
+
|
|
138
|
+
conn.commit()
|
|
139
|
+
logger.info(f"MySQL tables created: {self.sessions_table}, {self.messages_table}")
|
|
140
|
+
finally:
|
|
141
|
+
cur.close()
|
|
142
|
+
conn.close()
|
|
143
|
+
|
|
144
|
+
def create_session(self, session: ConversationSession) -> ConversationSession:
|
|
145
|
+
"""Create a new session."""
|
|
146
|
+
conn = self._get_conn()
|
|
147
|
+
try:
|
|
148
|
+
cur = conn.cursor()
|
|
149
|
+
cur.execute(f"""
|
|
150
|
+
INSERT INTO {self.sessions_table}
|
|
151
|
+
(session_id, user_id, agent_id, name, state, metadata, created_at, updated_at)
|
|
152
|
+
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
|
153
|
+
""", (
|
|
154
|
+
session.session_id,
|
|
155
|
+
session.user_id,
|
|
156
|
+
session.agent_id,
|
|
157
|
+
session.name,
|
|
158
|
+
json.dumps(session.state) if session.state else None,
|
|
159
|
+
json.dumps(session.metadata) if session.metadata else None,
|
|
160
|
+
session.created_at,
|
|
161
|
+
session.updated_at,
|
|
162
|
+
))
|
|
163
|
+
conn.commit()
|
|
164
|
+
return session
|
|
165
|
+
finally:
|
|
166
|
+
cur.close()
|
|
167
|
+
conn.close()
|
|
168
|
+
|
|
169
|
+
def get_session(self, session_id: str) -> Optional[ConversationSession]:
|
|
170
|
+
"""Get a session by ID."""
|
|
171
|
+
conn = self._get_conn()
|
|
172
|
+
try:
|
|
173
|
+
cur = conn.cursor(dictionary=True)
|
|
174
|
+
cur.execute(f"""
|
|
175
|
+
SELECT * FROM {self.sessions_table} WHERE session_id = %s
|
|
176
|
+
""", (session_id,))
|
|
177
|
+
row = cur.fetchone()
|
|
178
|
+
if not row:
|
|
179
|
+
return None
|
|
180
|
+
return ConversationSession(
|
|
181
|
+
session_id=row["session_id"],
|
|
182
|
+
user_id=row["user_id"],
|
|
183
|
+
agent_id=row["agent_id"],
|
|
184
|
+
name=row["name"],
|
|
185
|
+
state=row["state"] if isinstance(row["state"], dict) else (json.loads(row["state"]) if row["state"] else None),
|
|
186
|
+
metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
|
|
187
|
+
created_at=row["created_at"],
|
|
188
|
+
updated_at=row["updated_at"],
|
|
189
|
+
)
|
|
190
|
+
finally:
|
|
191
|
+
cur.close()
|
|
192
|
+
conn.close()
|
|
193
|
+
|
|
194
|
+
def update_session(self, session: ConversationSession) -> ConversationSession:
|
|
195
|
+
"""Update an existing session."""
|
|
196
|
+
conn = self._get_conn()
|
|
197
|
+
try:
|
|
198
|
+
cur = conn.cursor()
|
|
199
|
+
cur.execute(f"""
|
|
200
|
+
UPDATE {self.sessions_table}
|
|
201
|
+
SET user_id = %s, agent_id = %s, name = %s,
|
|
202
|
+
state = %s, metadata = %s, updated_at = %s
|
|
203
|
+
WHERE session_id = %s
|
|
204
|
+
""", (
|
|
205
|
+
session.user_id,
|
|
206
|
+
session.agent_id,
|
|
207
|
+
session.name,
|
|
208
|
+
json.dumps(session.state) if session.state else None,
|
|
209
|
+
json.dumps(session.metadata) if session.metadata else None,
|
|
210
|
+
session.updated_at,
|
|
211
|
+
session.session_id,
|
|
212
|
+
))
|
|
213
|
+
conn.commit()
|
|
214
|
+
return session
|
|
215
|
+
finally:
|
|
216
|
+
cur.close()
|
|
217
|
+
conn.close()
|
|
218
|
+
|
|
219
|
+
def delete_session(self, session_id: str) -> bool:
|
|
220
|
+
"""Delete a session and all its messages."""
|
|
221
|
+
conn = self._get_conn()
|
|
222
|
+
try:
|
|
223
|
+
cur = conn.cursor()
|
|
224
|
+
cur.execute(f"""
|
|
225
|
+
DELETE FROM {self.sessions_table} WHERE session_id = %s
|
|
226
|
+
""", (session_id,))
|
|
227
|
+
deleted = cur.rowcount > 0
|
|
228
|
+
conn.commit()
|
|
229
|
+
return deleted
|
|
230
|
+
finally:
|
|
231
|
+
cur.close()
|
|
232
|
+
conn.close()
|
|
233
|
+
|
|
234
|
+
def list_sessions(
|
|
235
|
+
self,
|
|
236
|
+
user_id: Optional[str] = None,
|
|
237
|
+
agent_id: Optional[str] = None,
|
|
238
|
+
limit: int = 100,
|
|
239
|
+
offset: int = 0
|
|
240
|
+
) -> List[ConversationSession]:
|
|
241
|
+
"""List sessions."""
|
|
242
|
+
conn = self._get_conn()
|
|
243
|
+
try:
|
|
244
|
+
cur = conn.cursor(dictionary=True)
|
|
245
|
+
|
|
246
|
+
conditions = []
|
|
247
|
+
params: List[Any] = []
|
|
248
|
+
|
|
249
|
+
if user_id:
|
|
250
|
+
conditions.append("user_id = %s")
|
|
251
|
+
params.append(user_id)
|
|
252
|
+
if agent_id:
|
|
253
|
+
conditions.append("agent_id = %s")
|
|
254
|
+
params.append(agent_id)
|
|
255
|
+
|
|
256
|
+
where_clause = ""
|
|
257
|
+
if conditions:
|
|
258
|
+
where_clause = "WHERE " + " AND ".join(conditions)
|
|
259
|
+
|
|
260
|
+
params.extend([limit, offset])
|
|
261
|
+
|
|
262
|
+
cur.execute(f"""
|
|
263
|
+
SELECT * FROM {self.sessions_table}
|
|
264
|
+
{where_clause}
|
|
265
|
+
ORDER BY updated_at DESC
|
|
266
|
+
LIMIT %s OFFSET %s
|
|
267
|
+
""", params)
|
|
268
|
+
|
|
269
|
+
return [
|
|
270
|
+
ConversationSession(
|
|
271
|
+
session_id=row["session_id"],
|
|
272
|
+
user_id=row["user_id"],
|
|
273
|
+
agent_id=row["agent_id"],
|
|
274
|
+
name=row["name"],
|
|
275
|
+
state=row["state"] if isinstance(row["state"], dict) else (json.loads(row["state"]) if row["state"] else None),
|
|
276
|
+
metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
|
|
277
|
+
created_at=row["created_at"],
|
|
278
|
+
updated_at=row["updated_at"],
|
|
279
|
+
)
|
|
280
|
+
for row in cur.fetchall()
|
|
281
|
+
]
|
|
282
|
+
finally:
|
|
283
|
+
cur.close()
|
|
284
|
+
conn.close()
|
|
285
|
+
|
|
286
|
+
def add_message(self, session_id: str, message: ConversationMessage) -> ConversationMessage:
|
|
287
|
+
"""Add a message to a session."""
|
|
288
|
+
message.session_id = session_id
|
|
289
|
+
conn = self._get_conn()
|
|
290
|
+
try:
|
|
291
|
+
cur = conn.cursor()
|
|
292
|
+
cur.execute(f"""
|
|
293
|
+
INSERT INTO {self.messages_table}
|
|
294
|
+
(id, session_id, role, content, tool_calls, tool_call_id, metadata, created_at)
|
|
295
|
+
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
|
296
|
+
""", (
|
|
297
|
+
message.id,
|
|
298
|
+
session_id,
|
|
299
|
+
message.role,
|
|
300
|
+
message.content,
|
|
301
|
+
json.dumps(message.tool_calls) if message.tool_calls else None,
|
|
302
|
+
message.tool_call_id,
|
|
303
|
+
json.dumps(message.metadata) if message.metadata else None,
|
|
304
|
+
message.created_at,
|
|
305
|
+
))
|
|
306
|
+
conn.commit()
|
|
307
|
+
return message
|
|
308
|
+
finally:
|
|
309
|
+
cur.close()
|
|
310
|
+
conn.close()
|
|
311
|
+
|
|
312
|
+
def get_messages(
|
|
313
|
+
self,
|
|
314
|
+
session_id: str,
|
|
315
|
+
limit: Optional[int] = None,
|
|
316
|
+
before: Optional[float] = None,
|
|
317
|
+
after: Optional[float] = None
|
|
318
|
+
) -> List[ConversationMessage]:
|
|
319
|
+
"""Get messages from a session."""
|
|
320
|
+
conn = self._get_conn()
|
|
321
|
+
try:
|
|
322
|
+
cur = conn.cursor(dictionary=True)
|
|
323
|
+
|
|
324
|
+
conditions = ["session_id = %s"]
|
|
325
|
+
params: List[Any] = [session_id]
|
|
326
|
+
|
|
327
|
+
if before:
|
|
328
|
+
conditions.append("created_at < %s")
|
|
329
|
+
params.append(before)
|
|
330
|
+
if after:
|
|
331
|
+
conditions.append("created_at > %s")
|
|
332
|
+
params.append(after)
|
|
333
|
+
|
|
334
|
+
where_clause = "WHERE " + " AND ".join(conditions)
|
|
335
|
+
limit_clause = f"LIMIT {limit}" if limit else ""
|
|
336
|
+
|
|
337
|
+
cur.execute(f"""
|
|
338
|
+
SELECT * FROM {self.messages_table}
|
|
339
|
+
{where_clause}
|
|
340
|
+
ORDER BY created_at ASC
|
|
341
|
+
{limit_clause}
|
|
342
|
+
""", params)
|
|
343
|
+
|
|
344
|
+
return [
|
|
345
|
+
ConversationMessage(
|
|
346
|
+
id=row["id"],
|
|
347
|
+
session_id=row["session_id"],
|
|
348
|
+
role=row["role"],
|
|
349
|
+
content=row["content"],
|
|
350
|
+
tool_calls=row["tool_calls"] if isinstance(row["tool_calls"], list) else (json.loads(row["tool_calls"]) if row["tool_calls"] else None),
|
|
351
|
+
tool_call_id=row["tool_call_id"],
|
|
352
|
+
metadata=row["metadata"] if isinstance(row["metadata"], dict) else (json.loads(row["metadata"]) if row["metadata"] else None),
|
|
353
|
+
created_at=row["created_at"],
|
|
354
|
+
)
|
|
355
|
+
for row in cur.fetchall()
|
|
356
|
+
]
|
|
357
|
+
finally:
|
|
358
|
+
cur.close()
|
|
359
|
+
conn.close()
|
|
360
|
+
|
|
361
|
+
def delete_messages(self, session_id: str, message_ids: Optional[List[str]] = None) -> int:
|
|
362
|
+
"""Delete messages."""
|
|
363
|
+
conn = self._get_conn()
|
|
364
|
+
try:
|
|
365
|
+
cur = conn.cursor()
|
|
366
|
+
|
|
367
|
+
if message_ids:
|
|
368
|
+
placeholders = ",".join(["%s"] * len(message_ids))
|
|
369
|
+
cur.execute(f"""
|
|
370
|
+
DELETE FROM {self.messages_table}
|
|
371
|
+
WHERE session_id = %s AND id IN ({placeholders})
|
|
372
|
+
""", [session_id] + message_ids)
|
|
373
|
+
else:
|
|
374
|
+
cur.execute(f"""
|
|
375
|
+
DELETE FROM {self.messages_table} WHERE session_id = %s
|
|
376
|
+
""", (session_id,))
|
|
377
|
+
|
|
378
|
+
deleted = cur.rowcount
|
|
379
|
+
conn.commit()
|
|
380
|
+
return deleted
|
|
381
|
+
finally:
|
|
382
|
+
cur.close()
|
|
383
|
+
conn.close()
|
|
384
|
+
|
|
385
|
+
def close(self) -> None:
|
|
386
|
+
"""Close the store."""
|
|
387
|
+
pass # Pool handles cleanup
|