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,376 @@
|
|
|
1
|
+
"""
|
|
2
|
+
HTTP Stream Transport for MCP Server
|
|
3
|
+
|
|
4
|
+
Implements the MCP Streamable HTTP transport (MCP 2025-11-25 spec):
|
|
5
|
+
- Single endpoint for all MCP communication
|
|
6
|
+
- POST for client→server messages
|
|
7
|
+
- GET for server→client SSE stream (optional)
|
|
8
|
+
- Session management via MCP-Session-Id header
|
|
9
|
+
- Supports batch (JSON) and stream (SSE) response modes
|
|
10
|
+
- Origin header validation for security
|
|
11
|
+
- MCP-Protocol-Version header support
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import asyncio
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import time
|
|
18
|
+
import uuid
|
|
19
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from ..server import MCPServer
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# MCP Protocol Version (Updated to 2025-11-25)
|
|
27
|
+
PROTOCOL_VERSION = "2025-11-25"
|
|
28
|
+
SUPPORTED_VERSIONS = ["2025-11-25", "2025-03-26"]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HTTPStreamTransport:
|
|
32
|
+
"""
|
|
33
|
+
HTTP Stream transport for MCP server.
|
|
34
|
+
|
|
35
|
+
Implements the Streamable HTTP transport per MCP 2025-11-25 spec.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
server: "MCPServer",
|
|
41
|
+
host: str = "127.0.0.1",
|
|
42
|
+
port: int = 8080,
|
|
43
|
+
endpoint: str = "/mcp",
|
|
44
|
+
cors_origins: Optional[list] = None,
|
|
45
|
+
allowed_origins: Optional[list] = None,
|
|
46
|
+
api_key: Optional[str] = None,
|
|
47
|
+
session_ttl: int = 3600,
|
|
48
|
+
allow_client_termination: bool = True,
|
|
49
|
+
response_mode: str = "batch",
|
|
50
|
+
resumability_enabled: bool = True,
|
|
51
|
+
event_history_duration: int = 300,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
Initialize HTTP Stream transport.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
server: MCPServer instance
|
|
58
|
+
host: Server host
|
|
59
|
+
port: Server port
|
|
60
|
+
endpoint: MCP endpoint path
|
|
61
|
+
cors_origins: CORS allowed origins
|
|
62
|
+
allowed_origins: Origins allowed for security validation (None = localhost only)
|
|
63
|
+
api_key: Optional API key for authentication
|
|
64
|
+
session_ttl: Session TTL in seconds
|
|
65
|
+
allow_client_termination: Allow client to terminate session via DELETE
|
|
66
|
+
response_mode: Default response mode ("batch" or "stream")
|
|
67
|
+
resumability_enabled: Enable SSE resumability
|
|
68
|
+
event_history_duration: Duration to keep event history (seconds)
|
|
69
|
+
"""
|
|
70
|
+
self.server = server
|
|
71
|
+
self.host = host
|
|
72
|
+
self.port = port
|
|
73
|
+
self.endpoint = endpoint
|
|
74
|
+
self.cors_origins = cors_origins or ["*"]
|
|
75
|
+
self.api_key = api_key
|
|
76
|
+
self.session_ttl = session_ttl
|
|
77
|
+
self.allow_client_termination = allow_client_termination
|
|
78
|
+
self.response_mode = response_mode
|
|
79
|
+
self.resumability_enabled = resumability_enabled
|
|
80
|
+
self.event_history_duration = event_history_duration
|
|
81
|
+
|
|
82
|
+
# Allowed origins for security (MCP 2025-11-25 requirement)
|
|
83
|
+
# Default: only localhost if binding to localhost
|
|
84
|
+
if allowed_origins is None:
|
|
85
|
+
if host in ("127.0.0.1", "localhost", "::1"):
|
|
86
|
+
self.allowed_origins = ["http://localhost", "http://127.0.0.1", "https://localhost", "https://127.0.0.1"]
|
|
87
|
+
else:
|
|
88
|
+
# External binding requires explicit origin config
|
|
89
|
+
self.allowed_origins = None # Will reject all Origin headers
|
|
90
|
+
logger.warning("External binding without allowed_origins - Origin validation will reject all requests with Origin header")
|
|
91
|
+
else:
|
|
92
|
+
self.allowed_origins = allowed_origins
|
|
93
|
+
|
|
94
|
+
# Session storage
|
|
95
|
+
self._sessions: Dict[str, Dict[str, Any]] = {}
|
|
96
|
+
|
|
97
|
+
# SSE event history for resumability
|
|
98
|
+
self._event_history: Dict[str, list] = {}
|
|
99
|
+
self._event_counter = 0
|
|
100
|
+
self._event_timestamps: Dict[str, float] = {}
|
|
101
|
+
|
|
102
|
+
def run(self) -> None:
|
|
103
|
+
"""Run the HTTP Stream server."""
|
|
104
|
+
try:
|
|
105
|
+
import uvicorn
|
|
106
|
+
except ImportError:
|
|
107
|
+
raise ImportError("uvicorn required. Install with: pip install uvicorn")
|
|
108
|
+
|
|
109
|
+
app = self._create_app()
|
|
110
|
+
|
|
111
|
+
logger.info(f"MCP server '{self.server.name}' starting on http://{self.host}:{self.port}{self.endpoint}")
|
|
112
|
+
|
|
113
|
+
uvicorn.run(app, host=self.host, port=self.port, log_level="warning")
|
|
114
|
+
|
|
115
|
+
def _validate_origin(self, request_origin: Optional[str]) -> bool:
|
|
116
|
+
"""
|
|
117
|
+
Validate Origin header per MCP 2025-11-25 security requirements.
|
|
118
|
+
|
|
119
|
+
Returns True if origin is valid or not present.
|
|
120
|
+
Returns False if origin is present but invalid.
|
|
121
|
+
"""
|
|
122
|
+
if request_origin is None:
|
|
123
|
+
# No Origin header - allow (same-origin requests don't send Origin)
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
if self.allowed_origins is None:
|
|
127
|
+
# No allowed origins configured - reject all Origin headers
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
# Check if origin matches any allowed origin
|
|
131
|
+
for allowed in self.allowed_origins:
|
|
132
|
+
if request_origin == allowed or request_origin.startswith(allowed):
|
|
133
|
+
return True
|
|
134
|
+
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
def _validate_protocol_version(self, version: Optional[str]) -> bool:
|
|
138
|
+
"""Validate MCP-Protocol-Version header."""
|
|
139
|
+
if version is None:
|
|
140
|
+
# No version header - assume 2025-03-26 for backwards compatibility
|
|
141
|
+
return True
|
|
142
|
+
return version in SUPPORTED_VERSIONS
|
|
143
|
+
|
|
144
|
+
def _create_app(self) -> Any:
|
|
145
|
+
"""Create the Starlette/FastAPI application."""
|
|
146
|
+
try:
|
|
147
|
+
from starlette.applications import Starlette
|
|
148
|
+
from starlette.middleware import Middleware
|
|
149
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
150
|
+
from starlette.requests import Request
|
|
151
|
+
from starlette.responses import JSONResponse, Response, StreamingResponse
|
|
152
|
+
from starlette.routing import Route
|
|
153
|
+
except ImportError:
|
|
154
|
+
raise ImportError("starlette required. Install with: pip install starlette")
|
|
155
|
+
|
|
156
|
+
async def mcp_post(request: Request) -> Response:
|
|
157
|
+
"""Handle POST requests (client→server messages)."""
|
|
158
|
+
# Validate Origin header (MCP 2025-11-25 security requirement)
|
|
159
|
+
origin = request.headers.get("Origin")
|
|
160
|
+
if not self._validate_origin(origin):
|
|
161
|
+
logger.warning(f"Rejected request with invalid Origin: {origin}")
|
|
162
|
+
return JSONResponse(
|
|
163
|
+
{"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": "Forbidden: Invalid Origin"}},
|
|
164
|
+
status_code=403,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Validate MCP-Protocol-Version header
|
|
168
|
+
protocol_version = request.headers.get("MCP-Protocol-Version")
|
|
169
|
+
if protocol_version and not self._validate_protocol_version(protocol_version):
|
|
170
|
+
return JSONResponse(
|
|
171
|
+
{"jsonrpc": "2.0", "id": None, "error": {"code": -32600, "message": f"Unsupported protocol version: {protocol_version}"}},
|
|
172
|
+
status_code=400,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Check authentication
|
|
176
|
+
if self.api_key:
|
|
177
|
+
auth_header = request.headers.get("Authorization", "")
|
|
178
|
+
if not auth_header.startswith("Bearer ") or auth_header[7:] != self.api_key:
|
|
179
|
+
return JSONResponse(
|
|
180
|
+
{"error": "Unauthorized"},
|
|
181
|
+
status_code=401,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Get or create session (check both header casings for compatibility)
|
|
185
|
+
session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
|
|
186
|
+
if session_id and session_id not in self._sessions:
|
|
187
|
+
# Session expired
|
|
188
|
+
return JSONResponse(
|
|
189
|
+
{"error": "Session not found"},
|
|
190
|
+
status_code=404,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
# Parse request body
|
|
194
|
+
try:
|
|
195
|
+
body = await request.json()
|
|
196
|
+
except json.JSONDecodeError:
|
|
197
|
+
return JSONResponse(
|
|
198
|
+
{"jsonrpc": "2.0", "id": None, "error": {"code": -32700, "message": "Parse error"}},
|
|
199
|
+
status_code=400,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Handle message
|
|
203
|
+
response = await self.server.handle_message(body)
|
|
204
|
+
|
|
205
|
+
# Check if this is an initialize request
|
|
206
|
+
if body.get("method") == "initialize":
|
|
207
|
+
# Create new session
|
|
208
|
+
new_session_id = str(uuid.uuid4())
|
|
209
|
+
self._sessions[new_session_id] = {
|
|
210
|
+
"created_at": time.time(),
|
|
211
|
+
"last_activity": time.time(),
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
# Add session ID to response headers (MCP 2025-11-25 uses MCP-Session-Id)
|
|
215
|
+
headers = {
|
|
216
|
+
"MCP-Session-Id": new_session_id,
|
|
217
|
+
"MCP-Protocol-Version": PROTOCOL_VERSION,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if response:
|
|
221
|
+
return JSONResponse(response, headers=headers)
|
|
222
|
+
return Response(status_code=202, headers=headers)
|
|
223
|
+
|
|
224
|
+
# Update session activity
|
|
225
|
+
if session_id and session_id in self._sessions:
|
|
226
|
+
self._sessions[session_id]["last_activity"] = time.time()
|
|
227
|
+
|
|
228
|
+
# Check Accept header for response mode
|
|
229
|
+
accept = request.headers.get("Accept", "application/json")
|
|
230
|
+
|
|
231
|
+
if "text/event-stream" in accept and response:
|
|
232
|
+
# Stream mode - return SSE
|
|
233
|
+
async def generate_sse():
|
|
234
|
+
event_id = self._next_event_id()
|
|
235
|
+
data = json.dumps(response)
|
|
236
|
+
yield f"id:{event_id}\ndata:{data}\n\n"
|
|
237
|
+
|
|
238
|
+
return StreamingResponse(
|
|
239
|
+
generate_sse(),
|
|
240
|
+
media_type="text/event-stream",
|
|
241
|
+
headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Batch mode - return JSON
|
|
245
|
+
if response:
|
|
246
|
+
return JSONResponse(
|
|
247
|
+
response,
|
|
248
|
+
headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return Response(
|
|
252
|
+
status_code=202,
|
|
253
|
+
headers={"MCP-Protocol-Version": PROTOCOL_VERSION},
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
async def mcp_get(request: Request) -> Response:
|
|
257
|
+
"""Handle GET requests (server→client SSE stream)."""
|
|
258
|
+
# Check both header casings for compatibility
|
|
259
|
+
session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
|
|
260
|
+
if not session_id or session_id not in self._sessions:
|
|
261
|
+
return JSONResponse(
|
|
262
|
+
{"error": "Session required"},
|
|
263
|
+
status_code=400,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
# Check for Last-Event-ID for resumability
|
|
267
|
+
last_event_id = request.headers.get("Last-Event-ID")
|
|
268
|
+
|
|
269
|
+
async def generate_sse():
|
|
270
|
+
# If resuming, replay missed events
|
|
271
|
+
if last_event_id and session_id in self._event_history:
|
|
272
|
+
history = self._event_history[session_id]
|
|
273
|
+
replay = False
|
|
274
|
+
for event_id, data in history:
|
|
275
|
+
if replay:
|
|
276
|
+
yield f"id:{event_id}\ndata:{data}\n\n"
|
|
277
|
+
elif event_id == last_event_id:
|
|
278
|
+
replay = True
|
|
279
|
+
|
|
280
|
+
# Keep connection alive
|
|
281
|
+
while True:
|
|
282
|
+
await asyncio.sleep(30)
|
|
283
|
+
yield ":keepalive\n\n"
|
|
284
|
+
|
|
285
|
+
return StreamingResponse(
|
|
286
|
+
generate_sse(),
|
|
287
|
+
media_type="text/event-stream",
|
|
288
|
+
headers={
|
|
289
|
+
"Cache-Control": "no-cache",
|
|
290
|
+
"MCP-Protocol-Version": PROTOCOL_VERSION,
|
|
291
|
+
},
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
async def mcp_delete(request: Request) -> Response:
|
|
295
|
+
"""Handle DELETE requests (session termination)."""
|
|
296
|
+
if not self.allow_client_termination:
|
|
297
|
+
return Response(status_code=405)
|
|
298
|
+
|
|
299
|
+
session_id = request.headers.get("MCP-Session-Id") or request.headers.get("Mcp-Session-Id")
|
|
300
|
+
if session_id and session_id in self._sessions:
|
|
301
|
+
del self._sessions[session_id]
|
|
302
|
+
if session_id in self._event_history:
|
|
303
|
+
del self._event_history[session_id]
|
|
304
|
+
return Response(status_code=204)
|
|
305
|
+
|
|
306
|
+
return JSONResponse(
|
|
307
|
+
{"error": "Session not found"},
|
|
308
|
+
status_code=404,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
async def mcp_options(request: Request) -> Response:
|
|
312
|
+
"""Handle OPTIONS requests for CORS preflight."""
|
|
313
|
+
return Response(
|
|
314
|
+
status_code=204,
|
|
315
|
+
headers={
|
|
316
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
317
|
+
"Access-Control-Allow-Headers": "Content-Type, Accept, Authorization, MCP-Session-Id, Mcp-Session-Id, MCP-Protocol-Version, Mcp-Protocol-Version, Last-Event-ID",
|
|
318
|
+
},
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
async def health(request: Request) -> Response:
|
|
322
|
+
"""Health check endpoint."""
|
|
323
|
+
return JSONResponse({
|
|
324
|
+
"status": "healthy",
|
|
325
|
+
"server": self.server.name,
|
|
326
|
+
"version": self.server.version,
|
|
327
|
+
"protocol_version": PROTOCOL_VERSION,
|
|
328
|
+
"active_sessions": len(self._sessions),
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
async def root(request: Request) -> Response:
|
|
332
|
+
"""Root endpoint."""
|
|
333
|
+
return JSONResponse({
|
|
334
|
+
"message": f"PraisonAI MCP Server: {self.server.name}",
|
|
335
|
+
"mcp_endpoint": self.endpoint,
|
|
336
|
+
"protocol_version": PROTOCOL_VERSION,
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
# Create routes
|
|
340
|
+
routes = [
|
|
341
|
+
Route(self.endpoint, mcp_post, methods=["POST"]),
|
|
342
|
+
Route(self.endpoint, mcp_get, methods=["GET"]),
|
|
343
|
+
Route(self.endpoint, mcp_delete, methods=["DELETE"]),
|
|
344
|
+
Route(self.endpoint, mcp_options, methods=["OPTIONS"]),
|
|
345
|
+
Route("/health", health, methods=["GET"]),
|
|
346
|
+
Route("/", root, methods=["GET"]),
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
# Create middleware
|
|
350
|
+
middleware = [
|
|
351
|
+
Middleware(
|
|
352
|
+
CORSMiddleware,
|
|
353
|
+
allow_origins=self.cors_origins,
|
|
354
|
+
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
|
|
355
|
+
allow_headers=["*"],
|
|
356
|
+
),
|
|
357
|
+
]
|
|
358
|
+
|
|
359
|
+
return Starlette(routes=routes, middleware=middleware)
|
|
360
|
+
|
|
361
|
+
def _next_event_id(self) -> str:
|
|
362
|
+
"""Generate next SSE event ID."""
|
|
363
|
+
self._event_counter += 1
|
|
364
|
+
return str(self._event_counter)
|
|
365
|
+
|
|
366
|
+
def _cleanup_sessions(self) -> None:
|
|
367
|
+
"""Clean up expired sessions."""
|
|
368
|
+
now = time.time()
|
|
369
|
+
expired = [
|
|
370
|
+
sid for sid, data in self._sessions.items()
|
|
371
|
+
if now - data["last_activity"] > self.session_ttl
|
|
372
|
+
]
|
|
373
|
+
for sid in expired:
|
|
374
|
+
del self._sessions[sid]
|
|
375
|
+
if sid in self._event_history:
|
|
376
|
+
del self._event_history[sid]
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""
|
|
2
|
+
STDIO Transport for MCP Server
|
|
3
|
+
|
|
4
|
+
Implements the MCP STDIO transport protocol:
|
|
5
|
+
- JSON-RPC 2.0 messages over stdin/stdout
|
|
6
|
+
- Messages delimited by newlines
|
|
7
|
+
- Logs go to stderr only
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import sys
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from ..server import MCPServer
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class StdioTransport:
|
|
23
|
+
"""
|
|
24
|
+
STDIO transport for MCP server.
|
|
25
|
+
|
|
26
|
+
Reads JSON-RPC messages from stdin and writes responses to stdout.
|
|
27
|
+
All logging goes to stderr to avoid corrupting the protocol stream.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, server: "MCPServer"):
|
|
31
|
+
"""
|
|
32
|
+
Initialize STDIO transport.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
server: MCPServer instance to handle messages
|
|
36
|
+
"""
|
|
37
|
+
self.server = server
|
|
38
|
+
self._running = False
|
|
39
|
+
|
|
40
|
+
async def run(self) -> None:
|
|
41
|
+
"""Run the STDIO transport loop."""
|
|
42
|
+
self._running = True
|
|
43
|
+
|
|
44
|
+
# Configure logging to stderr only
|
|
45
|
+
handler = logging.StreamHandler(sys.stderr)
|
|
46
|
+
handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
|
|
47
|
+
logger.addHandler(handler)
|
|
48
|
+
|
|
49
|
+
logger.info(f"MCP server '{self.server.name}' starting on STDIO transport")
|
|
50
|
+
|
|
51
|
+
# Use asyncio streams for non-blocking I/O
|
|
52
|
+
loop = asyncio.get_event_loop()
|
|
53
|
+
reader = asyncio.StreamReader()
|
|
54
|
+
protocol = asyncio.StreamReaderProtocol(reader)
|
|
55
|
+
|
|
56
|
+
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
|
|
57
|
+
|
|
58
|
+
# Create writer for stdout
|
|
59
|
+
write_transport, write_protocol = await loop.connect_write_pipe(
|
|
60
|
+
asyncio.streams.FlowControlMixin, sys.stdout
|
|
61
|
+
)
|
|
62
|
+
writer = asyncio.StreamWriter(write_transport, write_protocol, reader, loop)
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
while self._running:
|
|
66
|
+
try:
|
|
67
|
+
# Read a line (JSON-RPC message)
|
|
68
|
+
line = await reader.readline()
|
|
69
|
+
if not line:
|
|
70
|
+
# EOF reached
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
line = line.decode("utf-8").strip()
|
|
74
|
+
if not line:
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
# Parse JSON-RPC message
|
|
78
|
+
try:
|
|
79
|
+
message = json.loads(line)
|
|
80
|
+
except json.JSONDecodeError as e:
|
|
81
|
+
logger.error(f"JSON parse error: {e}")
|
|
82
|
+
error_response = {
|
|
83
|
+
"jsonrpc": "2.0",
|
|
84
|
+
"id": None,
|
|
85
|
+
"error": {
|
|
86
|
+
"code": -32700,
|
|
87
|
+
"message": f"Parse error: {e}",
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
await self._write_message(writer, error_response)
|
|
91
|
+
continue
|
|
92
|
+
|
|
93
|
+
# Handle message
|
|
94
|
+
response = await self.server.handle_message(message)
|
|
95
|
+
|
|
96
|
+
# Write response if not a notification
|
|
97
|
+
if response is not None:
|
|
98
|
+
await self._write_message(writer, response)
|
|
99
|
+
|
|
100
|
+
except asyncio.CancelledError:
|
|
101
|
+
break
|
|
102
|
+
except Exception as e:
|
|
103
|
+
logger.exception(f"Error processing message: {e}")
|
|
104
|
+
|
|
105
|
+
finally:
|
|
106
|
+
self._running = False
|
|
107
|
+
writer.close()
|
|
108
|
+
logger.info("MCP server stopped")
|
|
109
|
+
|
|
110
|
+
async def _write_message(self, writer: asyncio.StreamWriter, message: dict) -> None:
|
|
111
|
+
"""Write a JSON-RPC message to stdout."""
|
|
112
|
+
try:
|
|
113
|
+
data = json.dumps(message) + "\n"
|
|
114
|
+
writer.write(data.encode("utf-8"))
|
|
115
|
+
await writer.drain()
|
|
116
|
+
except Exception as e:
|
|
117
|
+
logger.error(f"Error writing message: {e}")
|
|
118
|
+
|
|
119
|
+
def stop(self) -> None:
|
|
120
|
+
"""Stop the transport."""
|
|
121
|
+
self._running = False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def run_stdio_server(server: "MCPServer") -> None:
|
|
125
|
+
"""
|
|
126
|
+
Convenience function to run a server with STDIO transport.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
server: MCPServer instance
|
|
130
|
+
"""
|
|
131
|
+
transport = StdioTransport(server)
|
|
132
|
+
asyncio.run(transport.run())
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PraisonAI Persistence Layer
|
|
3
|
+
|
|
4
|
+
Provides database integrations for:
|
|
5
|
+
- Conversation history persistence (ConversationStore)
|
|
6
|
+
- Knowledge storage and retrieval (KnowledgeStore)
|
|
7
|
+
- Session/state management (StateStore)
|
|
8
|
+
|
|
9
|
+
All integrations use lazy loading to avoid importing unused dependencies.
|
|
10
|
+
|
|
11
|
+
Supported Backends (22 total):
|
|
12
|
+
- ConversationStore (6): postgres, mysql, sqlite, singlestore, supabase, surrealdb
|
|
13
|
+
- KnowledgeStore (10): qdrant, pinecone, chroma, weaviate, lancedb, milvus, pgvector, redis, cassandra, clickhouse
|
|
14
|
+
- StateStore (6): redis, dynamodb, firestore, mongodb, upstash, memory
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
# Base interfaces
|
|
21
|
+
"ConversationStore",
|
|
22
|
+
"ConversationSession",
|
|
23
|
+
"ConversationMessage",
|
|
24
|
+
"KnowledgeStore",
|
|
25
|
+
"KnowledgeDocument",
|
|
26
|
+
"StateStore",
|
|
27
|
+
# Orchestrator
|
|
28
|
+
"PersistenceOrchestrator",
|
|
29
|
+
# Factory functions
|
|
30
|
+
"create_conversation_store",
|
|
31
|
+
"create_knowledge_store",
|
|
32
|
+
"create_state_store",
|
|
33
|
+
"create_stores_from_config",
|
|
34
|
+
# Config
|
|
35
|
+
"PersistenceConfig",
|
|
36
|
+
# Agent hooks
|
|
37
|
+
"wrap_agent_with_persistence",
|
|
38
|
+
"PersistentAgent",
|
|
39
|
+
"create_persistent_session",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
# Lazy imports to avoid loading dependencies until needed
|
|
43
|
+
def __getattr__(name: str):
|
|
44
|
+
if name in ("ConversationStore", "ConversationSession", "ConversationMessage"):
|
|
45
|
+
from .conversation.base import ConversationStore, ConversationSession, ConversationMessage
|
|
46
|
+
return locals()[name]
|
|
47
|
+
|
|
48
|
+
if name in ("KnowledgeStore", "KnowledgeDocument"):
|
|
49
|
+
from .knowledge.base import KnowledgeStore, KnowledgeDocument
|
|
50
|
+
return locals()[name]
|
|
51
|
+
|
|
52
|
+
if name == "StateStore":
|
|
53
|
+
from .state.base import StateStore
|
|
54
|
+
return StateStore
|
|
55
|
+
|
|
56
|
+
if name == "PersistenceOrchestrator":
|
|
57
|
+
from .orchestrator import PersistenceOrchestrator
|
|
58
|
+
return PersistenceOrchestrator
|
|
59
|
+
|
|
60
|
+
if name == "PersistenceConfig":
|
|
61
|
+
from .config import PersistenceConfig
|
|
62
|
+
return PersistenceConfig
|
|
63
|
+
|
|
64
|
+
if name == "create_conversation_store":
|
|
65
|
+
from .factory import create_conversation_store
|
|
66
|
+
return create_conversation_store
|
|
67
|
+
|
|
68
|
+
if name == "create_knowledge_store":
|
|
69
|
+
from .factory import create_knowledge_store
|
|
70
|
+
return create_knowledge_store
|
|
71
|
+
|
|
72
|
+
if name == "create_state_store":
|
|
73
|
+
from .factory import create_state_store
|
|
74
|
+
return create_state_store
|
|
75
|
+
|
|
76
|
+
if name == "create_stores_from_config":
|
|
77
|
+
from .factory import create_stores_from_config
|
|
78
|
+
return create_stores_from_config
|
|
79
|
+
|
|
80
|
+
if name in ("wrap_agent_with_persistence", "PersistentAgent", "create_persistent_session"):
|
|
81
|
+
from .hooks.agent_hooks import wrap_agent_with_persistence, PersistentAgent, create_persistent_session
|
|
82
|
+
return locals()[name]
|
|
83
|
+
|
|
84
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|