PraisonAI 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tools checks for the Doctor CLI module.
|
|
3
|
+
|
|
4
|
+
Validates tool availability, dependencies, and API keys.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
from ..models import (
|
|
10
|
+
CheckCategory,
|
|
11
|
+
CheckResult,
|
|
12
|
+
CheckStatus,
|
|
13
|
+
CheckSeverity,
|
|
14
|
+
DoctorConfig,
|
|
15
|
+
)
|
|
16
|
+
from ..registry import register_check
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Tool categories and their requirements
|
|
20
|
+
TOOL_CATEGORIES = {
|
|
21
|
+
"web_search": {
|
|
22
|
+
"tools": ["internet_search", "duckduckgo", "tavily_search", "exa_search", "searxng_search"],
|
|
23
|
+
"description": "Web search tools",
|
|
24
|
+
},
|
|
25
|
+
"file_ops": {
|
|
26
|
+
"tools": ["read_file", "write_file", "list_files", "file_tools"],
|
|
27
|
+
"description": "File operation tools",
|
|
28
|
+
},
|
|
29
|
+
"data": {
|
|
30
|
+
"tools": ["csv_tools", "json_tools", "excel_tools", "xml_tools", "yaml_tools", "pandas_tools"],
|
|
31
|
+
"description": "Data processing tools",
|
|
32
|
+
},
|
|
33
|
+
"database": {
|
|
34
|
+
"tools": ["duckdb_tools", "mongodb_tools"],
|
|
35
|
+
"description": "Database tools",
|
|
36
|
+
},
|
|
37
|
+
"code": {
|
|
38
|
+
"tools": ["python_tools", "shell_tools", "execute_code"],
|
|
39
|
+
"description": "Code execution tools",
|
|
40
|
+
},
|
|
41
|
+
"web_crawl": {
|
|
42
|
+
"tools": ["crawl4ai", "spider_tools", "newspaper_tools"],
|
|
43
|
+
"description": "Web crawling tools",
|
|
44
|
+
},
|
|
45
|
+
"knowledge": {
|
|
46
|
+
"tools": ["wiki_search", "arxiv_tools", "wikipedia_tools"],
|
|
47
|
+
"description": "Knowledge/research tools",
|
|
48
|
+
},
|
|
49
|
+
"finance": {
|
|
50
|
+
"tools": ["yfinance", "get_stock_price", "get_stock_info"],
|
|
51
|
+
"description": "Financial data tools",
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Tools that require API keys
|
|
56
|
+
TOOLS_REQUIRING_API_KEYS = {
|
|
57
|
+
"tavily_search": "TAVILY_API_KEY",
|
|
58
|
+
"tavily": "TAVILY_API_KEY",
|
|
59
|
+
"exa_search": "EXA_API_KEY",
|
|
60
|
+
"exa": "EXA_API_KEY",
|
|
61
|
+
"ydc_search": "YDC_API_KEY",
|
|
62
|
+
"ydc": "YDC_API_KEY",
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Tools that require optional dependencies
|
|
66
|
+
TOOLS_REQUIRING_DEPS = {
|
|
67
|
+
"crawl4ai": "crawl4ai",
|
|
68
|
+
"tavily_search": "tavily",
|
|
69
|
+
"tavily": "tavily",
|
|
70
|
+
"pandas_tools": "pandas",
|
|
71
|
+
"excel_tools": "openpyxl",
|
|
72
|
+
"yfinance": "yfinance",
|
|
73
|
+
"duckdb_tools": "duckdb",
|
|
74
|
+
"mongodb_tools": "pymongo",
|
|
75
|
+
"newspaper_tools": "newspaper3k",
|
|
76
|
+
"arxiv_tools": "arxiv",
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@register_check(
|
|
81
|
+
id="tools_registry",
|
|
82
|
+
title="Tools Registry",
|
|
83
|
+
description="Check tool registry is accessible",
|
|
84
|
+
category=CheckCategory.TOOLS,
|
|
85
|
+
severity=CheckSeverity.MEDIUM,
|
|
86
|
+
)
|
|
87
|
+
def check_tools_registry(config: DoctorConfig) -> CheckResult:
|
|
88
|
+
"""Check tool registry is accessible."""
|
|
89
|
+
try:
|
|
90
|
+
from praisonaiagents.tools import TOOL_MAPPINGS
|
|
91
|
+
tool_count = len(TOOL_MAPPINGS)
|
|
92
|
+
|
|
93
|
+
return CheckResult(
|
|
94
|
+
id="tools_registry",
|
|
95
|
+
title="Tools Registry",
|
|
96
|
+
category=CheckCategory.TOOLS,
|
|
97
|
+
status=CheckStatus.PASS,
|
|
98
|
+
message=f"Tool registry accessible with {tool_count} tools",
|
|
99
|
+
metadata={"tool_count": tool_count},
|
|
100
|
+
)
|
|
101
|
+
except ImportError as e:
|
|
102
|
+
return CheckResult(
|
|
103
|
+
id="tools_registry",
|
|
104
|
+
title="Tools Registry",
|
|
105
|
+
category=CheckCategory.TOOLS,
|
|
106
|
+
status=CheckStatus.FAIL,
|
|
107
|
+
message="Cannot access tool registry",
|
|
108
|
+
details=str(e),
|
|
109
|
+
remediation="Ensure praisonaiagents is properly installed",
|
|
110
|
+
severity=CheckSeverity.HIGH,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@register_check(
|
|
115
|
+
id="tools_web_search",
|
|
116
|
+
title="Web Search Tools",
|
|
117
|
+
description="Check web search tool availability",
|
|
118
|
+
category=CheckCategory.TOOLS,
|
|
119
|
+
severity=CheckSeverity.MEDIUM,
|
|
120
|
+
)
|
|
121
|
+
def check_tools_web_search(config: DoctorConfig) -> CheckResult:
|
|
122
|
+
"""Check web search tool availability."""
|
|
123
|
+
available = []
|
|
124
|
+
missing = []
|
|
125
|
+
|
|
126
|
+
# Check DuckDuckGo (no API key required)
|
|
127
|
+
try:
|
|
128
|
+
from praisonaiagents.tools import internet_search
|
|
129
|
+
available.append("internet_search (DuckDuckGo)")
|
|
130
|
+
except ImportError:
|
|
131
|
+
missing.append("internet_search")
|
|
132
|
+
|
|
133
|
+
# Check Tavily
|
|
134
|
+
if os.environ.get("TAVILY_API_KEY"):
|
|
135
|
+
try:
|
|
136
|
+
from praisonaiagents.tools import tavily_search
|
|
137
|
+
available.append("tavily_search")
|
|
138
|
+
except ImportError:
|
|
139
|
+
missing.append("tavily_search (missing tavily package)")
|
|
140
|
+
else:
|
|
141
|
+
missing.append("tavily_search (TAVILY_API_KEY not set)")
|
|
142
|
+
|
|
143
|
+
# Check Exa
|
|
144
|
+
if os.environ.get("EXA_API_KEY"):
|
|
145
|
+
try:
|
|
146
|
+
from praisonaiagents.tools import exa_search
|
|
147
|
+
available.append("exa_search")
|
|
148
|
+
except ImportError:
|
|
149
|
+
missing.append("exa_search (missing exa package)")
|
|
150
|
+
else:
|
|
151
|
+
missing.append("exa_search (EXA_API_KEY not set)")
|
|
152
|
+
|
|
153
|
+
if available:
|
|
154
|
+
return CheckResult(
|
|
155
|
+
id="tools_web_search",
|
|
156
|
+
title="Web Search Tools",
|
|
157
|
+
category=CheckCategory.TOOLS,
|
|
158
|
+
status=CheckStatus.PASS,
|
|
159
|
+
message=f"{len(available)} web search tool(s) available",
|
|
160
|
+
details=", ".join(available),
|
|
161
|
+
metadata={"available": available, "missing": missing},
|
|
162
|
+
)
|
|
163
|
+
else:
|
|
164
|
+
return CheckResult(
|
|
165
|
+
id="tools_web_search",
|
|
166
|
+
title="Web Search Tools",
|
|
167
|
+
category=CheckCategory.TOOLS,
|
|
168
|
+
status=CheckStatus.WARN,
|
|
169
|
+
message="No web search tools available",
|
|
170
|
+
details=", ".join(missing),
|
|
171
|
+
remediation="Install duckduckgo-search or set TAVILY_API_KEY",
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@register_check(
|
|
176
|
+
id="tools_file_ops",
|
|
177
|
+
title="File Operation Tools",
|
|
178
|
+
description="Check file operation tool availability",
|
|
179
|
+
category=CheckCategory.TOOLS,
|
|
180
|
+
severity=CheckSeverity.LOW,
|
|
181
|
+
)
|
|
182
|
+
def check_tools_file_ops(config: DoctorConfig) -> CheckResult:
|
|
183
|
+
"""Check file operation tool availability."""
|
|
184
|
+
try:
|
|
185
|
+
from praisonaiagents.tools import read_file, write_file, list_files
|
|
186
|
+
return CheckResult(
|
|
187
|
+
id="tools_file_ops",
|
|
188
|
+
title="File Operation Tools",
|
|
189
|
+
category=CheckCategory.TOOLS,
|
|
190
|
+
status=CheckStatus.PASS,
|
|
191
|
+
message="File operation tools available (read_file, write_file, list_files)",
|
|
192
|
+
)
|
|
193
|
+
except ImportError as e:
|
|
194
|
+
return CheckResult(
|
|
195
|
+
id="tools_file_ops",
|
|
196
|
+
title="File Operation Tools",
|
|
197
|
+
category=CheckCategory.TOOLS,
|
|
198
|
+
status=CheckStatus.WARN,
|
|
199
|
+
message="Some file operation tools not available",
|
|
200
|
+
details=str(e),
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@register_check(
|
|
205
|
+
id="tools_code_execution",
|
|
206
|
+
title="Code Execution Tools",
|
|
207
|
+
description="Check code execution tool availability",
|
|
208
|
+
category=CheckCategory.TOOLS,
|
|
209
|
+
severity=CheckSeverity.LOW,
|
|
210
|
+
)
|
|
211
|
+
def check_tools_code_execution(config: DoctorConfig) -> CheckResult:
|
|
212
|
+
"""Check code execution tool availability."""
|
|
213
|
+
available = []
|
|
214
|
+
|
|
215
|
+
try:
|
|
216
|
+
from praisonaiagents.tools import execute_code
|
|
217
|
+
available.append("execute_code")
|
|
218
|
+
except ImportError:
|
|
219
|
+
pass
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
from praisonaiagents.tools import execute_command
|
|
223
|
+
available.append("execute_command")
|
|
224
|
+
except ImportError:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
if available:
|
|
228
|
+
return CheckResult(
|
|
229
|
+
id="tools_code_execution",
|
|
230
|
+
title="Code Execution Tools",
|
|
231
|
+
category=CheckCategory.TOOLS,
|
|
232
|
+
status=CheckStatus.PASS,
|
|
233
|
+
message=f"Code execution tools available: {', '.join(available)}",
|
|
234
|
+
metadata={"available": available},
|
|
235
|
+
)
|
|
236
|
+
else:
|
|
237
|
+
return CheckResult(
|
|
238
|
+
id="tools_code_execution",
|
|
239
|
+
title="Code Execution Tools",
|
|
240
|
+
category=CheckCategory.TOOLS,
|
|
241
|
+
status=CheckStatus.SKIP,
|
|
242
|
+
message="Code execution tools not loaded (optional)",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@register_check(
|
|
247
|
+
id="tools_api_keys",
|
|
248
|
+
title="Tool API Keys",
|
|
249
|
+
description="Check API keys for tools that require them",
|
|
250
|
+
category=CheckCategory.TOOLS,
|
|
251
|
+
severity=CheckSeverity.INFO,
|
|
252
|
+
)
|
|
253
|
+
def check_tools_api_keys(config: DoctorConfig) -> CheckResult:
|
|
254
|
+
"""Check API keys for tools that require them."""
|
|
255
|
+
configured = []
|
|
256
|
+
missing = []
|
|
257
|
+
|
|
258
|
+
api_keys = {
|
|
259
|
+
"TAVILY_API_KEY": "Tavily search",
|
|
260
|
+
"EXA_API_KEY": "Exa search",
|
|
261
|
+
"YDC_API_KEY": "You.com search",
|
|
262
|
+
"SPIDER_API_KEY": "Spider web crawling",
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
for key, description in api_keys.items():
|
|
266
|
+
if os.environ.get(key):
|
|
267
|
+
configured.append(description)
|
|
268
|
+
else:
|
|
269
|
+
missing.append(f"{description} ({key})")
|
|
270
|
+
|
|
271
|
+
if configured:
|
|
272
|
+
return CheckResult(
|
|
273
|
+
id="tools_api_keys",
|
|
274
|
+
title="Tool API Keys",
|
|
275
|
+
category=CheckCategory.TOOLS,
|
|
276
|
+
status=CheckStatus.PASS,
|
|
277
|
+
message=f"{len(configured)} tool API key(s) configured",
|
|
278
|
+
details=f"Configured: {', '.join(configured)}",
|
|
279
|
+
metadata={"configured": configured, "missing": missing},
|
|
280
|
+
)
|
|
281
|
+
else:
|
|
282
|
+
return CheckResult(
|
|
283
|
+
id="tools_api_keys",
|
|
284
|
+
title="Tool API Keys",
|
|
285
|
+
category=CheckCategory.TOOLS,
|
|
286
|
+
status=CheckStatus.SKIP,
|
|
287
|
+
message="No optional tool API keys configured",
|
|
288
|
+
details="Tools like Tavily, Exa require API keys for enhanced functionality",
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@register_check(
|
|
293
|
+
id="tools_optional_deps",
|
|
294
|
+
title="Tool Optional Dependencies",
|
|
295
|
+
description="Check optional dependencies for tools",
|
|
296
|
+
category=CheckCategory.TOOLS,
|
|
297
|
+
severity=CheckSeverity.INFO,
|
|
298
|
+
requires_deep=True,
|
|
299
|
+
)
|
|
300
|
+
def check_tools_optional_deps(config: DoctorConfig) -> CheckResult:
|
|
301
|
+
"""Check optional dependencies for tools."""
|
|
302
|
+
available = []
|
|
303
|
+
missing = []
|
|
304
|
+
|
|
305
|
+
deps_to_check = [
|
|
306
|
+
("crawl4ai", "Crawl4AI web crawling"),
|
|
307
|
+
("tavily", "Tavily search"),
|
|
308
|
+
("pandas", "Pandas data tools"),
|
|
309
|
+
("openpyxl", "Excel tools"),
|
|
310
|
+
("yfinance", "Yahoo Finance tools"),
|
|
311
|
+
("duckdb", "DuckDB tools"),
|
|
312
|
+
("pymongo", "MongoDB tools"),
|
|
313
|
+
("arxiv", "arXiv tools"),
|
|
314
|
+
]
|
|
315
|
+
|
|
316
|
+
for package, description in deps_to_check:
|
|
317
|
+
try:
|
|
318
|
+
__import__(package)
|
|
319
|
+
available.append(description)
|
|
320
|
+
except ImportError:
|
|
321
|
+
missing.append(f"{description} ({package})")
|
|
322
|
+
|
|
323
|
+
total = len(deps_to_check)
|
|
324
|
+
|
|
325
|
+
return CheckResult(
|
|
326
|
+
id="tools_optional_deps",
|
|
327
|
+
title="Tool Optional Dependencies",
|
|
328
|
+
category=CheckCategory.TOOLS,
|
|
329
|
+
status=CheckStatus.PASS,
|
|
330
|
+
message=f"{len(available)}/{total} optional tool dependencies installed",
|
|
331
|
+
details=f"Missing: {', '.join(missing[:3])}{'...' if len(missing) > 3 else ''}" if missing else "All installed",
|
|
332
|
+
metadata={"available": available, "missing": missing},
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
@register_check(
|
|
337
|
+
id="tools_summary",
|
|
338
|
+
title="Tools Summary",
|
|
339
|
+
description="Summary of tool availability by category",
|
|
340
|
+
category=CheckCategory.TOOLS,
|
|
341
|
+
severity=CheckSeverity.INFO,
|
|
342
|
+
)
|
|
343
|
+
def check_tools_summary(config: DoctorConfig) -> CheckResult:
|
|
344
|
+
"""Summary of tool availability by category."""
|
|
345
|
+
try:
|
|
346
|
+
from praisonaiagents.tools import TOOL_MAPPINGS
|
|
347
|
+
|
|
348
|
+
category_counts = {}
|
|
349
|
+
for cat_name, cat_info in TOOL_CATEGORIES.items():
|
|
350
|
+
available = sum(1 for t in cat_info["tools"] if t in TOOL_MAPPINGS)
|
|
351
|
+
total = len(cat_info["tools"])
|
|
352
|
+
category_counts[cat_name] = f"{available}/{total}"
|
|
353
|
+
|
|
354
|
+
summary = ", ".join(f"{k}: {v}" for k, v in category_counts.items())
|
|
355
|
+
|
|
356
|
+
return CheckResult(
|
|
357
|
+
id="tools_summary",
|
|
358
|
+
title="Tools Summary",
|
|
359
|
+
category=CheckCategory.TOOLS,
|
|
360
|
+
status=CheckStatus.PASS,
|
|
361
|
+
message=f"Tool categories: {summary}",
|
|
362
|
+
metadata={"categories": category_counts},
|
|
363
|
+
)
|
|
364
|
+
except ImportError:
|
|
365
|
+
return CheckResult(
|
|
366
|
+
id="tools_summary",
|
|
367
|
+
title="Tools Summary",
|
|
368
|
+
category=CheckCategory.TOOLS,
|
|
369
|
+
status=CheckStatus.SKIP,
|
|
370
|
+
message="Cannot generate tools summary",
|
|
371
|
+
)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Doctor engine for executing health checks.
|
|
3
|
+
|
|
4
|
+
Provides the core execution logic for running doctor checks.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import platform
|
|
9
|
+
import sys
|
|
10
|
+
import traceback
|
|
11
|
+
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError
|
|
12
|
+
from datetime import datetime, timezone
|
|
13
|
+
from typing import Callable, List, Optional
|
|
14
|
+
import time
|
|
15
|
+
|
|
16
|
+
from .models import (
|
|
17
|
+
CheckCategory,
|
|
18
|
+
CheckResult,
|
|
19
|
+
CheckStatus,
|
|
20
|
+
CheckSeverity,
|
|
21
|
+
DoctorConfig,
|
|
22
|
+
DoctorReport,
|
|
23
|
+
EnvironmentSummary,
|
|
24
|
+
)
|
|
25
|
+
from .registry import CheckRegistry, get_registry
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class DoctorEngine:
|
|
29
|
+
"""
|
|
30
|
+
Engine for executing doctor checks.
|
|
31
|
+
|
|
32
|
+
Handles check execution, timeout management, and report generation.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, config: Optional[DoctorConfig] = None):
|
|
36
|
+
"""
|
|
37
|
+
Initialize the doctor engine.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
config: Doctor configuration
|
|
41
|
+
"""
|
|
42
|
+
self.config = config or DoctorConfig()
|
|
43
|
+
self.registry = get_registry()
|
|
44
|
+
self._results: List[CheckResult] = []
|
|
45
|
+
|
|
46
|
+
def get_environment_summary(self) -> EnvironmentSummary:
|
|
47
|
+
"""Gather environment information."""
|
|
48
|
+
# Get package versions lazily
|
|
49
|
+
praisonai_version = "unknown"
|
|
50
|
+
praisonaiagents_version = "unknown"
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
from praisonai.version import __version__ as pai_version
|
|
54
|
+
praisonai_version = pai_version
|
|
55
|
+
except ImportError:
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
import praisonaiagents
|
|
60
|
+
praisonaiagents_version = getattr(praisonaiagents, "__version__", "unknown")
|
|
61
|
+
except ImportError:
|
|
62
|
+
pass
|
|
63
|
+
|
|
64
|
+
return EnvironmentSummary(
|
|
65
|
+
python_version=f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
|
|
66
|
+
python_executable=sys.executable,
|
|
67
|
+
os_name=platform.system(),
|
|
68
|
+
os_version=platform.release(),
|
|
69
|
+
architecture=platform.machine(),
|
|
70
|
+
praisonai_version=praisonai_version,
|
|
71
|
+
praisonaiagents_version=praisonaiagents_version,
|
|
72
|
+
working_directory=os.getcwd(),
|
|
73
|
+
virtual_env=os.environ.get("VIRTUAL_ENV"),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def run_check(
|
|
77
|
+
self,
|
|
78
|
+
check_id: str,
|
|
79
|
+
implementation: Callable,
|
|
80
|
+
timeout: Optional[float] = None,
|
|
81
|
+
) -> CheckResult:
|
|
82
|
+
"""
|
|
83
|
+
Run a single check with timeout handling.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
check_id: Check identifier
|
|
87
|
+
implementation: Check function
|
|
88
|
+
timeout: Timeout in seconds
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Check result
|
|
92
|
+
"""
|
|
93
|
+
timeout = timeout or self.config.timeout
|
|
94
|
+
definition = self.registry.get_check(check_id)
|
|
95
|
+
|
|
96
|
+
start_time = time.time()
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
# Run with timeout using ThreadPoolExecutor
|
|
100
|
+
with ThreadPoolExecutor(max_workers=1) as executor:
|
|
101
|
+
future = executor.submit(implementation, self.config)
|
|
102
|
+
try:
|
|
103
|
+
result = future.result(timeout=timeout)
|
|
104
|
+
result.duration_ms = (time.time() - start_time) * 1000
|
|
105
|
+
return result
|
|
106
|
+
except FuturesTimeoutError:
|
|
107
|
+
return CheckResult(
|
|
108
|
+
id=check_id,
|
|
109
|
+
title=definition.title if definition else check_id,
|
|
110
|
+
category=definition.category if definition else CheckCategory.ENVIRONMENT,
|
|
111
|
+
status=CheckStatus.ERROR,
|
|
112
|
+
message=f"Check timed out after {timeout}s",
|
|
113
|
+
remediation="Increase timeout with --timeout or check for hanging operations",
|
|
114
|
+
duration_ms=(time.time() - start_time) * 1000,
|
|
115
|
+
severity=CheckSeverity.HIGH,
|
|
116
|
+
)
|
|
117
|
+
except Exception as e:
|
|
118
|
+
# Capture exception details but redact potential secrets
|
|
119
|
+
tb = traceback.format_exc()
|
|
120
|
+
return CheckResult(
|
|
121
|
+
id=check_id,
|
|
122
|
+
title=definition.title if definition else check_id,
|
|
123
|
+
category=definition.category if definition else CheckCategory.ENVIRONMENT,
|
|
124
|
+
status=CheckStatus.ERROR,
|
|
125
|
+
message=f"Check failed with error: {type(e).__name__}: {str(e)[:100]}",
|
|
126
|
+
details=tb[:500] if not self.config.quiet else None,
|
|
127
|
+
remediation="Check the error details and fix the underlying issue",
|
|
128
|
+
duration_ms=(time.time() - start_time) * 1000,
|
|
129
|
+
severity=CheckSeverity.HIGH,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def run_checks(
|
|
133
|
+
self,
|
|
134
|
+
check_ids: Optional[List[str]] = None,
|
|
135
|
+
categories: Optional[List[CheckCategory]] = None,
|
|
136
|
+
) -> List[CheckResult]:
|
|
137
|
+
"""
|
|
138
|
+
Run multiple checks.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
check_ids: Specific check IDs to run (None = all)
|
|
142
|
+
categories: Filter by categories
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
List of check results
|
|
146
|
+
"""
|
|
147
|
+
# Get filtered checks
|
|
148
|
+
checks = self.registry.filter_checks(
|
|
149
|
+
only=check_ids or self.config.only or None,
|
|
150
|
+
skip=self.config.skip or None,
|
|
151
|
+
categories=categories,
|
|
152
|
+
deep_mode=self.config.deep,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Resolve dependencies and get ordered list
|
|
156
|
+
ordered_ids = self.registry.resolve_dependencies([c.id for c in checks])
|
|
157
|
+
|
|
158
|
+
results = []
|
|
159
|
+
failed_deps = set()
|
|
160
|
+
|
|
161
|
+
for check_id in ordered_ids:
|
|
162
|
+
definition = self.registry.get_check(check_id)
|
|
163
|
+
implementation = self.registry.get_implementation(check_id)
|
|
164
|
+
|
|
165
|
+
if not definition or not implementation:
|
|
166
|
+
continue
|
|
167
|
+
|
|
168
|
+
# Check if dependencies failed
|
|
169
|
+
if any(dep in failed_deps for dep in definition.dependencies):
|
|
170
|
+
results.append(CheckResult(
|
|
171
|
+
id=check_id,
|
|
172
|
+
title=definition.title,
|
|
173
|
+
category=definition.category,
|
|
174
|
+
status=CheckStatus.SKIP,
|
|
175
|
+
message="Skipped due to failed dependency",
|
|
176
|
+
severity=definition.severity,
|
|
177
|
+
))
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
# Run the check
|
|
181
|
+
result = self.run_check(check_id, implementation)
|
|
182
|
+
results.append(result)
|
|
183
|
+
|
|
184
|
+
# Track failures for dependency resolution
|
|
185
|
+
if result.status in (CheckStatus.FAIL, CheckStatus.ERROR):
|
|
186
|
+
failed_deps.add(check_id)
|
|
187
|
+
|
|
188
|
+
# Fail fast if configured
|
|
189
|
+
if self.config.fail_fast:
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
self._results = results
|
|
193
|
+
return results
|
|
194
|
+
|
|
195
|
+
def generate_report(self, results: Optional[List[CheckResult]] = None) -> DoctorReport:
|
|
196
|
+
"""
|
|
197
|
+
Generate a complete doctor report.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
results: Check results (uses stored results if None)
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
Complete doctor report
|
|
204
|
+
"""
|
|
205
|
+
results = results or self._results
|
|
206
|
+
|
|
207
|
+
report = DoctorReport(
|
|
208
|
+
version="1.0.0",
|
|
209
|
+
timestamp=datetime.now(timezone.utc).isoformat(),
|
|
210
|
+
environment=self.get_environment_summary(),
|
|
211
|
+
results=results,
|
|
212
|
+
mode="deep" if self.config.deep else "fast",
|
|
213
|
+
filters={
|
|
214
|
+
"only": self.config.only,
|
|
215
|
+
"skip": self.config.skip,
|
|
216
|
+
},
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
report.calculate_summary()
|
|
220
|
+
report.exit_code = report.calculate_exit_code(strict=self.config.strict)
|
|
221
|
+
|
|
222
|
+
return report
|
|
223
|
+
|
|
224
|
+
def run(
|
|
225
|
+
self,
|
|
226
|
+
check_ids: Optional[List[str]] = None,
|
|
227
|
+
categories: Optional[List[CheckCategory]] = None,
|
|
228
|
+
) -> DoctorReport:
|
|
229
|
+
"""
|
|
230
|
+
Run checks and generate report.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
check_ids: Specific check IDs to run
|
|
234
|
+
categories: Filter by categories
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Complete doctor report
|
|
238
|
+
"""
|
|
239
|
+
start_time = time.time()
|
|
240
|
+
|
|
241
|
+
results = self.run_checks(check_ids, categories)
|
|
242
|
+
report = self.generate_report(results)
|
|
243
|
+
|
|
244
|
+
report.duration_ms = (time.time() - start_time) * 1000
|
|
245
|
+
|
|
246
|
+
return report
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def run_doctor(
|
|
250
|
+
config: Optional[DoctorConfig] = None,
|
|
251
|
+
check_ids: Optional[List[str]] = None,
|
|
252
|
+
categories: Optional[List[CheckCategory]] = None,
|
|
253
|
+
) -> DoctorReport:
|
|
254
|
+
"""
|
|
255
|
+
Convenience function to run doctor checks.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
config: Doctor configuration
|
|
259
|
+
check_ids: Specific check IDs to run
|
|
260
|
+
categories: Filter by categories
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Complete doctor report
|
|
264
|
+
"""
|
|
265
|
+
engine = DoctorEngine(config)
|
|
266
|
+
return engine.run(check_ids, categories)
|