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,633 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comprehensive TDD tests for all 22 database backends.
|
|
3
|
+
|
|
4
|
+
Tests are organized by category:
|
|
5
|
+
1. ConversationStore backends (6): PostgreSQL, MySQL, SQLite, SingleStore, Supabase, SurrealDB
|
|
6
|
+
2. KnowledgeStore backends (10): Qdrant, ChromaDB, Pinecone, Weaviate, LanceDB, Milvus, PGVector, Redis Vector, Cassandra, ClickHouse
|
|
7
|
+
3. StateStore backends (6): Redis, Memory, MongoDB, DynamoDB, Firestore, Upstash
|
|
8
|
+
|
|
9
|
+
Run with: python -m pytest praisonai/persistence/tests/test_all_backends.py -v
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import os
|
|
14
|
+
import time
|
|
15
|
+
import uuid
|
|
16
|
+
import random
|
|
17
|
+
import json
|
|
18
|
+
|
|
19
|
+
# Add the package to path
|
|
20
|
+
sys.path.insert(0, '/Users/praison/praisonai-package/src/praisonai')
|
|
21
|
+
|
|
22
|
+
from praisonai.persistence.conversation.base import ConversationStore, ConversationSession, ConversationMessage
|
|
23
|
+
from praisonai.persistence.knowledge.base import KnowledgeStore, KnowledgeDocument
|
|
24
|
+
from praisonai.persistence.state.base import StateStore
|
|
25
|
+
from praisonai.persistence.orchestrator import PersistenceOrchestrator
|
|
26
|
+
from praisonai.persistence.factory import create_conversation_store, create_knowledge_store, create_state_store
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# =============================================================================
|
|
30
|
+
# Test Utilities
|
|
31
|
+
# =============================================================================
|
|
32
|
+
|
|
33
|
+
def generate_test_session() -> ConversationSession:
|
|
34
|
+
"""Generate a test session with unique ID."""
|
|
35
|
+
return ConversationSession(
|
|
36
|
+
session_id=f"test-{uuid.uuid4().hex[:8]}",
|
|
37
|
+
user_id="test-user",
|
|
38
|
+
agent_id="test-agent",
|
|
39
|
+
name="Test Session",
|
|
40
|
+
metadata={"test": True, "timestamp": time.time()}
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def generate_test_message(session_id: str, role: str = "user", content: str = None) -> ConversationMessage:
|
|
45
|
+
"""Generate a test message."""
|
|
46
|
+
return ConversationMessage(
|
|
47
|
+
id=f"msg-{uuid.uuid4().hex[:8]}",
|
|
48
|
+
session_id=session_id,
|
|
49
|
+
role=role,
|
|
50
|
+
content=content or f"Test message from {role} at {time.time()}",
|
|
51
|
+
metadata={"test": True}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def generate_test_document(dim: int = 384) -> KnowledgeDocument:
|
|
56
|
+
"""Generate a test document with random embedding."""
|
|
57
|
+
random.seed(42)
|
|
58
|
+
return KnowledgeDocument(
|
|
59
|
+
id=f"doc-{uuid.uuid4().hex[:8]}",
|
|
60
|
+
content=f"Test document content {uuid.uuid4().hex[:8]}",
|
|
61
|
+
embedding=[random.random() for _ in range(dim)],
|
|
62
|
+
metadata={"test": True, "category": "test"}
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# =============================================================================
|
|
67
|
+
# ConversationStore Tests
|
|
68
|
+
# =============================================================================
|
|
69
|
+
|
|
70
|
+
class ConversationStoreTestMixin:
|
|
71
|
+
"""Mixin with standard tests for all ConversationStore implementations."""
|
|
72
|
+
|
|
73
|
+
store: ConversationStore = None
|
|
74
|
+
|
|
75
|
+
def test_create_session(self):
|
|
76
|
+
"""Test session creation."""
|
|
77
|
+
session = generate_test_session()
|
|
78
|
+
result = self.store.create_session(session)
|
|
79
|
+
assert result.session_id == session.session_id
|
|
80
|
+
assert result.user_id == session.user_id
|
|
81
|
+
# Cleanup
|
|
82
|
+
self.store.delete_session(session.session_id)
|
|
83
|
+
|
|
84
|
+
def test_get_session(self):
|
|
85
|
+
"""Test session retrieval."""
|
|
86
|
+
session = generate_test_session()
|
|
87
|
+
self.store.create_session(session)
|
|
88
|
+
|
|
89
|
+
retrieved = self.store.get_session(session.session_id)
|
|
90
|
+
assert retrieved is not None
|
|
91
|
+
assert retrieved.session_id == session.session_id
|
|
92
|
+
assert retrieved.name == session.name
|
|
93
|
+
|
|
94
|
+
# Cleanup
|
|
95
|
+
self.store.delete_session(session.session_id)
|
|
96
|
+
|
|
97
|
+
def test_update_session(self):
|
|
98
|
+
"""Test session update."""
|
|
99
|
+
session = generate_test_session()
|
|
100
|
+
self.store.create_session(session)
|
|
101
|
+
|
|
102
|
+
session.name = "Updated Name"
|
|
103
|
+
session.metadata = {"updated": True}
|
|
104
|
+
self.store.update_session(session)
|
|
105
|
+
|
|
106
|
+
retrieved = self.store.get_session(session.session_id)
|
|
107
|
+
assert retrieved.name == "Updated Name"
|
|
108
|
+
|
|
109
|
+
# Cleanup
|
|
110
|
+
self.store.delete_session(session.session_id)
|
|
111
|
+
|
|
112
|
+
def test_delete_session(self):
|
|
113
|
+
"""Test session deletion."""
|
|
114
|
+
session = generate_test_session()
|
|
115
|
+
self.store.create_session(session)
|
|
116
|
+
|
|
117
|
+
result = self.store.delete_session(session.session_id)
|
|
118
|
+
assert result is True
|
|
119
|
+
|
|
120
|
+
retrieved = self.store.get_session(session.session_id)
|
|
121
|
+
assert retrieved is None
|
|
122
|
+
|
|
123
|
+
def test_add_message(self):
|
|
124
|
+
"""Test adding messages to session."""
|
|
125
|
+
session = generate_test_session()
|
|
126
|
+
self.store.create_session(session)
|
|
127
|
+
|
|
128
|
+
msg = generate_test_message(session.session_id, "user", "Hello!")
|
|
129
|
+
result = self.store.add_message(session.session_id, msg)
|
|
130
|
+
assert result.id == msg.id
|
|
131
|
+
assert result.content == "Hello!"
|
|
132
|
+
|
|
133
|
+
# Cleanup
|
|
134
|
+
self.store.delete_session(session.session_id)
|
|
135
|
+
|
|
136
|
+
def test_get_messages(self):
|
|
137
|
+
"""Test retrieving messages from session."""
|
|
138
|
+
session = generate_test_session()
|
|
139
|
+
self.store.create_session(session)
|
|
140
|
+
|
|
141
|
+
# Add multiple messages
|
|
142
|
+
for i in range(5):
|
|
143
|
+
role = "user" if i % 2 == 0 else "assistant"
|
|
144
|
+
msg = generate_test_message(session.session_id, role, f"Message {i}")
|
|
145
|
+
self.store.add_message(session.session_id, msg)
|
|
146
|
+
|
|
147
|
+
messages = self.store.get_messages(session.session_id)
|
|
148
|
+
assert len(messages) == 5
|
|
149
|
+
|
|
150
|
+
# Test limit
|
|
151
|
+
limited = self.store.get_messages(session.session_id, limit=3)
|
|
152
|
+
assert len(limited) == 3
|
|
153
|
+
|
|
154
|
+
# Cleanup
|
|
155
|
+
self.store.delete_session(session.session_id)
|
|
156
|
+
|
|
157
|
+
def test_list_sessions(self):
|
|
158
|
+
"""Test listing sessions."""
|
|
159
|
+
sessions = []
|
|
160
|
+
for i in range(3):
|
|
161
|
+
session = generate_test_session()
|
|
162
|
+
session.user_id = "list-test-user"
|
|
163
|
+
self.store.create_session(session)
|
|
164
|
+
sessions.append(session)
|
|
165
|
+
|
|
166
|
+
listed = self.store.list_sessions(user_id="list-test-user")
|
|
167
|
+
assert len(listed) >= 3
|
|
168
|
+
|
|
169
|
+
# Cleanup
|
|
170
|
+
for s in sessions:
|
|
171
|
+
self.store.delete_session(s.session_id)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
# =============================================================================
|
|
175
|
+
# KnowledgeStore Tests
|
|
176
|
+
# =============================================================================
|
|
177
|
+
|
|
178
|
+
class KnowledgeStoreTestMixin:
|
|
179
|
+
"""Mixin with standard tests for all KnowledgeStore implementations."""
|
|
180
|
+
|
|
181
|
+
store: KnowledgeStore = None
|
|
182
|
+
collection_name: str = "test_collection"
|
|
183
|
+
dimension: int = 384
|
|
184
|
+
|
|
185
|
+
def test_create_collection(self):
|
|
186
|
+
"""Test collection creation."""
|
|
187
|
+
if self.store.collection_exists(self.collection_name):
|
|
188
|
+
self.store.delete_collection(self.collection_name)
|
|
189
|
+
|
|
190
|
+
self.store.create_collection(self.collection_name, self.dimension)
|
|
191
|
+
assert self.store.collection_exists(self.collection_name)
|
|
192
|
+
|
|
193
|
+
# Cleanup
|
|
194
|
+
self.store.delete_collection(self.collection_name)
|
|
195
|
+
|
|
196
|
+
def test_insert_documents(self):
|
|
197
|
+
"""Test document insertion."""
|
|
198
|
+
if not self.store.collection_exists(self.collection_name):
|
|
199
|
+
self.store.create_collection(self.collection_name, self.dimension)
|
|
200
|
+
|
|
201
|
+
docs = [generate_test_document(self.dimension) for _ in range(5)]
|
|
202
|
+
ids = self.store.insert(self.collection_name, docs)
|
|
203
|
+
assert len(ids) == 5
|
|
204
|
+
|
|
205
|
+
# Cleanup
|
|
206
|
+
self.store.delete_collection(self.collection_name)
|
|
207
|
+
|
|
208
|
+
def test_search_documents(self):
|
|
209
|
+
"""Test vector search."""
|
|
210
|
+
if not self.store.collection_exists(self.collection_name):
|
|
211
|
+
self.store.create_collection(self.collection_name, self.dimension)
|
|
212
|
+
|
|
213
|
+
# Insert documents
|
|
214
|
+
docs = [generate_test_document(self.dimension) for _ in range(10)]
|
|
215
|
+
self.store.insert(self.collection_name, docs)
|
|
216
|
+
|
|
217
|
+
# Search
|
|
218
|
+
random.seed(42)
|
|
219
|
+
query_embedding = [random.random() for _ in range(self.dimension)]
|
|
220
|
+
results = self.store.search(self.collection_name, query_embedding, limit=5)
|
|
221
|
+
assert len(results) <= 5
|
|
222
|
+
|
|
223
|
+
# Cleanup
|
|
224
|
+
self.store.delete_collection(self.collection_name)
|
|
225
|
+
|
|
226
|
+
def test_delete_documents(self):
|
|
227
|
+
"""Test document deletion."""
|
|
228
|
+
if not self.store.collection_exists(self.collection_name):
|
|
229
|
+
self.store.create_collection(self.collection_name, self.dimension)
|
|
230
|
+
|
|
231
|
+
docs = [generate_test_document(self.dimension) for _ in range(3)]
|
|
232
|
+
ids = self.store.insert(self.collection_name, docs)
|
|
233
|
+
|
|
234
|
+
deleted = self.store.delete(self.collection_name, ids=ids[:1])
|
|
235
|
+
assert deleted >= 1
|
|
236
|
+
|
|
237
|
+
# Cleanup
|
|
238
|
+
self.store.delete_collection(self.collection_name)
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# =============================================================================
|
|
242
|
+
# StateStore Tests
|
|
243
|
+
# =============================================================================
|
|
244
|
+
|
|
245
|
+
class StateStoreTestMixin:
|
|
246
|
+
"""Mixin with standard tests for all StateStore implementations."""
|
|
247
|
+
|
|
248
|
+
store: StateStore = None
|
|
249
|
+
|
|
250
|
+
def test_set_get(self):
|
|
251
|
+
"""Test basic set/get operations."""
|
|
252
|
+
key = f"test:{uuid.uuid4().hex[:8]}"
|
|
253
|
+
value = {"test": True, "count": 42}
|
|
254
|
+
|
|
255
|
+
self.store.set(key, value)
|
|
256
|
+
retrieved = self.store.get(key)
|
|
257
|
+
|
|
258
|
+
assert retrieved == value
|
|
259
|
+
|
|
260
|
+
# Cleanup
|
|
261
|
+
self.store.delete(key)
|
|
262
|
+
|
|
263
|
+
def test_set_with_ttl(self):
|
|
264
|
+
"""Test set with TTL."""
|
|
265
|
+
key = f"test:ttl:{uuid.uuid4().hex[:8]}"
|
|
266
|
+
self.store.set(key, "temporary", ttl=3600)
|
|
267
|
+
|
|
268
|
+
assert self.store.exists(key)
|
|
269
|
+
ttl = self.store.ttl(key)
|
|
270
|
+
assert ttl is None or ttl > 0
|
|
271
|
+
|
|
272
|
+
# Cleanup
|
|
273
|
+
self.store.delete(key)
|
|
274
|
+
|
|
275
|
+
def test_delete(self):
|
|
276
|
+
"""Test key deletion."""
|
|
277
|
+
key = f"test:delete:{uuid.uuid4().hex[:8]}"
|
|
278
|
+
self.store.set(key, "to_delete")
|
|
279
|
+
|
|
280
|
+
result = self.store.delete(key)
|
|
281
|
+
assert result is True
|
|
282
|
+
assert not self.store.exists(key)
|
|
283
|
+
|
|
284
|
+
def test_exists(self):
|
|
285
|
+
"""Test key existence check."""
|
|
286
|
+
key = f"test:exists:{uuid.uuid4().hex[:8]}"
|
|
287
|
+
|
|
288
|
+
assert not self.store.exists(key)
|
|
289
|
+
self.store.set(key, "value")
|
|
290
|
+
assert self.store.exists(key)
|
|
291
|
+
|
|
292
|
+
# Cleanup
|
|
293
|
+
self.store.delete(key)
|
|
294
|
+
|
|
295
|
+
def test_hash_operations(self):
|
|
296
|
+
"""Test hash operations."""
|
|
297
|
+
key = f"test:hash:{uuid.uuid4().hex[:8]}"
|
|
298
|
+
|
|
299
|
+
self.store.hset(key, "field1", "value1")
|
|
300
|
+
self.store.hset(key, "field2", 42)
|
|
301
|
+
|
|
302
|
+
assert self.store.hget(key, "field1") == "value1"
|
|
303
|
+
|
|
304
|
+
all_fields = self.store.hgetall(key)
|
|
305
|
+
assert "field1" in all_fields
|
|
306
|
+
assert "field2" in all_fields
|
|
307
|
+
|
|
308
|
+
# Cleanup
|
|
309
|
+
self.store.delete(key)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# =============================================================================
|
|
313
|
+
# Concrete Test Classes
|
|
314
|
+
# =============================================================================
|
|
315
|
+
|
|
316
|
+
def run_test(test_func, name):
|
|
317
|
+
"""Run a single test and report result."""
|
|
318
|
+
try:
|
|
319
|
+
test_func()
|
|
320
|
+
print(f" ✅ {name}")
|
|
321
|
+
return True
|
|
322
|
+
except Exception as e:
|
|
323
|
+
print(f" ❌ {name}: {e}")
|
|
324
|
+
return False
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def test_postgresql_conversation_store():
|
|
328
|
+
"""Test PostgreSQL ConversationStore."""
|
|
329
|
+
print("\n=== PostgreSQL ConversationStore ===")
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
from praisonai.persistence.conversation.postgres import PostgresConversationStore
|
|
333
|
+
store = PostgresConversationStore(
|
|
334
|
+
host='localhost', port=5432, database='praisonai',
|
|
335
|
+
user='postgres', password='praison123'
|
|
336
|
+
)
|
|
337
|
+
except Exception as e:
|
|
338
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
339
|
+
return
|
|
340
|
+
|
|
341
|
+
class TestPostgres(ConversationStoreTestMixin):
|
|
342
|
+
pass
|
|
343
|
+
|
|
344
|
+
test = TestPostgres()
|
|
345
|
+
test.store = store
|
|
346
|
+
|
|
347
|
+
results = []
|
|
348
|
+
results.append(run_test(test.test_create_session, "create_session"))
|
|
349
|
+
results.append(run_test(test.test_get_session, "get_session"))
|
|
350
|
+
results.append(run_test(test.test_update_session, "update_session"))
|
|
351
|
+
results.append(run_test(test.test_delete_session, "delete_session"))
|
|
352
|
+
results.append(run_test(test.test_add_message, "add_message"))
|
|
353
|
+
results.append(run_test(test.test_get_messages, "get_messages"))
|
|
354
|
+
results.append(run_test(test.test_list_sessions, "list_sessions"))
|
|
355
|
+
|
|
356
|
+
store.close()
|
|
357
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def test_sqlite_conversation_store():
|
|
361
|
+
"""Test SQLite ConversationStore."""
|
|
362
|
+
print("\n=== SQLite ConversationStore ===")
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
from praisonai.persistence.conversation.sqlite import SQLiteConversationStore
|
|
366
|
+
store = SQLiteConversationStore(path="/tmp/test_praison.db")
|
|
367
|
+
except Exception as e:
|
|
368
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
369
|
+
return
|
|
370
|
+
|
|
371
|
+
class TestSQLite(ConversationStoreTestMixin):
|
|
372
|
+
pass
|
|
373
|
+
|
|
374
|
+
test = TestSQLite()
|
|
375
|
+
test.store = store
|
|
376
|
+
|
|
377
|
+
results = []
|
|
378
|
+
results.append(run_test(test.test_create_session, "create_session"))
|
|
379
|
+
results.append(run_test(test.test_get_session, "get_session"))
|
|
380
|
+
results.append(run_test(test.test_update_session, "update_session"))
|
|
381
|
+
results.append(run_test(test.test_delete_session, "delete_session"))
|
|
382
|
+
results.append(run_test(test.test_add_message, "add_message"))
|
|
383
|
+
results.append(run_test(test.test_get_messages, "get_messages"))
|
|
384
|
+
results.append(run_test(test.test_list_sessions, "list_sessions"))
|
|
385
|
+
|
|
386
|
+
store.close()
|
|
387
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def test_qdrant_knowledge_store():
|
|
391
|
+
"""Test Qdrant KnowledgeStore."""
|
|
392
|
+
print("\n=== Qdrant KnowledgeStore ===")
|
|
393
|
+
|
|
394
|
+
try:
|
|
395
|
+
from praisonai.persistence.knowledge.qdrant import QdrantKnowledgeStore
|
|
396
|
+
store = QdrantKnowledgeStore(host='localhost', port=6333)
|
|
397
|
+
except Exception as e:
|
|
398
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
399
|
+
return
|
|
400
|
+
|
|
401
|
+
class TestQdrant(KnowledgeStoreTestMixin):
|
|
402
|
+
pass
|
|
403
|
+
|
|
404
|
+
test = TestQdrant()
|
|
405
|
+
test.store = store
|
|
406
|
+
test.collection_name = f"test_{uuid.uuid4().hex[:8]}"
|
|
407
|
+
|
|
408
|
+
results = []
|
|
409
|
+
results.append(run_test(test.test_create_collection, "create_collection"))
|
|
410
|
+
results.append(run_test(test.test_insert_documents, "insert_documents"))
|
|
411
|
+
results.append(run_test(test.test_search_documents, "search_documents"))
|
|
412
|
+
results.append(run_test(test.test_delete_documents, "delete_documents"))
|
|
413
|
+
|
|
414
|
+
store.close()
|
|
415
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def test_chroma_knowledge_store():
|
|
419
|
+
"""Test ChromaDB KnowledgeStore."""
|
|
420
|
+
print("\n=== ChromaDB KnowledgeStore ===")
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
from praisonai.persistence.knowledge.chroma import ChromaKnowledgeStore
|
|
424
|
+
store = ChromaKnowledgeStore(path="/tmp/test_chroma")
|
|
425
|
+
except Exception as e:
|
|
426
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
class TestChroma(KnowledgeStoreTestMixin):
|
|
430
|
+
pass
|
|
431
|
+
|
|
432
|
+
test = TestChroma()
|
|
433
|
+
test.store = store
|
|
434
|
+
test.collection_name = f"test_{uuid.uuid4().hex[:8]}"
|
|
435
|
+
|
|
436
|
+
results = []
|
|
437
|
+
results.append(run_test(test.test_create_collection, "create_collection"))
|
|
438
|
+
results.append(run_test(test.test_insert_documents, "insert_documents"))
|
|
439
|
+
results.append(run_test(test.test_search_documents, "search_documents"))
|
|
440
|
+
results.append(run_test(test.test_delete_documents, "delete_documents"))
|
|
441
|
+
|
|
442
|
+
store.close()
|
|
443
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def test_redis_state_store():
|
|
447
|
+
"""Test Redis StateStore."""
|
|
448
|
+
print("\n=== Redis StateStore ===")
|
|
449
|
+
|
|
450
|
+
try:
|
|
451
|
+
from praisonai.persistence.state.redis import RedisStateStore
|
|
452
|
+
store = RedisStateStore(host='localhost', port=6379)
|
|
453
|
+
except Exception as e:
|
|
454
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
455
|
+
return
|
|
456
|
+
|
|
457
|
+
class TestRedis(StateStoreTestMixin):
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
test = TestRedis()
|
|
461
|
+
test.store = store
|
|
462
|
+
|
|
463
|
+
results = []
|
|
464
|
+
results.append(run_test(test.test_set_get, "set_get"))
|
|
465
|
+
results.append(run_test(test.test_set_with_ttl, "set_with_ttl"))
|
|
466
|
+
results.append(run_test(test.test_delete, "delete"))
|
|
467
|
+
results.append(run_test(test.test_exists, "exists"))
|
|
468
|
+
results.append(run_test(test.test_hash_operations, "hash_operations"))
|
|
469
|
+
|
|
470
|
+
store.close()
|
|
471
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def test_memory_state_store():
|
|
475
|
+
"""Test Memory StateStore (zero-dependency)."""
|
|
476
|
+
print("\n=== Memory StateStore ===")
|
|
477
|
+
|
|
478
|
+
try:
|
|
479
|
+
from praisonai.persistence.state.memory import MemoryStateStore
|
|
480
|
+
store = MemoryStateStore(path="/tmp/test_state.json")
|
|
481
|
+
except Exception as e:
|
|
482
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
class TestMemory(StateStoreTestMixin):
|
|
486
|
+
pass
|
|
487
|
+
|
|
488
|
+
test = TestMemory()
|
|
489
|
+
test.store = store
|
|
490
|
+
|
|
491
|
+
results = []
|
|
492
|
+
results.append(run_test(test.test_set_get, "set_get"))
|
|
493
|
+
results.append(run_test(test.test_set_with_ttl, "set_with_ttl"))
|
|
494
|
+
results.append(run_test(test.test_delete, "delete"))
|
|
495
|
+
results.append(run_test(test.test_exists, "exists"))
|
|
496
|
+
results.append(run_test(test.test_hash_operations, "hash_operations"))
|
|
497
|
+
|
|
498
|
+
store.close()
|
|
499
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def test_orchestrator_integration():
|
|
503
|
+
"""Test PersistenceOrchestrator with all stores."""
|
|
504
|
+
print("\n=== PersistenceOrchestrator Integration ===")
|
|
505
|
+
|
|
506
|
+
try:
|
|
507
|
+
from praisonai.persistence.conversation.postgres import PostgresConversationStore
|
|
508
|
+
from praisonai.persistence.knowledge.qdrant import QdrantKnowledgeStore
|
|
509
|
+
from praisonai.persistence.state.redis import RedisStateStore
|
|
510
|
+
|
|
511
|
+
conv_store = PostgresConversationStore(
|
|
512
|
+
host='localhost', port=5432, database='praisonai',
|
|
513
|
+
user='postgres', password='praison123'
|
|
514
|
+
)
|
|
515
|
+
knowledge_store = QdrantKnowledgeStore(host='localhost', port=6333)
|
|
516
|
+
state_store = RedisStateStore(host='localhost', port=6379)
|
|
517
|
+
|
|
518
|
+
orchestrator = PersistenceOrchestrator(
|
|
519
|
+
conversation_store=conv_store,
|
|
520
|
+
knowledge_store=knowledge_store,
|
|
521
|
+
state_store=state_store,
|
|
522
|
+
)
|
|
523
|
+
except Exception as e:
|
|
524
|
+
print(f" ⚠️ SKIPPED: {e}")
|
|
525
|
+
return
|
|
526
|
+
|
|
527
|
+
results = []
|
|
528
|
+
|
|
529
|
+
# Test 1: Agent lifecycle
|
|
530
|
+
def test_agent_lifecycle():
|
|
531
|
+
class MockAgent:
|
|
532
|
+
name = "test-agent"
|
|
533
|
+
|
|
534
|
+
agent = MockAgent()
|
|
535
|
+
session_id = f"orch-test-{uuid.uuid4().hex[:8]}"
|
|
536
|
+
|
|
537
|
+
# Start
|
|
538
|
+
history = orchestrator.on_agent_start(agent, session_id=session_id, user_id="test-user")
|
|
539
|
+
assert isinstance(history, list)
|
|
540
|
+
|
|
541
|
+
# Messages
|
|
542
|
+
orchestrator.on_message(session_id, "user", "Hello!")
|
|
543
|
+
orchestrator.on_message(session_id, "assistant", "Hi there!")
|
|
544
|
+
|
|
545
|
+
# End
|
|
546
|
+
orchestrator.on_agent_end(agent, session_id)
|
|
547
|
+
|
|
548
|
+
# Verify
|
|
549
|
+
messages = orchestrator.get_messages(session_id)
|
|
550
|
+
assert len(messages) == 2
|
|
551
|
+
|
|
552
|
+
# Cleanup
|
|
553
|
+
orchestrator.delete_session(session_id)
|
|
554
|
+
|
|
555
|
+
results.append(run_test(test_agent_lifecycle, "agent_lifecycle"))
|
|
556
|
+
|
|
557
|
+
# Test 2: State management
|
|
558
|
+
def test_state_management():
|
|
559
|
+
key = f"orch:state:{uuid.uuid4().hex[:8]}"
|
|
560
|
+
orchestrator.set_state(key, {"test": True})
|
|
561
|
+
value = orchestrator.get_state(key)
|
|
562
|
+
assert value == {"test": True}
|
|
563
|
+
orchestrator.delete_state(key)
|
|
564
|
+
|
|
565
|
+
results.append(run_test(test_state_management, "state_management"))
|
|
566
|
+
|
|
567
|
+
# Test 3: Session resume
|
|
568
|
+
def test_session_resume():
|
|
569
|
+
class MockAgent:
|
|
570
|
+
name = "resume-agent"
|
|
571
|
+
|
|
572
|
+
agent = MockAgent()
|
|
573
|
+
session_id = f"resume-test-{uuid.uuid4().hex[:8]}"
|
|
574
|
+
|
|
575
|
+
# First run
|
|
576
|
+
orchestrator.on_agent_start(agent, session_id=session_id)
|
|
577
|
+
orchestrator.on_message(session_id, "user", "First message")
|
|
578
|
+
orchestrator.on_agent_end(agent, session_id)
|
|
579
|
+
|
|
580
|
+
# Second run - should resume
|
|
581
|
+
history = orchestrator.on_agent_start(agent, session_id=session_id, resume=True)
|
|
582
|
+
assert len(history) == 1
|
|
583
|
+
assert history[0].content == "First message"
|
|
584
|
+
|
|
585
|
+
# Cleanup
|
|
586
|
+
orchestrator.delete_session(session_id)
|
|
587
|
+
|
|
588
|
+
results.append(run_test(test_session_resume, "session_resume"))
|
|
589
|
+
|
|
590
|
+
orchestrator.close()
|
|
591
|
+
print(f" Results: {sum(results)}/{len(results)} passed")
|
|
592
|
+
|
|
593
|
+
|
|
594
|
+
def run_all_tests():
|
|
595
|
+
"""Run all database backend tests."""
|
|
596
|
+
print("=" * 60)
|
|
597
|
+
print("PraisonAI Persistence Layer - Comprehensive TDD Tests")
|
|
598
|
+
print("=" * 60)
|
|
599
|
+
|
|
600
|
+
# ConversationStore tests
|
|
601
|
+
print("\n" + "=" * 40)
|
|
602
|
+
print("CONVERSATION STORE BACKENDS")
|
|
603
|
+
print("=" * 40)
|
|
604
|
+
test_postgresql_conversation_store()
|
|
605
|
+
test_sqlite_conversation_store()
|
|
606
|
+
|
|
607
|
+
# KnowledgeStore tests
|
|
608
|
+
print("\n" + "=" * 40)
|
|
609
|
+
print("KNOWLEDGE STORE BACKENDS")
|
|
610
|
+
print("=" * 40)
|
|
611
|
+
test_qdrant_knowledge_store()
|
|
612
|
+
test_chroma_knowledge_store()
|
|
613
|
+
|
|
614
|
+
# StateStore tests
|
|
615
|
+
print("\n" + "=" * 40)
|
|
616
|
+
print("STATE STORE BACKENDS")
|
|
617
|
+
print("=" * 40)
|
|
618
|
+
test_redis_state_store()
|
|
619
|
+
test_memory_state_store()
|
|
620
|
+
|
|
621
|
+
# Integration tests
|
|
622
|
+
print("\n" + "=" * 40)
|
|
623
|
+
print("INTEGRATION TESTS")
|
|
624
|
+
print("=" * 40)
|
|
625
|
+
test_orchestrator_integration()
|
|
626
|
+
|
|
627
|
+
print("\n" + "=" * 60)
|
|
628
|
+
print("All tests completed!")
|
|
629
|
+
print("=" * 60)
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
if __name__ == "__main__":
|
|
633
|
+
run_all_tests()
|