praisonai-code 0.0.1__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_code/__init__.py +17 -0
- praisonai_code/cli/__init__.py +12 -0
- praisonai_code/cli/_forward_shim.py +10 -0
- praisonai_code/cli/_paths.py +88 -0
- praisonai_code/cli/_warnings.py +58 -0
- praisonai_code/cli/app.py +757 -0
- praisonai_code/cli/approval_backend.py +272 -0
- praisonai_code/cli/branding.py +94 -0
- praisonai_code/cli/commands/__init__.py +114 -0
- praisonai_code/cli/commands/acp.py +80 -0
- praisonai_code/cli/commands/agent.py +116 -0
- praisonai_code/cli/commands/agents.py +80 -0
- praisonai_code/cli/commands/app.py +139 -0
- praisonai_code/cli/commands/attach.py +95 -0
- praisonai_code/cli/commands/audit.py +102 -0
- praisonai_code/cli/commands/auth.py +508 -0
- praisonai_code/cli/commands/batch.py +848 -0
- praisonai_code/cli/commands/benchmark.py +286 -0
- praisonai_code/cli/commands/browser.py +299 -0
- praisonai_code/cli/commands/call.py +45 -0
- praisonai_code/cli/commands/chat.py +332 -0
- praisonai_code/cli/commands/checkpoint.py +170 -0
- praisonai_code/cli/commands/code.py +276 -0
- praisonai_code/cli/commands/command.py +114 -0
- praisonai_code/cli/commands/commit.py +47 -0
- praisonai_code/cli/commands/completion.py +333 -0
- praisonai_code/cli/commands/config.py +681 -0
- praisonai_code/cli/commands/context.py +414 -0
- praisonai_code/cli/commands/daemon.py +203 -0
- praisonai_code/cli/commands/debug.py +142 -0
- praisonai_code/cli/commands/deploy.py +71 -0
- praisonai_code/cli/commands/diag.py +55 -0
- praisonai_code/cli/commands/docs.py +1575 -0
- praisonai_code/cli/commands/doctor.py +332 -0
- praisonai_code/cli/commands/endpoints.py +51 -0
- praisonai_code/cli/commands/environment.py +179 -0
- praisonai_code/cli/commands/eval.py +131 -0
- praisonai_code/cli/commands/examples.py +953 -0
- praisonai_code/cli/commands/flow.py +436 -0
- praisonai_code/cli/commands/github.py +752 -0
- praisonai_code/cli/commands/hooks.py +74 -0
- praisonai_code/cli/commands/init.py +174 -0
- praisonai_code/cli/commands/knowledge.py +440 -0
- praisonai_code/cli/commands/langextract.py +120 -0
- praisonai_code/cli/commands/langfuse.py +984 -0
- praisonai_code/cli/commands/loop.py +211 -0
- praisonai_code/cli/commands/lsp.py +112 -0
- praisonai_code/cli/commands/managed.py +659 -0
- praisonai_code/cli/commands/mcp.py +763 -0
- praisonai_code/cli/commands/memory.py +298 -0
- praisonai_code/cli/commands/models.py +264 -0
- praisonai_code/cli/commands/n8n.py +326 -0
- praisonai_code/cli/commands/obs.py +19 -0
- praisonai_code/cli/commands/package.py +76 -0
- praisonai_code/cli/commands/paths.py +106 -0
- praisonai_code/cli/commands/permissions.py +272 -0
- praisonai_code/cli/commands/plugins.py +609 -0
- praisonai_code/cli/commands/port.py +530 -0
- praisonai_code/cli/commands/profile.py +466 -0
- praisonai_code/cli/commands/publish.py +193 -0
- praisonai_code/cli/commands/rag.py +913 -0
- praisonai_code/cli/commands/realtime.py +52 -0
- praisonai_code/cli/commands/recipe.py +684 -0
- praisonai_code/cli/commands/registry.py +59 -0
- praisonai_code/cli/commands/replay.py +830 -0
- praisonai_code/cli/commands/research.py +49 -0
- praisonai_code/cli/commands/retrieval.py +377 -0
- praisonai_code/cli/commands/rules.py +71 -0
- praisonai_code/cli/commands/run.py +1573 -0
- praisonai_code/cli/commands/sandbox.py +371 -0
- praisonai_code/cli/commands/schedule.py +529 -0
- praisonai_code/cli/commands/serve.py +690 -0
- praisonai_code/cli/commands/session.py +450 -0
- praisonai_code/cli/commands/setup.py +174 -0
- praisonai_code/cli/commands/skills.py +545 -0
- praisonai_code/cli/commands/standardise.py +711 -0
- praisonai_code/cli/commands/templates.py +54 -0
- praisonai_code/cli/commands/test.py +558 -0
- praisonai_code/cli/commands/todo.py +74 -0
- praisonai_code/cli/commands/tools.py +205 -0
- praisonai_code/cli/commands/traces.py +145 -0
- praisonai_code/cli/commands/tracker.py +852 -0
- praisonai_code/cli/commands/train.py +613 -0
- praisonai_code/cli/commands/ui.py +172 -0
- praisonai_code/cli/commands/up.py +354 -0
- praisonai_code/cli/commands/validate.py +291 -0
- praisonai_code/cli/commands/version.py +101 -0
- praisonai_code/cli/commands/workflow.py +97 -0
- praisonai_code/cli/config_loader.py +437 -0
- praisonai_code/cli/configuration/__init__.py +27 -0
- praisonai_code/cli/configuration/config.schema.json +57 -0
- praisonai_code/cli/configuration/credentials.py +446 -0
- praisonai_code/cli/configuration/loader.py +364 -0
- praisonai_code/cli/configuration/model_resolver.py +161 -0
- praisonai_code/cli/configuration/oauth.py +389 -0
- praisonai_code/cli/configuration/paths.py +224 -0
- praisonai_code/cli/configuration/resolver.py +687 -0
- praisonai_code/cli/configuration/schema.py +317 -0
- praisonai_code/cli/execution/__init__.py +99 -0
- praisonai_code/cli/execution/core.py +208 -0
- praisonai_code/cli/execution/profiler.py +898 -0
- praisonai_code/cli/execution/request.py +85 -0
- praisonai_code/cli/execution/result.py +74 -0
- praisonai_code/cli/fallback_schema.py +416 -0
- praisonai_code/cli/features/__init__.py +278 -0
- praisonai_code/cli/features/_endpoint_registry.py +64 -0
- praisonai_code/cli/features/_search_registry.py +43 -0
- praisonai_code/cli/features/acp.py +236 -0
- praisonai_code/cli/features/action_orchestrator.py +576 -0
- praisonai_code/cli/features/agent_scheduler.py +773 -0
- praisonai_code/cli/features/agent_tools.py +603 -0
- praisonai_code/cli/features/agents.py +397 -0
- praisonai_code/cli/features/at_mentions.py +471 -0
- praisonai_code/cli/features/audit_cli.py +270 -0
- praisonai_code/cli/features/auto_memory.py +182 -0
- praisonai_code/cli/features/auto_mode.py +552 -0
- praisonai_code/cli/features/autonomy_mode.py +546 -0
- praisonai_code/cli/features/background.py +356 -0
- praisonai_code/cli/features/base.py +168 -0
- praisonai_code/cli/features/benchmark.py +1462 -0
- praisonai_code/cli/features/capabilities.py +1326 -0
- praisonai_code/cli/features/checkpoints.py +345 -0
- praisonai_code/cli/features/cli_profiler.py +335 -0
- praisonai_code/cli/features/code_intelligence.py +666 -0
- praisonai_code/cli/features/compaction.py +294 -0
- praisonai_code/cli/features/compare.py +534 -0
- praisonai_code/cli/features/config_hierarchy.py +366 -0
- praisonai_code/cli/features/context_manager.py +597 -0
- praisonai_code/cli/features/cost_tracker.py +514 -0
- praisonai_code/cli/features/csv_test_runner.py +736 -0
- praisonai_code/cli/features/custom_definitions.py +790 -0
- praisonai_code/cli/features/debug.py +810 -0
- praisonai_code/cli/features/deploy.py +605 -0
- praisonai_code/cli/features/diag.py +289 -0
- praisonai_code/cli/features/display_jsonl.py +173 -0
- praisonai_code/cli/features/doctor/__init__.py +63 -0
- praisonai_code/cli/features/doctor/checks/__init__.py +29 -0
- praisonai_code/cli/features/doctor/checks/acp_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/bot_checks.py +340 -0
- praisonai_code/cli/features/doctor/checks/config_checks.py +373 -0
- praisonai_code/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai_code/cli/features/doctor/checks/env_checks.py +637 -0
- praisonai_code/cli/features/doctor/checks/gateway_checks.py +387 -0
- praisonai_code/cli/features/doctor/checks/lsp_checks.py +231 -0
- praisonai_code/cli/features/doctor/checks/mcp_checks.py +367 -0
- praisonai_code/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai_code/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai_code/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai_code/cli/features/doctor/checks/packaging_checks.py +422 -0
- praisonai_code/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai_code/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai_code/cli/features/doctor/checks/runtime_checks.py +650 -0
- praisonai_code/cli/features/doctor/checks/runtime_migration_checks.py +220 -0
- praisonai_code/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai_code/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai_code/cli/features/doctor/checks/skills_checks.py +327 -0
- praisonai_code/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai_code/cli/features/doctor/engine.py +266 -0
- praisonai_code/cli/features/doctor/formatters.py +377 -0
- praisonai_code/cli/features/doctor/handler.py +564 -0
- praisonai_code/cli/features/doctor/models.py +276 -0
- praisonai_code/cli/features/doctor/registry.py +239 -0
- praisonai_code/cli/features/endpoints.py +1016 -0
- praisonai_code/cli/features/eval.py +559 -0
- praisonai_code/cli/features/examples.py +707 -0
- praisonai_code/cli/features/external_agents.py +231 -0
- praisonai_code/cli/features/fast_context.py +410 -0
- praisonai_code/cli/features/file_history.py +320 -0
- praisonai_code/cli/features/flow_display.py +566 -0
- praisonai_code/cli/features/git_attribution.py +159 -0
- praisonai_code/cli/features/git_integration.py +651 -0
- praisonai_code/cli/features/guardrail.py +171 -0
- praisonai_code/cli/features/handoff.py +252 -0
- praisonai_code/cli/features/hooks.py +583 -0
- praisonai_code/cli/features/hybrid_workflow.py +391 -0
- praisonai_code/cli/features/image.py +384 -0
- praisonai_code/cli/features/interactive_core_headless.py +450 -0
- praisonai_code/cli/features/interactive_runtime.py +600 -0
- praisonai_code/cli/features/interactive_test_harness.py +537 -0
- praisonai_code/cli/features/interactive_tools.py +428 -0
- praisonai_code/cli/features/interactive_tui.py +603 -0
- praisonai_code/cli/features/job_workflow.py +906 -0
- praisonai_code/cli/features/jobs.py +632 -0
- praisonai_code/cli/features/knowledge.py +531 -0
- praisonai_code/cli/features/knowledge_cli.py +438 -0
- praisonai_code/cli/features/lite.py +244 -0
- praisonai_code/cli/features/logs.py +200 -0
- praisonai_code/cli/features/lsp_cli.py +225 -0
- praisonai_code/cli/features/lsp_diagnostics.py +185 -0
- praisonai_code/cli/features/mcp.py +344 -0
- praisonai_code/cli/features/message_queue.py +587 -0
- praisonai_code/cli/features/metrics.py +210 -0
- praisonai_code/cli/features/migrate.py +1329 -0
- praisonai_code/cli/features/migration_flow.py +463 -0
- praisonai_code/cli/features/migration_spec.py +276 -0
- praisonai_code/cli/features/n8n.py +703 -0
- praisonai_code/cli/features/observability.py +293 -0
- praisonai_code/cli/features/ollama.py +361 -0
- praisonai_code/cli/features/output_modes.py +155 -0
- praisonai_code/cli/features/output_style.py +273 -0
- praisonai_code/cli/features/package.py +631 -0
- praisonai_code/cli/features/performance.py +308 -0
- praisonai_code/cli/features/persistence.py +636 -0
- praisonai_code/cli/features/profiler/__init__.py +81 -0
- praisonai_code/cli/features/profiler/core.py +558 -0
- praisonai_code/cli/features/profiler/optimizations.py +652 -0
- praisonai_code/cli/features/profiler/suite.py +386 -0
- praisonai_code/cli/features/queue/__init__.py +73 -0
- praisonai_code/cli/features/queue/manager.py +435 -0
- praisonai_code/cli/features/queue/models.py +289 -0
- praisonai_code/cli/features/queue/persistence.py +564 -0
- praisonai_code/cli/features/queue/scheduler.py +529 -0
- praisonai_code/cli/features/queue/worker.py +400 -0
- praisonai_code/cli/features/recipe.py +2187 -0
- praisonai_code/cli/features/recipe_creator.py +996 -0
- praisonai_code/cli/features/recipe_optimizer.py +1364 -0
- praisonai_code/cli/features/recipe_prompts.py +226 -0
- praisonai_code/cli/features/registry.py +229 -0
- praisonai_code/cli/features/repo_map.py +860 -0
- praisonai_code/cli/features/router.py +466 -0
- praisonai_code/cli/features/safe_shell.py +427 -0
- praisonai_code/cli/features/sandbox_cli.py +283 -0
- praisonai_code/cli/features/sandbox_executor.py +536 -0
- praisonai_code/cli/features/sdk_knowledge.py +500 -0
- praisonai_code/cli/features/session.py +222 -0
- praisonai_code/cli/features/session_checkpoints.py +208 -0
- praisonai_code/cli/features/setup/__init__.py +9 -0
- praisonai_code/cli/features/setup/handler.py +355 -0
- praisonai_code/cli/features/setup/templates.py +62 -0
- praisonai_code/cli/features/skills.py +940 -0
- praisonai_code/cli/features/slash_commands.py +692 -0
- praisonai_code/cli/features/telemetry.py +179 -0
- praisonai_code/cli/features/templates.py +1390 -0
- praisonai_code/cli/features/thinking.py +343 -0
- praisonai_code/cli/features/todo.py +334 -0
- praisonai_code/cli/features/tools.py +680 -0
- praisonai_code/cli/features/tui/__init__.py +83 -0
- praisonai_code/cli/features/tui/app.py +871 -0
- praisonai_code/cli/features/tui/cli.py +580 -0
- praisonai_code/cli/features/tui/config.py +150 -0
- praisonai_code/cli/features/tui/debug.py +526 -0
- praisonai_code/cli/features/tui/events.py +99 -0
- praisonai_code/cli/features/tui/mock_provider.py +328 -0
- praisonai_code/cli/features/tui/orchestrator.py +652 -0
- praisonai_code/cli/features/tui/screens/__init__.py +50 -0
- praisonai_code/cli/features/tui/screens/help.py +157 -0
- praisonai_code/cli/features/tui/screens/main.py +568 -0
- praisonai_code/cli/features/tui/screens/queue.py +174 -0
- praisonai_code/cli/features/tui/screens/session.py +124 -0
- praisonai_code/cli/features/tui/screens/settings.py +148 -0
- praisonai_code/cli/features/tui/session_store.py +198 -0
- praisonai_code/cli/features/tui/widgets/__init__.py +56 -0
- praisonai_code/cli/features/tui/widgets/chat.py +263 -0
- praisonai_code/cli/features/tui/widgets/command_popup.py +258 -0
- praisonai_code/cli/features/tui/widgets/composer.py +292 -0
- praisonai_code/cli/features/tui/widgets/file_popup.py +207 -0
- praisonai_code/cli/features/tui/widgets/queue_panel.py +223 -0
- praisonai_code/cli/features/tui/widgets/status.py +181 -0
- praisonai_code/cli/features/tui/widgets/tool_panel.py +307 -0
- praisonai_code/cli/features/wizard.py +289 -0
- praisonai_code/cli/features/workflow.py +802 -0
- praisonai_code/cli/features/yaml_utils.py +321 -0
- praisonai_code/cli/interactive/__init__.py +48 -0
- praisonai_code/cli/interactive/async_tui.py +1218 -0
- praisonai_code/cli/interactive/config.py +139 -0
- praisonai_code/cli/interactive/core.py +618 -0
- praisonai_code/cli/interactive/events.py +131 -0
- praisonai_code/cli/interactive/frontends/__init__.py +31 -0
- praisonai_code/cli/interactive/frontends/rich_frontend.py +462 -0
- praisonai_code/cli/interactive/frontends/textual_frontend.py +157 -0
- praisonai_code/cli/interactive/praison_io.py +502 -0
- praisonai_code/cli/interactive/repl.py +297 -0
- praisonai_code/cli/interactive/split_tui.py +456 -0
- praisonai_code/cli/interactive/tui_app.py +457 -0
- praisonai_code/cli/langfuse_client.py +360 -0
- praisonai_code/cli/main.py +7421 -0
- praisonai_code/cli/output/__init__.py +25 -0
- praisonai_code/cli/output/console.py +456 -0
- praisonai_code/cli/output/event_bridge.py +191 -0
- praisonai_code/cli/schedule_cli.py +54 -0
- praisonai_code/cli/schema_provider.py +23 -0
- praisonai_code/cli/session/__init__.py +16 -0
- praisonai_code/cli/session/resume.py +148 -0
- praisonai_code/cli/session/unified.py +548 -0
- praisonai_code/cli/state/__init__.py +31 -0
- praisonai_code/cli/state/identifiers.py +161 -0
- praisonai_code/cli/state/project_sessions.py +383 -0
- praisonai_code/cli/state/sessions.py +390 -0
- praisonai_code/cli/ui/__init__.py +160 -0
- praisonai_code/cli/ui/config.py +46 -0
- praisonai_code/cli/ui/events.py +61 -0
- praisonai_code/cli/ui/mg_backend.py +342 -0
- praisonai_code/cli/ui/plain.py +133 -0
- praisonai_code/cli/ui/rich_backend.py +162 -0
- praisonai_code/cli/unified_schema.py +655 -0
- praisonai_code/cli/utils/env_utils.py +126 -0
- praisonai_code/cli/utils/project.py +131 -0
- praisonai_code/cli_backends/__init__.py +73 -0
- praisonai_code/cli_backends/claude.py +373 -0
- praisonai_code/cli_backends/registry.py +113 -0
- praisonai_code/runtime/__init__.py +36 -0
- praisonai_code/runtime/__main__.py +81 -0
- praisonai_code/runtime/client.py +131 -0
- praisonai_code/runtime/descriptor.py +209 -0
- praisonai_code/runtime/server.py +356 -0
- praisonai_code-0.0.1.dist-info/METADATA +80 -0
- praisonai_code-0.0.1.dist-info/RECORD +309 -0
- praisonai_code-0.0.1.dist-info/WHEEL +5 -0
- praisonai_code-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Runtime compatibility checks for multi-agent setups.
|
|
3
|
+
|
|
4
|
+
Provides preflight validation for team YAML configurations to detect
|
|
5
|
+
runtime conflicts, missing capabilities, and handoff compatibility issues.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import yaml
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any, Dict, List, Optional, Set, Iterator
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from ..models import CheckResult, CheckStatus, CheckCategory, CheckSeverity
|
|
14
|
+
from ..registry import CheckRegistry
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class RuntimeCapability:
|
|
19
|
+
"""Represents a runtime capability requirement."""
|
|
20
|
+
name: str
|
|
21
|
+
description: str
|
|
22
|
+
required: bool = True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class RuntimeInfo:
|
|
27
|
+
"""Information about a specific runtime."""
|
|
28
|
+
id: str
|
|
29
|
+
name: str
|
|
30
|
+
available: bool
|
|
31
|
+
capabilities: List[RuntimeCapability]
|
|
32
|
+
supports_handoff: bool = False
|
|
33
|
+
supports_tool_loop: bool = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class AgentRuntimeConfig:
|
|
38
|
+
"""Runtime configuration for a specific agent."""
|
|
39
|
+
role_name: str
|
|
40
|
+
runtime_id: Optional[str]
|
|
41
|
+
resolved_runtime: Optional[str]
|
|
42
|
+
capabilities_needed: Set[str]
|
|
43
|
+
handoff_targets: List[str]
|
|
44
|
+
has_tools: bool
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class RuntimeCompatibilityChecker:
|
|
48
|
+
"""Checks runtime compatibility for multi-agent setups."""
|
|
49
|
+
|
|
50
|
+
def __init__(self):
|
|
51
|
+
self.known_runtimes = self._get_known_runtimes()
|
|
52
|
+
|
|
53
|
+
def _get_known_runtimes(self) -> Dict[str, RuntimeInfo]:
|
|
54
|
+
"""Get information about known runtimes."""
|
|
55
|
+
runtimes = {}
|
|
56
|
+
|
|
57
|
+
# Check framework availability using the same pattern as agents_generator
|
|
58
|
+
try:
|
|
59
|
+
from praisonai._framework_availability import is_available
|
|
60
|
+
except ImportError:
|
|
61
|
+
is_available = lambda x: False
|
|
62
|
+
|
|
63
|
+
# Adapter-aware availability: a runtime is only "usable" if its adapter
|
|
64
|
+
# is both installed AND implemented/registered. This keeps `doctor` in
|
|
65
|
+
# sync with the registry-backed `--framework` choices so it never
|
|
66
|
+
# reports a runtime as available when the matching framework cannot run
|
|
67
|
+
# (e.g. unimplemented AutoGen v0.4 / AG2 entry-point placeholders).
|
|
68
|
+
def _runtime_usable(framework_name: str, package_name: str) -> bool:
|
|
69
|
+
try:
|
|
70
|
+
from praisonai.framework_adapters.registry import get_default_registry
|
|
71
|
+
except ImportError:
|
|
72
|
+
# Fallback to raw package probe only when the registry module
|
|
73
|
+
# itself cannot be imported (e.g. partial install).
|
|
74
|
+
return is_available(package_name)
|
|
75
|
+
|
|
76
|
+
registry = get_default_registry()
|
|
77
|
+
# When the registry is the source of truth, an unregistered
|
|
78
|
+
# framework is not runnable even if its packages are installed,
|
|
79
|
+
# so doctor stays consistent with the `--framework` choices.
|
|
80
|
+
if framework_name not in registry.list_names():
|
|
81
|
+
return False
|
|
82
|
+
return registry.is_available(framework_name)
|
|
83
|
+
|
|
84
|
+
# PraisonAI Agents runtime
|
|
85
|
+
runtimes['praisonai'] = RuntimeInfo(
|
|
86
|
+
id='praisonai',
|
|
87
|
+
name='PraisonAI Agents',
|
|
88
|
+
available=is_available('praisonaiagents'),
|
|
89
|
+
capabilities=[
|
|
90
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
91
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
92
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs'),
|
|
93
|
+
RuntimeCapability('context_sharing', 'Share conversation context'),
|
|
94
|
+
RuntimeCapability('cli_backend', 'CLI backend integration'),
|
|
95
|
+
],
|
|
96
|
+
supports_handoff=True,
|
|
97
|
+
supports_tool_loop=True
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# CrewAI runtime
|
|
101
|
+
runtimes['crewai'] = RuntimeInfo(
|
|
102
|
+
id='crewai',
|
|
103
|
+
name='CrewAI',
|
|
104
|
+
available=is_available('crewai'),
|
|
105
|
+
capabilities=[
|
|
106
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
107
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
108
|
+
RuntimeCapability('sequential_execution', 'Sequential task execution'),
|
|
109
|
+
RuntimeCapability('hierarchical_execution', 'Hierarchical task execution'),
|
|
110
|
+
],
|
|
111
|
+
supports_handoff=False,
|
|
112
|
+
supports_tool_loop=True
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# AutoGen v0.2 runtime
|
|
116
|
+
runtimes['autogen'] = RuntimeInfo(
|
|
117
|
+
id='autogen',
|
|
118
|
+
name='AutoGen v0.2',
|
|
119
|
+
available=is_available('autogen'),
|
|
120
|
+
capabilities=[
|
|
121
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
122
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
123
|
+
RuntimeCapability('group_chat', 'Multi-agent conversations'),
|
|
124
|
+
],
|
|
125
|
+
supports_handoff=False,
|
|
126
|
+
supports_tool_loop=True
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
# AutoGen v0.4 runtime (optional entry-point adapter)
|
|
130
|
+
runtimes['autogen_v4'] = RuntimeInfo(
|
|
131
|
+
id='autogen_v4',
|
|
132
|
+
name='AutoGen v0.4',
|
|
133
|
+
available=_runtime_usable('autogen_v4', 'autogen_v4'),
|
|
134
|
+
capabilities=[
|
|
135
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
136
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
137
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs', required=False),
|
|
138
|
+
],
|
|
139
|
+
supports_handoff=True,
|
|
140
|
+
supports_tool_loop=True
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
# AG2 runtime (optional entry-point adapter)
|
|
144
|
+
runtimes['ag2'] = RuntimeInfo(
|
|
145
|
+
id='ag2',
|
|
146
|
+
name='AG2 (AutoGen Next)',
|
|
147
|
+
available=_runtime_usable('ag2', 'ag2'),
|
|
148
|
+
capabilities=[
|
|
149
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
150
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
151
|
+
],
|
|
152
|
+
supports_handoff=False,
|
|
153
|
+
supports_tool_loop=True
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
runtimes['langgraph'] = RuntimeInfo(
|
|
157
|
+
id='langgraph',
|
|
158
|
+
name='LangGraph',
|
|
159
|
+
available=_runtime_usable('langgraph', 'langgraph'),
|
|
160
|
+
capabilities=[
|
|
161
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
162
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
163
|
+
RuntimeCapability('sequential_execution', 'Sequential task execution'),
|
|
164
|
+
],
|
|
165
|
+
supports_handoff=False,
|
|
166
|
+
supports_tool_loop=True
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
runtimes['openai_agents'] = RuntimeInfo(
|
|
170
|
+
id='openai_agents',
|
|
171
|
+
name='OpenAI Agents SDK',
|
|
172
|
+
available=_runtime_usable('openai_agents', 'openai_agents'),
|
|
173
|
+
capabilities=[
|
|
174
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
175
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
176
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs', required=False),
|
|
177
|
+
],
|
|
178
|
+
supports_handoff=True,
|
|
179
|
+
supports_tool_loop=True
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
runtimes['agno'] = RuntimeInfo(
|
|
183
|
+
id='agno',
|
|
184
|
+
name='Agno',
|
|
185
|
+
available=_runtime_usable('agno', 'agno'),
|
|
186
|
+
capabilities=[
|
|
187
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
188
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
189
|
+
RuntimeCapability('sequential_execution', 'Sequential task execution'),
|
|
190
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs via Team route', required=False),
|
|
191
|
+
],
|
|
192
|
+
supports_handoff=True,
|
|
193
|
+
supports_tool_loop=True
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
runtimes['google_adk'] = RuntimeInfo(
|
|
197
|
+
id='google_adk',
|
|
198
|
+
name='Google ADK',
|
|
199
|
+
available=_runtime_usable('google_adk', 'google_adk'),
|
|
200
|
+
capabilities=[
|
|
201
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
202
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
203
|
+
RuntimeCapability('sequential_execution', 'Sequential task execution'),
|
|
204
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs via sub_agents', required=False),
|
|
205
|
+
],
|
|
206
|
+
supports_handoff=True,
|
|
207
|
+
supports_tool_loop=True
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
runtimes['pydantic_ai'] = RuntimeInfo(
|
|
211
|
+
id='pydantic_ai',
|
|
212
|
+
name='Pydantic AI',
|
|
213
|
+
available=_runtime_usable('pydantic_ai', 'pydantic_ai'),
|
|
214
|
+
capabilities=[
|
|
215
|
+
RuntimeCapability('agent_creation', 'Create and manage agents'),
|
|
216
|
+
RuntimeCapability('tool_execution', 'Execute tools and functions'),
|
|
217
|
+
RuntimeCapability('sequential_execution', 'Sequential task execution'),
|
|
218
|
+
RuntimeCapability('handoff_support', 'Agent-to-agent handoffs via delegation tools', required=False),
|
|
219
|
+
],
|
|
220
|
+
supports_handoff=True,
|
|
221
|
+
supports_tool_loop=True
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return runtimes
|
|
225
|
+
|
|
226
|
+
def _check_autogen_v4(self) -> bool:
|
|
227
|
+
"""Check if AutoGen v0.4+ packages are installed."""
|
|
228
|
+
try:
|
|
229
|
+
from praisonai._framework_availability import is_available as fw_available
|
|
230
|
+
return fw_available('autogen_v4')
|
|
231
|
+
except ImportError:
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
def _parse_yaml_config(self, yaml_path: str) -> Dict[str, Any]:
|
|
235
|
+
"""Parse team YAML configuration."""
|
|
236
|
+
try:
|
|
237
|
+
with open(yaml_path, 'r') as f:
|
|
238
|
+
return yaml.safe_load(f)
|
|
239
|
+
except Exception as e:
|
|
240
|
+
raise ValueError(f"Failed to parse YAML file: {e}")
|
|
241
|
+
|
|
242
|
+
def _extract_agent_configs(self, config: Dict[str, Any]) -> List[AgentRuntimeConfig]:
|
|
243
|
+
"""Extract agent runtime configurations from YAML."""
|
|
244
|
+
agents = []
|
|
245
|
+
|
|
246
|
+
# Handle both 'roles' (canonical) and 'agents' (legacy) sections
|
|
247
|
+
entities = {}
|
|
248
|
+
entities.update(config.get('roles', {}))
|
|
249
|
+
entities.update(config.get('agents', {}))
|
|
250
|
+
|
|
251
|
+
for role_name, role_config in entities.items():
|
|
252
|
+
if not isinstance(role_config, dict):
|
|
253
|
+
continue
|
|
254
|
+
|
|
255
|
+
# Extract runtime information
|
|
256
|
+
runtime_id = role_config.get('runtime')
|
|
257
|
+
capabilities_needed = set()
|
|
258
|
+
handoff_targets = []
|
|
259
|
+
has_tools = bool(role_config.get('tools', []))
|
|
260
|
+
|
|
261
|
+
# Check for handoff configuration
|
|
262
|
+
handoff_config = role_config.get('handoff', {})
|
|
263
|
+
if handoff_config:
|
|
264
|
+
if isinstance(handoff_config, dict):
|
|
265
|
+
raw_targets = handoff_config.get('to', [])
|
|
266
|
+
if isinstance(raw_targets, str):
|
|
267
|
+
handoff_targets = [raw_targets]
|
|
268
|
+
elif isinstance(raw_targets, list):
|
|
269
|
+
handoff_targets = raw_targets
|
|
270
|
+
else:
|
|
271
|
+
handoff_targets = []
|
|
272
|
+
elif isinstance(handoff_config, str):
|
|
273
|
+
handoff_targets = [handoff_config]
|
|
274
|
+
elif isinstance(handoff_config, list):
|
|
275
|
+
handoff_targets = handoff_config
|
|
276
|
+
|
|
277
|
+
if handoff_targets:
|
|
278
|
+
capabilities_needed.add('handoff_support')
|
|
279
|
+
|
|
280
|
+
# Check for CLI backend usage
|
|
281
|
+
if role_config.get('cli_backend'):
|
|
282
|
+
capabilities_needed.add('cli_backend')
|
|
283
|
+
|
|
284
|
+
# Check for tool requirements
|
|
285
|
+
if has_tools:
|
|
286
|
+
capabilities_needed.add('tool_execution')
|
|
287
|
+
|
|
288
|
+
# Resolve runtime using framework selection logic
|
|
289
|
+
resolved_runtime = self._resolve_runtime_for_agent(
|
|
290
|
+
config, runtime_id, role_config
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
agents.append(AgentRuntimeConfig(
|
|
294
|
+
role_name=role_name,
|
|
295
|
+
runtime_id=runtime_id,
|
|
296
|
+
resolved_runtime=resolved_runtime,
|
|
297
|
+
capabilities_needed=capabilities_needed,
|
|
298
|
+
handoff_targets=handoff_targets,
|
|
299
|
+
has_tools=has_tools
|
|
300
|
+
))
|
|
301
|
+
|
|
302
|
+
return agents
|
|
303
|
+
|
|
304
|
+
def _resolve_runtime_for_agent(self, config: Dict[str, Any],
|
|
305
|
+
agent_runtime: Optional[str],
|
|
306
|
+
agent_config: Dict[str, Any]) -> str:
|
|
307
|
+
"""Resolve the actual runtime that will be used for an agent."""
|
|
308
|
+
# Agent-level runtime takes priority
|
|
309
|
+
if agent_runtime:
|
|
310
|
+
return agent_runtime
|
|
311
|
+
|
|
312
|
+
# Fall back to global framework
|
|
313
|
+
framework = config.get('framework', 'praisonai')
|
|
314
|
+
|
|
315
|
+
# Handle AutoGen version selection logic
|
|
316
|
+
if framework == 'autogen':
|
|
317
|
+
autogen_version = str(
|
|
318
|
+
config.get('autogen_version', os.environ.get('AUTOGEN_VERSION', 'auto'))
|
|
319
|
+
).lower()
|
|
320
|
+
|
|
321
|
+
autogen_v4_runtime = self.known_runtimes.get('autogen_v4')
|
|
322
|
+
autogen_runtime = self.known_runtimes.get('autogen')
|
|
323
|
+
|
|
324
|
+
if autogen_version == 'v0.4' and autogen_v4_runtime and autogen_v4_runtime.available:
|
|
325
|
+
return 'autogen_v4'
|
|
326
|
+
elif autogen_version == 'v0.2' and autogen_runtime and autogen_runtime.available:
|
|
327
|
+
return 'autogen'
|
|
328
|
+
elif autogen_version == 'auto':
|
|
329
|
+
if autogen_v4_runtime and autogen_v4_runtime.available:
|
|
330
|
+
return 'autogen_v4'
|
|
331
|
+
elif autogen_runtime and autogen_runtime.available:
|
|
332
|
+
return 'autogen'
|
|
333
|
+
|
|
334
|
+
return framework
|
|
335
|
+
|
|
336
|
+
def validate_team_yaml(self, yaml_path: str) -> Iterator[CheckResult]:
|
|
337
|
+
"""Validate a team YAML file for runtime compatibility."""
|
|
338
|
+
try:
|
|
339
|
+
config = self._parse_yaml_config(yaml_path)
|
|
340
|
+
except Exception as e:
|
|
341
|
+
yield CheckResult(
|
|
342
|
+
id="runtime.yaml_parse",
|
|
343
|
+
title="YAML Parsing",
|
|
344
|
+
category=CheckCategory.CONFIG,
|
|
345
|
+
status=CheckStatus.ERROR,
|
|
346
|
+
message=f"Failed to parse YAML: {e}",
|
|
347
|
+
severity=CheckSeverity.CRITICAL
|
|
348
|
+
)
|
|
349
|
+
return
|
|
350
|
+
|
|
351
|
+
# Extract agent configurations
|
|
352
|
+
try:
|
|
353
|
+
agents = self._extract_agent_configs(config)
|
|
354
|
+
except Exception as e:
|
|
355
|
+
yield CheckResult(
|
|
356
|
+
id="runtime.config_extract",
|
|
357
|
+
title="Configuration Extraction",
|
|
358
|
+
category=CheckCategory.CONFIG,
|
|
359
|
+
status=CheckStatus.ERROR,
|
|
360
|
+
message=f"Failed to extract agent configs: {e}",
|
|
361
|
+
severity=CheckSeverity.HIGH
|
|
362
|
+
)
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
if not agents:
|
|
366
|
+
yield CheckResult(
|
|
367
|
+
id="runtime.no_agents",
|
|
368
|
+
title="Agent Presence",
|
|
369
|
+
category=CheckCategory.CONFIG,
|
|
370
|
+
status=CheckStatus.WARN,
|
|
371
|
+
message="No agents found in configuration",
|
|
372
|
+
severity=CheckSeverity.LOW
|
|
373
|
+
)
|
|
374
|
+
return
|
|
375
|
+
|
|
376
|
+
# Validate each agent's runtime
|
|
377
|
+
yield from self._validate_agent_runtimes(agents)
|
|
378
|
+
|
|
379
|
+
# Validate cross-agent compatibility
|
|
380
|
+
yield from self._validate_handoff_compatibility(agents)
|
|
381
|
+
|
|
382
|
+
# Validate mixed-runtime scenarios
|
|
383
|
+
yield from self._validate_mixed_runtime_setup(agents)
|
|
384
|
+
|
|
385
|
+
def _validate_agent_runtimes(self, agents: List[AgentRuntimeConfig]) -> Iterator[CheckResult]:
|
|
386
|
+
"""Validate individual agent runtime configurations."""
|
|
387
|
+
for agent in agents:
|
|
388
|
+
runtime_info = self.known_runtimes.get(agent.resolved_runtime)
|
|
389
|
+
|
|
390
|
+
if not runtime_info:
|
|
391
|
+
yield CheckResult(
|
|
392
|
+
id=f"runtime.unknown_runtime.{agent.role_name}",
|
|
393
|
+
title=f"Unknown Runtime ({agent.role_name})",
|
|
394
|
+
category=CheckCategory.CONFIG,
|
|
395
|
+
status=CheckStatus.FAIL,
|
|
396
|
+
message=f"Unknown runtime '{agent.resolved_runtime}' for agent '{agent.role_name}'",
|
|
397
|
+
details=f"Known runtimes: {', '.join(self.known_runtimes.keys())}",
|
|
398
|
+
severity=CheckSeverity.HIGH
|
|
399
|
+
)
|
|
400
|
+
continue
|
|
401
|
+
|
|
402
|
+
if not runtime_info.available:
|
|
403
|
+
yield CheckResult(
|
|
404
|
+
id=f"runtime.unavailable.{agent.role_name}",
|
|
405
|
+
title=f"Runtime Unavailable ({agent.role_name})",
|
|
406
|
+
category=CheckCategory.ENVIRONMENT,
|
|
407
|
+
status=CheckStatus.FAIL,
|
|
408
|
+
message=f"Runtime '{runtime_info.name}' is not available for agent '{agent.role_name}'",
|
|
409
|
+
details="Install required packages or check dependencies",
|
|
410
|
+
remediation=f"Install packages for {runtime_info.name}",
|
|
411
|
+
severity=CheckSeverity.HIGH
|
|
412
|
+
)
|
|
413
|
+
continue
|
|
414
|
+
|
|
415
|
+
# Check capability requirements
|
|
416
|
+
runtime_capabilities = {cap.name for cap in runtime_info.capabilities}
|
|
417
|
+
missing_capabilities = agent.capabilities_needed - runtime_capabilities
|
|
418
|
+
|
|
419
|
+
if missing_capabilities:
|
|
420
|
+
yield CheckResult(
|
|
421
|
+
id=f"runtime.missing_capabilities.{agent.role_name}",
|
|
422
|
+
title=f"Missing Capabilities ({agent.role_name})",
|
|
423
|
+
category=CheckCategory.CONFIG,
|
|
424
|
+
status=CheckStatus.FAIL,
|
|
425
|
+
message=f"Runtime '{runtime_info.name}' missing required capabilities: {', '.join(missing_capabilities)}",
|
|
426
|
+
details=f"Agent '{agent.role_name}' requires: {', '.join(agent.capabilities_needed)}",
|
|
427
|
+
remediation="Use a compatible runtime or remove unsupported features",
|
|
428
|
+
severity=CheckSeverity.HIGH
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
def _validate_handoff_compatibility(self, agents: List[AgentRuntimeConfig]) -> Iterator[CheckResult]:
|
|
432
|
+
"""Validate handoff compatibility between agents."""
|
|
433
|
+
agent_map = {agent.role_name: agent for agent in agents}
|
|
434
|
+
|
|
435
|
+
for agent in agents:
|
|
436
|
+
if not agent.handoff_targets:
|
|
437
|
+
continue
|
|
438
|
+
|
|
439
|
+
runtime_info = self.known_runtimes.get(agent.resolved_runtime)
|
|
440
|
+
if not runtime_info or not runtime_info.supports_handoff:
|
|
441
|
+
yield CheckResult(
|
|
442
|
+
id=f"runtime.handoff_unsupported.{agent.role_name}",
|
|
443
|
+
title=f"Handoff Unsupported ({agent.role_name})",
|
|
444
|
+
category=CheckCategory.CONFIG,
|
|
445
|
+
status=CheckStatus.FAIL,
|
|
446
|
+
message=f"Agent '{agent.role_name}' configured for handoffs but runtime '{runtime_info.name if runtime_info else agent.resolved_runtime}' doesn't support handoffs",
|
|
447
|
+
details=f"Handoff targets: {', '.join(agent.handoff_targets)}",
|
|
448
|
+
remediation="Use praisonai, autogen_v4, or openai_agents framework for handoff support",
|
|
449
|
+
severity=CheckSeverity.HIGH
|
|
450
|
+
)
|
|
451
|
+
continue
|
|
452
|
+
|
|
453
|
+
# Check if handoff targets exist and are compatible
|
|
454
|
+
for target in agent.handoff_targets:
|
|
455
|
+
target_agent = agent_map.get(target)
|
|
456
|
+
if not target_agent:
|
|
457
|
+
yield CheckResult(
|
|
458
|
+
id=f"runtime.handoff_target_missing.{agent.role_name}",
|
|
459
|
+
title=f"Handoff Target Missing ({agent.role_name})",
|
|
460
|
+
category=CheckCategory.CONFIG,
|
|
461
|
+
status=CheckStatus.FAIL,
|
|
462
|
+
message=f"Agent '{agent.role_name}' has handoff target '{target}' that doesn't exist",
|
|
463
|
+
severity=CheckSeverity.HIGH
|
|
464
|
+
)
|
|
465
|
+
continue
|
|
466
|
+
|
|
467
|
+
target_runtime_info = self.known_runtimes.get(target_agent.resolved_runtime)
|
|
468
|
+
if target_runtime_info and not target_runtime_info.supports_tool_loop:
|
|
469
|
+
yield CheckResult(
|
|
470
|
+
id=f"runtime.handoff_target_incompatible.{agent.role_name}",
|
|
471
|
+
title=f"Handoff Target Incompatible ({agent.role_name})",
|
|
472
|
+
category=CheckCategory.CONFIG,
|
|
473
|
+
status=CheckStatus.WARN,
|
|
474
|
+
message=f"Handoff target '{target}' uses runtime that may not support tool loops",
|
|
475
|
+
details=f"Target runtime: {target_runtime_info.name}",
|
|
476
|
+
severity=CheckSeverity.MEDIUM
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
def _validate_mixed_runtime_setup(self, agents: List[AgentRuntimeConfig]) -> Iterator[CheckResult]:
|
|
480
|
+
"""Validate mixed-runtime scenario compatibility."""
|
|
481
|
+
runtimes_used = {agent.resolved_runtime for agent in agents}
|
|
482
|
+
|
|
483
|
+
if len(runtimes_used) <= 1:
|
|
484
|
+
return # Single runtime setup, no mixed-runtime issues
|
|
485
|
+
|
|
486
|
+
yield CheckResult(
|
|
487
|
+
id="runtime.mixed_runtimes",
|
|
488
|
+
title="Mixed Runtime Setup",
|
|
489
|
+
category=CheckCategory.CONFIG,
|
|
490
|
+
status=CheckStatus.WARN,
|
|
491
|
+
message=f"Multiple runtimes detected: {', '.join(runtimes_used)}",
|
|
492
|
+
details="Mixed runtimes may have compatibility issues",
|
|
493
|
+
remediation="Consider using a single runtime for consistency",
|
|
494
|
+
severity=CheckSeverity.MEDIUM
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
# Check for specific problematic combinations
|
|
498
|
+
handoff_agents = [a for a in agents if a.handoff_targets]
|
|
499
|
+
if handoff_agents:
|
|
500
|
+
handoff_runtimes = {a.resolved_runtime for a in handoff_agents}
|
|
501
|
+
incompatible_handoff_runtimes = handoff_runtimes - {
|
|
502
|
+
'praisonai',
|
|
503
|
+
'autogen_v4',
|
|
504
|
+
'openai_agents',
|
|
505
|
+
'agno',
|
|
506
|
+
'google_adk',
|
|
507
|
+
'pydantic_ai',
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if incompatible_handoff_runtimes:
|
|
511
|
+
yield CheckResult(
|
|
512
|
+
id="runtime.mixed_handoff_incompatible",
|
|
513
|
+
title="Mixed Runtime Handoff Issues",
|
|
514
|
+
category=CheckCategory.CONFIG,
|
|
515
|
+
status=CheckStatus.FAIL,
|
|
516
|
+
message=f"Handoffs configured with incompatible runtimes: {', '.join(incompatible_handoff_runtimes)}",
|
|
517
|
+
details="Only praisonai, autogen_v4, openai_agents, agno, google_adk, and pydantic_ai runtimes support handoffs",
|
|
518
|
+
remediation="Use praisonai, autogen_v4, openai_agents, agno, google_adk, or pydantic_ai for agents that need handoff capabilities",
|
|
519
|
+
severity=CheckSeverity.HIGH
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def lint_runtime_team(yaml_path: str) -> List[CheckResult]:
|
|
524
|
+
"""
|
|
525
|
+
Public API for programmatic runtime linting.
|
|
526
|
+
|
|
527
|
+
Args:
|
|
528
|
+
yaml_path: Path to the team YAML file
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
List of check results
|
|
532
|
+
"""
|
|
533
|
+
checker = RuntimeCompatibilityChecker()
|
|
534
|
+
return list(checker.validate_team_yaml(yaml_path))
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
# Register the runtime checks with the doctor framework
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def runtime_team_check(config):
|
|
541
|
+
"""Runtime team compatibility check for the doctor framework."""
|
|
542
|
+
team_file = getattr(config, 'team_file', None)
|
|
543
|
+
|
|
544
|
+
if not team_file:
|
|
545
|
+
return CheckResult(
|
|
546
|
+
id="runtime.no_team_file",
|
|
547
|
+
title="No Team File Specified",
|
|
548
|
+
category=CheckCategory.RUNTIME,
|
|
549
|
+
status=CheckStatus.SKIP,
|
|
550
|
+
message="Use --team to specify a team YAML file for runtime validation"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
if not os.path.exists(team_file):
|
|
554
|
+
return CheckResult(
|
|
555
|
+
id="runtime.team_file_not_found",
|
|
556
|
+
title="Team File Not Found",
|
|
557
|
+
category=CheckCategory.RUNTIME,
|
|
558
|
+
status=CheckStatus.FAIL,
|
|
559
|
+
message=f"Team YAML file not found: {team_file}",
|
|
560
|
+
severity=CheckSeverity.HIGH
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
checker = RuntimeCompatibilityChecker()
|
|
564
|
+
results = list(checker.validate_team_yaml(team_file))
|
|
565
|
+
|
|
566
|
+
if not results:
|
|
567
|
+
return CheckResult(
|
|
568
|
+
id="runtime.team_validation_success",
|
|
569
|
+
title="Runtime Team Compatibility",
|
|
570
|
+
category=CheckCategory.RUNTIME,
|
|
571
|
+
status=CheckStatus.PASS,
|
|
572
|
+
message=f"No runtime compatibility issues found in {team_file}"
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
# Aggregate results into a single CheckResult as expected by the doctor engine
|
|
576
|
+
has_failures = any(r.status in (CheckStatus.FAIL, CheckStatus.ERROR) for r in results)
|
|
577
|
+
has_warnings = any(r.status == CheckStatus.WARN for r in results)
|
|
578
|
+
|
|
579
|
+
if has_failures:
|
|
580
|
+
status = CheckStatus.FAIL
|
|
581
|
+
severity = CheckSeverity.HIGH
|
|
582
|
+
elif has_warnings:
|
|
583
|
+
status = CheckStatus.WARN
|
|
584
|
+
severity = CheckSeverity.MEDIUM
|
|
585
|
+
else:
|
|
586
|
+
status = CheckStatus.PASS
|
|
587
|
+
severity = CheckSeverity.LOW
|
|
588
|
+
|
|
589
|
+
return CheckResult(
|
|
590
|
+
id="runtime.team_validation_aggregate",
|
|
591
|
+
title="Runtime Team Compatibility",
|
|
592
|
+
category=CheckCategory.RUNTIME,
|
|
593
|
+
status=status,
|
|
594
|
+
message=f"Found {len(results)} runtime compatibility issue(s) in {team_file}",
|
|
595
|
+
details="\n".join(f"- {r.id}: {r.message}" for r in results) if results else "No issues found",
|
|
596
|
+
severity=severity,
|
|
597
|
+
metadata={"findings": [r.to_dict() for r in results]}
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def workflow_runtime_check(config):
|
|
602
|
+
"""Workflow runtime compatibility check (placeholder for future implementation)."""
|
|
603
|
+
workflow_file = getattr(config, 'workflow_file', None)
|
|
604
|
+
|
|
605
|
+
if not workflow_file:
|
|
606
|
+
return CheckResult(
|
|
607
|
+
id="runtime.workflow_not_implemented",
|
|
608
|
+
title="Workflow Runtime Validation",
|
|
609
|
+
category=CheckCategory.RUNTIME,
|
|
610
|
+
status=CheckStatus.SKIP,
|
|
611
|
+
message="Workflow runtime validation not yet implemented"
|
|
612
|
+
)
|
|
613
|
+
|
|
614
|
+
return CheckResult(
|
|
615
|
+
id="runtime.workflow_placeholder",
|
|
616
|
+
title="Workflow Runtime Validation",
|
|
617
|
+
category=CheckCategory.RUNTIME,
|
|
618
|
+
status=CheckStatus.SKIP,
|
|
619
|
+
message="Workflow runtime validation will be implemented in future versions"
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
# Register checks when module is imported
|
|
624
|
+
try:
|
|
625
|
+
registry = CheckRegistry()
|
|
626
|
+
|
|
627
|
+
# Register team runtime check
|
|
628
|
+
registry.register(
|
|
629
|
+
id='runtime.team',
|
|
630
|
+
title='Runtime Team Compatibility',
|
|
631
|
+
description='Validate runtime compatibility for team YAML configurations',
|
|
632
|
+
category=CheckCategory.RUNTIME,
|
|
633
|
+
implementation=runtime_team_check,
|
|
634
|
+
requires_deep=False
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
# Register workflow runtime check (placeholder)
|
|
638
|
+
registry.register(
|
|
639
|
+
id='runtime.workflow',
|
|
640
|
+
title='Workflow Runtime Compatibility',
|
|
641
|
+
description='Validate runtime compatibility for workflow YAML configurations',
|
|
642
|
+
category=CheckCategory.RUNTIME,
|
|
643
|
+
implementation=workflow_runtime_check,
|
|
644
|
+
requires_deep=False
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
except Exception as e:
|
|
648
|
+
# Log registration failure but don't crash the module
|
|
649
|
+
import logging
|
|
650
|
+
logging.getLogger(__name__).warning(f"Failed to register runtime checks: {e}")
|