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,757 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PraisonAI CLI Typer Application.
|
|
3
|
+
|
|
4
|
+
Main Typer app that registers all command groups and handles global options.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Optional, Dict, Tuple, List
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
import click
|
|
13
|
+
from typer.core import TyperGroup
|
|
14
|
+
from typer.main import get_command as typer_get_command
|
|
15
|
+
|
|
16
|
+
from .output.console import OutputController, OutputMode, set_output_controller
|
|
17
|
+
from .state.identifiers import create_context
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _setup_langfuse_observability(*, verbose: bool = False) -> None:
|
|
21
|
+
"""Set up Langfuse observability by wiring TraceSink to action emitter."""
|
|
22
|
+
try:
|
|
23
|
+
from praisonai.observability.langfuse import LangfuseSink
|
|
24
|
+
from praisonaiagents.trace.protocol import TraceEmitter, set_default_emitter
|
|
25
|
+
from praisonaiagents.trace.context_events import ContextTraceEmitter, set_context_emitter
|
|
26
|
+
import atexit
|
|
27
|
+
|
|
28
|
+
# Create LangfuseSink (auto-reads env vars)
|
|
29
|
+
sink = LangfuseSink()
|
|
30
|
+
|
|
31
|
+
# Set up action-level trace emitter (covers RouterAgent / PlanningAgent)
|
|
32
|
+
emitter = TraceEmitter(sink=sink, enabled=True)
|
|
33
|
+
set_default_emitter(emitter)
|
|
34
|
+
|
|
35
|
+
# Set up context-level trace emitter (captures Agent.start() lifecycle)
|
|
36
|
+
context_emitter = ContextTraceEmitter(sink=sink.context_sink(), enabled=True)
|
|
37
|
+
set_context_emitter(context_emitter)
|
|
38
|
+
|
|
39
|
+
# Register atexit close for the sink
|
|
40
|
+
atexit.register(sink.close)
|
|
41
|
+
|
|
42
|
+
except ImportError:
|
|
43
|
+
# Gracefully degrade if Langfuse not installed
|
|
44
|
+
pass
|
|
45
|
+
except Exception as e:
|
|
46
|
+
# Avoid breaking CLI if observability setup fails
|
|
47
|
+
if verbose:
|
|
48
|
+
typer.echo(f"Warning: failed to initialize Langfuse observability: {e}", err=True)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _setup_langextract_observability(*, verbose: bool = False) -> None:
|
|
52
|
+
"""Set up Langextract observability by wiring TraceSink to action emitter."""
|
|
53
|
+
try:
|
|
54
|
+
import importlib.util
|
|
55
|
+
|
|
56
|
+
# Explicitly check if langextract is available before attempting to use it
|
|
57
|
+
if importlib.util.find_spec('langextract') is None:
|
|
58
|
+
if verbose:
|
|
59
|
+
typer.echo("Warning: langextract is not installed. Install with: pip install 'praisonai[langextract]'", err=True)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
from praisonai.observability.langextract import LangextractSink, LangextractSinkConfig
|
|
63
|
+
from praisonaiagents.trace.protocol import TraceEmitter, set_default_emitter
|
|
64
|
+
import os
|
|
65
|
+
import atexit
|
|
66
|
+
|
|
67
|
+
# Build LangextractSinkConfig from env vars
|
|
68
|
+
config = LangextractSinkConfig(
|
|
69
|
+
output_path=os.getenv("PRAISONAI_LANGEXTRACT_OUTPUT", "praisonai-trace.html"),
|
|
70
|
+
auto_open=os.getenv("PRAISONAI_LANGEXTRACT_AUTO_OPEN", "false").lower() == "true",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Create LangextractSink
|
|
74
|
+
sink = LangextractSink(config=config)
|
|
75
|
+
|
|
76
|
+
# Ensure sink is closed on exit to write the trace file
|
|
77
|
+
atexit.register(sink.close)
|
|
78
|
+
|
|
79
|
+
# Set up action-level trace emitter (covers RouterAgent / PlanningAgent)
|
|
80
|
+
emitter = TraceEmitter(sink=sink, enabled=True)
|
|
81
|
+
set_default_emitter(emitter)
|
|
82
|
+
|
|
83
|
+
# Bridge the context emitter so regular Agent.start / tool calls / LLM
|
|
84
|
+
# responses are captured as well. Without this, typical single-agent
|
|
85
|
+
# flows produce an empty trace (no agent_start/end, no tool events).
|
|
86
|
+
def warn_handler(msg: str):
|
|
87
|
+
if verbose:
|
|
88
|
+
typer.echo(f"Warning: {msg}", err=True)
|
|
89
|
+
|
|
90
|
+
LangextractSink.bridge_context_events(
|
|
91
|
+
sink=sink,
|
|
92
|
+
session_id="praisonai-cli",
|
|
93
|
+
warn_callback=warn_handler
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
except ImportError:
|
|
97
|
+
# Gracefully degrade if langextract not installed
|
|
98
|
+
if verbose:
|
|
99
|
+
typer.echo("Warning: langextract is not installed. Install with: pip install 'praisonai[langextract]'", err=True)
|
|
100
|
+
except Exception as e:
|
|
101
|
+
# Avoid breaking CLI if observability setup fails
|
|
102
|
+
if verbose:
|
|
103
|
+
typer.echo(f"Warning: failed to initialize langextract observability: {e}", err=True)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class OutputFormat(str, Enum):
|
|
107
|
+
"""Output format options."""
|
|
108
|
+
text = "text"
|
|
109
|
+
json = "json"
|
|
110
|
+
stream_json = "stream-json"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# Command registry for lazy loading
|
|
114
|
+
_LAZY_COMMANDS: Dict[str, Tuple[str, str, str]] = {
|
|
115
|
+
# Core commands
|
|
116
|
+
"init": (".commands.init", "app", "Initialise the .praisonai/ project convention"),
|
|
117
|
+
"config": (".commands.config", "app", "Configuration management"),
|
|
118
|
+
"traces": (".commands.traces", "app", "Trace collection management"),
|
|
119
|
+
"env": (".commands.environment", "app", "Environment and diagnostics"),
|
|
120
|
+
"auth": (".commands.auth", "app", "Credential management"),
|
|
121
|
+
"session": (".commands.session", "app", "Session management"),
|
|
122
|
+
"completion": (".commands.completion", "app", "Shell completion scripts"),
|
|
123
|
+
"version": (".commands.version", "app", "Version information"),
|
|
124
|
+
"debug": (".commands.debug", "app", "Debug and test interactive flows"),
|
|
125
|
+
"lsp": (".commands.lsp", "app", "LSP service lifecycle"),
|
|
126
|
+
"diag": (".commands.diag", "app", "Diagnostics export"),
|
|
127
|
+
"doctor": (".commands.doctor", "app", "Health checks and diagnostics"),
|
|
128
|
+
"setup": (".commands.setup", "app", "Interactive onboarding / configuration wizard"),
|
|
129
|
+
"onboard": (".commands.onboard", "app", "Messaging bot onboarding wizard"),
|
|
130
|
+
"obs": (".commands.obs", "app", "Observability diagnostics and management"),
|
|
131
|
+
"validate": (".commands.validate", "app", "Validate YAML configuration files"),
|
|
132
|
+
"acp": (".commands.acp", "app", "Agent Client Protocol server"),
|
|
133
|
+
"mcp": (".commands.mcp", "app", "MCP server management"),
|
|
134
|
+
"serve": (".commands.serve", "app", "API server management"),
|
|
135
|
+
"daemon": (".commands.daemon", "app", "Warm local runtime (keeps MCP/provider clients hot)"),
|
|
136
|
+
"attach": (".commands.attach", "app", "Attach to a live session on the warm runtime"),
|
|
137
|
+
"schedule": (".commands.schedule", "app", "Scheduler management"),
|
|
138
|
+
"kanban": (".commands.kanban", "app", "Kanban task management"),
|
|
139
|
+
"run": (".commands.run", "app", "Run agents"),
|
|
140
|
+
"checkpoint": (".commands.checkpoint", "app", "File-level checkpoint management (save/restore/diff)"),
|
|
141
|
+
"profile": (".commands.profile", "app", "Performance profiling and diagnostics"),
|
|
142
|
+
"benchmark": (".commands.benchmark", "app", "Comprehensive performance benchmarking"),
|
|
143
|
+
"paths": (".commands.paths", "app", "Storage path inspection and migration"),
|
|
144
|
+
|
|
145
|
+
# Terminal-native commands
|
|
146
|
+
"chat": (".commands.chat", "app", "Terminal-native interactive chat (REPL)"),
|
|
147
|
+
"code": (".commands.code", "app", "Terminal-native code assistant"),
|
|
148
|
+
"call": (".commands.call", "app", "Voice/call interaction mode"),
|
|
149
|
+
"realtime": (".commands.realtime", "app", "Realtime interaction mode"),
|
|
150
|
+
"train": (".commands.train", "app", "Model training and fine-tuning"),
|
|
151
|
+
"ui": (".commands.ui", "app", "🤖 Clean Chat UI (praisonaiui)"),
|
|
152
|
+
"context": (".commands.context", "app", "Context management"),
|
|
153
|
+
"research": (".commands.research", "app", "Research and analysis"),
|
|
154
|
+
"memory": (".commands.memory", "app", "Memory management"),
|
|
155
|
+
"workflow": (".commands.workflow", "app", "Workflow management"),
|
|
156
|
+
"tools": (".commands.tools", "app", "Tool management"),
|
|
157
|
+
"n8n": (".commands.n8n", "app", "n8n visual workflow editor integration"),
|
|
158
|
+
"knowledge": (".commands.knowledge", "app", "Knowledge base management (legacy)"),
|
|
159
|
+
"rag": (".commands.rag", "app", "RAG commands (legacy - use index/query instead)"),
|
|
160
|
+
"deploy": (".commands.deploy", "app", "Deployment management"),
|
|
161
|
+
"agents": (".commands.agents", "app", "Agent management"),
|
|
162
|
+
"agent": (".commands.agent", "app", "Custom agent definitions management"),
|
|
163
|
+
"command": (".commands.command", "app", "Custom command definitions management"),
|
|
164
|
+
"skills": (".commands.skills", "app", "Skill management"),
|
|
165
|
+
"eval": (".commands.eval", "app", "Evaluation and testing"),
|
|
166
|
+
"templates": (".commands.templates", "app", "Template management"),
|
|
167
|
+
"recipe": (".commands.recipe", "app", "Recipe management"),
|
|
168
|
+
"todo": (".commands.todo", "app", "Todo/task management"),
|
|
169
|
+
"docs": (".commands.docs", "app", "Documentation management"),
|
|
170
|
+
"commit": (".commands.commit", "app", "AI-assisted git commits"),
|
|
171
|
+
"publish": (".commands.publish", "app", "Package publishing"),
|
|
172
|
+
"hooks": (".commands.hooks", "app", "Hook management"),
|
|
173
|
+
"rules": (".commands.rules", "app", "Rules management"),
|
|
174
|
+
"permissions": (".commands.permissions", "permissions", "Tool approval and permission management"),
|
|
175
|
+
"registry": (".commands.registry", "app", "Registry management"),
|
|
176
|
+
"package": (".commands.package", "app", "Package management"),
|
|
177
|
+
"endpoints": (".commands.endpoints", "app", "API endpoint management"),
|
|
178
|
+
"test": (".commands.test", "app", "Run test suite with tier and provider options"),
|
|
179
|
+
"examples": (".commands.examples", "app", "Run and manage example files"),
|
|
180
|
+
"batch": (".commands.batch", "app", "Run all PraisonAI scripts in current folder"),
|
|
181
|
+
"replay": (".commands.replay", "app", "Context replay for debugging agent execution"),
|
|
182
|
+
"loop": (".commands.loop", "app", "Autonomous agent execution loops"),
|
|
183
|
+
"tracker": (".commands.tracker", "app", "Autonomous agent tracking with step-by-step analysis"),
|
|
184
|
+
"github": (".commands.github", "app", "GitHub native context tracking and Issue triage"),
|
|
185
|
+
"managed": (".commands.managed", "app", "Managed Agents (Anthropic cloud-hosted backend)"),
|
|
186
|
+
"models": (".commands.models", "app", "List and describe available models"),
|
|
187
|
+
|
|
188
|
+
# Moltbot-inspired commands
|
|
189
|
+
"bot": (".commands.bot", "app", "Messaging bots with full agent capabilities"),
|
|
190
|
+
"gateway": (".commands.gateway", "app", "Multi-bot WebSocket gateway server"),
|
|
191
|
+
"pairing": (".commands.pairing", "app", "Manage bot user pairing"),
|
|
192
|
+
"identity": (".commands.identity", "app", "Manage cross-platform user identity links"),
|
|
193
|
+
"browser": (".commands.browser", "app", "Browser control for agent automation"),
|
|
194
|
+
"plugins": (".commands.plugins", "app", "Plugin management and inspection"),
|
|
195
|
+
"sandbox": (".commands.sandbox", "app", "Sandbox container management"),
|
|
196
|
+
"claw": (".commands.claw", "app", "🦞 PraisonAI Dashboard (full UI)"),
|
|
197
|
+
"flow": (".commands.flow", "app", "Visual workflow builder (Langflow)"),
|
|
198
|
+
"dashboard": (".commands.dashboard", "app", "🌟 Unified Dashboard (Flow + Claw + UI)"),
|
|
199
|
+
"langfuse": (".commands.langfuse", "app", "🔍 Langfuse observability platform"),
|
|
200
|
+
"langextract": (".commands.langextract", "app", "🧠 Langextract visual trace layer"),
|
|
201
|
+
"port": (".commands.port", "app", "🔌 Manage port usage and resolve conflicts"),
|
|
202
|
+
"up": (".commands.up", "app", "🚀 Start unified PraisonAI stack (Langfuse + Langflow)"),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
# Special commands that need custom handling
|
|
206
|
+
_SPECIAL_COMMANDS = {
|
|
207
|
+
"tui": (".features.tui.debug", "create_debug_app", "Interactive TUI and simulation"),
|
|
208
|
+
"queue": (".features.tui.cli", "create_queue_app", "Queue management"),
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class LazyCommandGroup(TyperGroup):
|
|
213
|
+
"""Click Group that lazily loads subcommands from registry."""
|
|
214
|
+
|
|
215
|
+
def list_commands(self, ctx: click.Context) -> List[str]:
|
|
216
|
+
"""Return list of available commands without importing them."""
|
|
217
|
+
# Start with commands from parent (already registered commands)
|
|
218
|
+
commands = set(super().list_commands(ctx))
|
|
219
|
+
|
|
220
|
+
# Add lazy-loaded commands
|
|
221
|
+
commands.update(_LAZY_COMMANDS.keys())
|
|
222
|
+
commands.update(_SPECIAL_COMMANDS.keys())
|
|
223
|
+
|
|
224
|
+
# Add special inline commands
|
|
225
|
+
commands.add("app")
|
|
226
|
+
|
|
227
|
+
# Add retrieval commands (these are registered via register_commands)
|
|
228
|
+
commands.update(["index", "query", "search"])
|
|
229
|
+
|
|
230
|
+
# Add standardise/standardize
|
|
231
|
+
commands.update(["standardise", "standardize"])
|
|
232
|
+
|
|
233
|
+
return sorted(list(commands))
|
|
234
|
+
|
|
235
|
+
def get_command(self, ctx: click.Context, name: str) -> Optional[click.Command]:
|
|
236
|
+
"""Lazily import and return the command."""
|
|
237
|
+
# First check if command is already registered (e.g., retrieval commands)
|
|
238
|
+
existing = super().get_command(ctx, name)
|
|
239
|
+
if existing is not None:
|
|
240
|
+
return existing
|
|
241
|
+
|
|
242
|
+
# Check regular lazy commands
|
|
243
|
+
if name in _LAZY_COMMANDS:
|
|
244
|
+
module_path, attr_name, _ = _LAZY_COMMANDS[name]
|
|
245
|
+
try:
|
|
246
|
+
module = importlib.import_module(module_path, __package__)
|
|
247
|
+
sub_app = getattr(module, attr_name)
|
|
248
|
+
if isinstance(sub_app, click.Command):
|
|
249
|
+
return sub_app
|
|
250
|
+
return typer_get_command(sub_app)
|
|
251
|
+
except (ImportError, AttributeError) as e:
|
|
252
|
+
typer.echo(f"Error loading command '{name}': {e}", err=True)
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
# Check special commands
|
|
256
|
+
if name in _SPECIAL_COMMANDS:
|
|
257
|
+
module_path, func_name, _ = _SPECIAL_COMMANDS[name]
|
|
258
|
+
try:
|
|
259
|
+
module = importlib.import_module(module_path, __package__)
|
|
260
|
+
create_func = getattr(module, func_name)
|
|
261
|
+
sub_app = create_func()
|
|
262
|
+
if sub_app:
|
|
263
|
+
if isinstance(sub_app, click.Command):
|
|
264
|
+
return sub_app
|
|
265
|
+
return typer_get_command(sub_app)
|
|
266
|
+
except (ImportError, AttributeError) as e:
|
|
267
|
+
typer.echo(f"Error loading command '{name}': {e}", err=True)
|
|
268
|
+
return None
|
|
269
|
+
|
|
270
|
+
# Handle standardise/standardize
|
|
271
|
+
if name in ["standardise", "standardize"]:
|
|
272
|
+
return self._get_standardise_command()
|
|
273
|
+
|
|
274
|
+
# Handle app command
|
|
275
|
+
if name == "app":
|
|
276
|
+
return self._get_app_command()
|
|
277
|
+
|
|
278
|
+
return None
|
|
279
|
+
|
|
280
|
+
def _get_standardise_command(self) -> Optional[click.Command]:
|
|
281
|
+
"""Get the standardise command group."""
|
|
282
|
+
try:
|
|
283
|
+
standardise_app = typer.Typer()
|
|
284
|
+
|
|
285
|
+
@standardise_app.command("check")
|
|
286
|
+
def standardise_check(
|
|
287
|
+
path: str = typer.Option(".", "--path", "-p", help="Project root path"),
|
|
288
|
+
feature: str = typer.Option(None, "--feature", help="Specific feature slug"),
|
|
289
|
+
scope: str = typer.Option("all", "--scope", help="Scope: all, docs, examples, sdk, cli"),
|
|
290
|
+
ci: bool = typer.Option(False, "--ci", help="CI mode"),
|
|
291
|
+
):
|
|
292
|
+
"""Check for standardisation issues."""
|
|
293
|
+
from .commands.standardise import _run_check
|
|
294
|
+
import argparse
|
|
295
|
+
args = argparse.Namespace(path=path, feature=feature, scope=scope, ci=ci, dry_run=True)
|
|
296
|
+
_run_check(args)
|
|
297
|
+
|
|
298
|
+
@standardise_app.command("report")
|
|
299
|
+
def standardise_report(
|
|
300
|
+
path: str = typer.Option(".", "--path", "-p", help="Project root path"),
|
|
301
|
+
format: str = typer.Option("text", "--format", "-f", help="Format: text, json, markdown"),
|
|
302
|
+
output: str = typer.Option(None, "--output", "-o", help="Output file"),
|
|
303
|
+
ci: bool = typer.Option(False, "--ci", help="CI mode"),
|
|
304
|
+
):
|
|
305
|
+
"""Generate detailed report."""
|
|
306
|
+
from .commands.standardise import _run_report
|
|
307
|
+
import argparse
|
|
308
|
+
args = argparse.Namespace(path=path, format=format, output=output, ci=ci, feature=None, scope="all", dry_run=True)
|
|
309
|
+
_run_report(args)
|
|
310
|
+
|
|
311
|
+
@standardise_app.command("fix")
|
|
312
|
+
def standardise_fix(
|
|
313
|
+
path: str = typer.Option(".", "--path", "-p", help="Project root path"),
|
|
314
|
+
feature: str = typer.Option(None, "--feature", help="Specific feature slug"),
|
|
315
|
+
apply: bool = typer.Option(False, "--apply", help="Actually apply changes"),
|
|
316
|
+
no_backup: bool = typer.Option(False, "--no-backup", help="Don't create backups"),
|
|
317
|
+
):
|
|
318
|
+
"""Fix standardisation issues."""
|
|
319
|
+
from .commands.standardise import _run_fix
|
|
320
|
+
import argparse
|
|
321
|
+
args = argparse.Namespace(path=path, feature=feature, apply=apply, no_backup=no_backup, scope="all", ci=False, dry_run=not apply)
|
|
322
|
+
_run_fix(args)
|
|
323
|
+
|
|
324
|
+
@standardise_app.command("init")
|
|
325
|
+
def standardise_init(
|
|
326
|
+
feature: str = typer.Argument(..., help="Feature slug to initialise"),
|
|
327
|
+
path: str = typer.Option(".", "--path", "-p", help="Project root path"),
|
|
328
|
+
apply: bool = typer.Option(False, "--apply", help="Actually create files"),
|
|
329
|
+
):
|
|
330
|
+
"""Initialise a new feature with all required artifacts."""
|
|
331
|
+
from .commands.standardise import _run_init
|
|
332
|
+
import argparse
|
|
333
|
+
args = argparse.Namespace(feature=feature, path=path, apply=apply, scope="all", ci=False, dry_run=not apply)
|
|
334
|
+
_run_init(args)
|
|
335
|
+
|
|
336
|
+
@standardise_app.command("ai")
|
|
337
|
+
def standardise_ai(
|
|
338
|
+
feature: str = typer.Argument(..., help="Feature slug to generate content for"),
|
|
339
|
+
gen_type: str = typer.Option("all", "--type", "-t", help="Type: docs, examples, all"),
|
|
340
|
+
apply: bool = typer.Option(False, "--apply", help="Actually create files"),
|
|
341
|
+
verify: bool = typer.Option(False, "--verify", help="Verify with AI"),
|
|
342
|
+
model: str = typer.Option("gpt-4o-mini", "--model", help="LLM model"),
|
|
343
|
+
path: str = typer.Option(".", "--path", "-p", help="Project root path"),
|
|
344
|
+
):
|
|
345
|
+
"""AI-powered generation of docs/examples."""
|
|
346
|
+
from .commands.standardise import _run_ai
|
|
347
|
+
import argparse
|
|
348
|
+
args = argparse.Namespace(feature=feature, type=gen_type, apply=apply, verify=verify, model=model, path=path, scope="all", ci=False, dry_run=not apply)
|
|
349
|
+
_run_ai(args)
|
|
350
|
+
|
|
351
|
+
@standardise_app.command("checkpoint")
|
|
352
|
+
def standardise_checkpoint(
|
|
353
|
+
message: str = typer.Option(None, "--message", "-m", help="Checkpoint message"),
|
|
354
|
+
path: str = typer.Option(".", "--path", "-p", help="Repository path"),
|
|
355
|
+
):
|
|
356
|
+
"""Create an undo checkpoint."""
|
|
357
|
+
from .commands.standardise import _run_checkpoint
|
|
358
|
+
import argparse
|
|
359
|
+
args = argparse.Namespace(message=message, path=path)
|
|
360
|
+
_run_checkpoint(args)
|
|
361
|
+
|
|
362
|
+
@standardise_app.command("undo")
|
|
363
|
+
def standardise_undo(
|
|
364
|
+
checkpoint: str = typer.Option(None, "--checkpoint", help="Checkpoint ID"),
|
|
365
|
+
list_checkpoints: bool = typer.Option(False, "--list", help="List checkpoints"),
|
|
366
|
+
path: str = typer.Option(".", "--path", "-p", help="Repository path"),
|
|
367
|
+
):
|
|
368
|
+
"""Undo to a previous checkpoint."""
|
|
369
|
+
from .commands.standardise import _run_undo
|
|
370
|
+
import argparse
|
|
371
|
+
args = argparse.Namespace(checkpoint=checkpoint, list=list_checkpoints, path=path)
|
|
372
|
+
_run_undo(args)
|
|
373
|
+
|
|
374
|
+
@standardise_app.command("redo")
|
|
375
|
+
def standardise_redo(
|
|
376
|
+
path: str = typer.Option(".", "--path", "-p", help="Repository path"),
|
|
377
|
+
):
|
|
378
|
+
"""Redo after an undo."""
|
|
379
|
+
from .commands.standardise import _run_redo
|
|
380
|
+
import argparse
|
|
381
|
+
args = argparse.Namespace(path=path)
|
|
382
|
+
_run_redo(args)
|
|
383
|
+
|
|
384
|
+
return typer_get_command(standardise_app)
|
|
385
|
+
except Exception:
|
|
386
|
+
return None
|
|
387
|
+
|
|
388
|
+
def _get_app_command(self) -> Optional[click.Command]:
|
|
389
|
+
"""Get the app command."""
|
|
390
|
+
# Create a local Typer app to avoid mutating the global app
|
|
391
|
+
app_group = typer.Typer(add_completion=False)
|
|
392
|
+
|
|
393
|
+
@app_group.command(name="app", context_settings={"allow_interspersed_args": False})
|
|
394
|
+
def app_cmd(
|
|
395
|
+
port: int = typer.Option(8000, "--port", "-p", help="Port to listen on"),
|
|
396
|
+
host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind to"),
|
|
397
|
+
config: str = typer.Option(None, "--config", "-c", help="Path to config file (YAML)"),
|
|
398
|
+
reload: bool = typer.Option(False, "--reload", "-r", help="Enable auto-reload for development"),
|
|
399
|
+
debug: bool = typer.Option(False, "--debug", "-d", help="Enable debug mode"),
|
|
400
|
+
name: str = typer.Option("PraisonAI App", "--name", "-n", help="Application name"),
|
|
401
|
+
):
|
|
402
|
+
"""
|
|
403
|
+
Start an AgentOS server for production deployment.
|
|
404
|
+
|
|
405
|
+
AgentOS provides a FastAPI-based web service for deploying AI agents
|
|
406
|
+
with REST and WebSocket endpoints.
|
|
407
|
+
"""
|
|
408
|
+
from rich.console import Console
|
|
409
|
+
console = Console()
|
|
410
|
+
|
|
411
|
+
try:
|
|
412
|
+
from praisonai import AgentOS
|
|
413
|
+
from praisonaiagents import AgentOSConfig
|
|
414
|
+
except ImportError as e:
|
|
415
|
+
console.print(f"[red]Error importing AgentOS: {e}[/red]")
|
|
416
|
+
console.print("[yellow]Install with: pip install praisonai[api][/yellow]")
|
|
417
|
+
raise typer.Abort()
|
|
418
|
+
|
|
419
|
+
# Load agents from config file if provided
|
|
420
|
+
agents = []
|
|
421
|
+
if config:
|
|
422
|
+
agents = self._load_agents_from_config_file(config, console)
|
|
423
|
+
|
|
424
|
+
# Create config
|
|
425
|
+
app_config = AgentOSConfig(
|
|
426
|
+
name=name,
|
|
427
|
+
host=host,
|
|
428
|
+
port=port,
|
|
429
|
+
reload=reload,
|
|
430
|
+
debug=debug,
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
# Create and start app
|
|
434
|
+
console.print(f"\n[bold green]🚀 Starting {name}[/bold green]")
|
|
435
|
+
console.print(f"[dim]Host: {host}:{port}[/dim]")
|
|
436
|
+
if agents:
|
|
437
|
+
console.print(f"[dim]Agents: {len(agents)}[/dim]")
|
|
438
|
+
if reload:
|
|
439
|
+
console.print("[yellow]Auto-reload enabled (development mode)[/yellow]")
|
|
440
|
+
console.print()
|
|
441
|
+
|
|
442
|
+
try:
|
|
443
|
+
agent_app = AgentOS(
|
|
444
|
+
name=name,
|
|
445
|
+
agents=agents,
|
|
446
|
+
config=app_config,
|
|
447
|
+
)
|
|
448
|
+
agent_app.serve()
|
|
449
|
+
except ImportError as e:
|
|
450
|
+
console.print(f"[red]Missing dependency: {e}[/red]")
|
|
451
|
+
console.print("[yellow]Install with: pip install praisonai[api][/yellow]")
|
|
452
|
+
raise typer.Abort()
|
|
453
|
+
except Exception as e:
|
|
454
|
+
console.print(f"[red]Error starting server: {e}[/red]")
|
|
455
|
+
raise typer.Abort()
|
|
456
|
+
|
|
457
|
+
# Bind the helper method to the function for later use
|
|
458
|
+
app_cmd._load_agents_from_config_file = self._load_agents_from_config_file
|
|
459
|
+
|
|
460
|
+
# Return the click.Command object (not the raw function)
|
|
461
|
+
return typer_get_command(app_group)
|
|
462
|
+
|
|
463
|
+
def _load_agents_from_config_file(self, config_path: str, console) -> list:
|
|
464
|
+
"""Load agents from a YAML config file."""
|
|
465
|
+
import yaml
|
|
466
|
+
|
|
467
|
+
try:
|
|
468
|
+
with open(config_path, 'r') as f:
|
|
469
|
+
config_data = yaml.safe_load(f)
|
|
470
|
+
except Exception as e:
|
|
471
|
+
console.print(f"[red]Error loading config: {e}[/red]")
|
|
472
|
+
return []
|
|
473
|
+
|
|
474
|
+
if not config_data:
|
|
475
|
+
return []
|
|
476
|
+
|
|
477
|
+
agents = []
|
|
478
|
+
|
|
479
|
+
# Try to load agents from config
|
|
480
|
+
agents_config = config_data.get('agents', [])
|
|
481
|
+
if not agents_config and 'agent' in config_data:
|
|
482
|
+
agents_config = [config_data['agent']]
|
|
483
|
+
|
|
484
|
+
if agents_config:
|
|
485
|
+
try:
|
|
486
|
+
from praisonaiagents import Agent
|
|
487
|
+
|
|
488
|
+
for agent_data in agents_config:
|
|
489
|
+
if isinstance(agent_data, dict):
|
|
490
|
+
agent = Agent(
|
|
491
|
+
name=agent_data.get('name', 'Agent'),
|
|
492
|
+
role=agent_data.get('role'),
|
|
493
|
+
instructions=agent_data.get('instructions', agent_data.get('goal', '')),
|
|
494
|
+
llm=agent_data.get('llm'),
|
|
495
|
+
)
|
|
496
|
+
agents.append(agent)
|
|
497
|
+
console.print(f"[green]✓ Loaded agent: {agent.name}[/green]")
|
|
498
|
+
except Exception as e:
|
|
499
|
+
console.print(f"[yellow]Warning: Could not load agents from config: {e}[/yellow]")
|
|
500
|
+
|
|
501
|
+
return agents
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
# Create main Typer app with lazy loading group
|
|
505
|
+
app = typer.Typer(
|
|
506
|
+
name="praisonai",
|
|
507
|
+
help="PraisonAI - AI Agents Framework CLI",
|
|
508
|
+
add_completion=False, # We handle completion manually
|
|
509
|
+
no_args_is_help=False, # Allow running without args for legacy compatibility
|
|
510
|
+
rich_markup_mode="rich",
|
|
511
|
+
cls=LazyCommandGroup, # Use our lazy loading command group
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
# Global state for options
|
|
516
|
+
class GlobalState:
|
|
517
|
+
"""Global state for CLI options."""
|
|
518
|
+
output_format: OutputFormat = OutputFormat.text
|
|
519
|
+
no_color: bool = False
|
|
520
|
+
quiet: bool = False
|
|
521
|
+
verbose: bool = False
|
|
522
|
+
screen_reader: bool = False
|
|
523
|
+
observe: Optional[str] = None
|
|
524
|
+
output_controller: Optional[OutputController] = None
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
state = GlobalState()
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
def version_callback(value: bool):
|
|
531
|
+
"""Show version and exit."""
|
|
532
|
+
if value:
|
|
533
|
+
from praisonai.version import __version__
|
|
534
|
+
typer.echo(f"PraisonAI version {__version__}")
|
|
535
|
+
raise typer.Exit()
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@app.callback(invoke_without_command=True)
|
|
539
|
+
def main_callback(
|
|
540
|
+
ctx: typer.Context,
|
|
541
|
+
version: bool = typer.Option(
|
|
542
|
+
False,
|
|
543
|
+
"--version",
|
|
544
|
+
"-V",
|
|
545
|
+
callback=version_callback,
|
|
546
|
+
is_eager=True,
|
|
547
|
+
help="Show version and exit",
|
|
548
|
+
),
|
|
549
|
+
output_format: OutputFormat = typer.Option(
|
|
550
|
+
OutputFormat.text,
|
|
551
|
+
"--output-format",
|
|
552
|
+
"-o",
|
|
553
|
+
help="Output format",
|
|
554
|
+
envvar="PRAISONAI_OUTPUT_FORMAT",
|
|
555
|
+
),
|
|
556
|
+
json_output: bool = typer.Option(
|
|
557
|
+
False,
|
|
558
|
+
"--json",
|
|
559
|
+
help="Output in JSON format (alias for --output-format json)",
|
|
560
|
+
),
|
|
561
|
+
no_color: bool = typer.Option(
|
|
562
|
+
False,
|
|
563
|
+
"--no-color",
|
|
564
|
+
help="Disable colored output",
|
|
565
|
+
envvar="NO_COLOR",
|
|
566
|
+
),
|
|
567
|
+
quiet: bool = typer.Option(
|
|
568
|
+
False,
|
|
569
|
+
"--quiet",
|
|
570
|
+
"-q",
|
|
571
|
+
help="Minimal output",
|
|
572
|
+
),
|
|
573
|
+
verbose: bool = typer.Option(
|
|
574
|
+
False,
|
|
575
|
+
"--verbose",
|
|
576
|
+
"-v",
|
|
577
|
+
help="Verbose output with debug details",
|
|
578
|
+
),
|
|
579
|
+
screen_reader: bool = typer.Option(
|
|
580
|
+
False,
|
|
581
|
+
"--screen-reader",
|
|
582
|
+
help="Screen reader friendly output (no spinners/panels)",
|
|
583
|
+
),
|
|
584
|
+
observe: Optional[str] = typer.Option(
|
|
585
|
+
None,
|
|
586
|
+
"--observe",
|
|
587
|
+
"-O",
|
|
588
|
+
help="Enable observability (langfuse, langextract)",
|
|
589
|
+
envvar="PRAISONAI_OBSERVE",
|
|
590
|
+
),
|
|
591
|
+
):
|
|
592
|
+
"""
|
|
593
|
+
PraisonAI - AI Agents Framework CLI.
|
|
594
|
+
|
|
595
|
+
Run agents, manage configuration, and more.
|
|
596
|
+
"""
|
|
597
|
+
# Store global options
|
|
598
|
+
state.output_format = output_format
|
|
599
|
+
state.no_color = no_color
|
|
600
|
+
state.quiet = quiet
|
|
601
|
+
state.verbose = verbose
|
|
602
|
+
state.screen_reader = screen_reader
|
|
603
|
+
state.observe = observe
|
|
604
|
+
|
|
605
|
+
# Handle --json alias
|
|
606
|
+
if json_output:
|
|
607
|
+
state.output_format = OutputFormat.json
|
|
608
|
+
|
|
609
|
+
# Validate and set up observability if requested
|
|
610
|
+
if observe:
|
|
611
|
+
if observe == "langfuse":
|
|
612
|
+
_setup_langfuse_observability(verbose=verbose)
|
|
613
|
+
elif observe == "langextract":
|
|
614
|
+
_setup_langextract_observability(verbose=verbose)
|
|
615
|
+
else:
|
|
616
|
+
raise typer.BadParameter(
|
|
617
|
+
f"Unsupported observe provider: {observe}. "
|
|
618
|
+
"Choose one of: langfuse, langextract."
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
# Determine output mode
|
|
622
|
+
if state.quiet:
|
|
623
|
+
mode = OutputMode.QUIET
|
|
624
|
+
elif state.verbose:
|
|
625
|
+
mode = OutputMode.VERBOSE
|
|
626
|
+
elif state.screen_reader:
|
|
627
|
+
mode = OutputMode.SCREEN_READER
|
|
628
|
+
elif state.output_format == OutputFormat.json:
|
|
629
|
+
mode = OutputMode.JSON
|
|
630
|
+
elif state.output_format == OutputFormat.stream_json:
|
|
631
|
+
mode = OutputMode.STREAM_JSON
|
|
632
|
+
else:
|
|
633
|
+
mode = OutputMode.TEXT
|
|
634
|
+
|
|
635
|
+
# Install warning filters for CLI usage only
|
|
636
|
+
from ._warnings import install_warning_filters
|
|
637
|
+
install_warning_filters()
|
|
638
|
+
|
|
639
|
+
# Create run context
|
|
640
|
+
context = create_context()
|
|
641
|
+
|
|
642
|
+
# Create and set output controller
|
|
643
|
+
state.output_controller = OutputController(
|
|
644
|
+
mode=mode,
|
|
645
|
+
no_color=state.no_color,
|
|
646
|
+
run_id=context.run_id,
|
|
647
|
+
trace_id=context.trace_id,
|
|
648
|
+
)
|
|
649
|
+
set_output_controller(state.output_controller)
|
|
650
|
+
|
|
651
|
+
# If no command provided, start interactive mode
|
|
652
|
+
if ctx.invoked_subcommand is None:
|
|
653
|
+
# Check for credentials before starting TUI
|
|
654
|
+
from praisonai.llm.credentials import is_configured
|
|
655
|
+
import sys
|
|
656
|
+
|
|
657
|
+
if not is_configured(): # Check for any configured credentials
|
|
658
|
+
# In non-interactive mode, just show error
|
|
659
|
+
if not sys.stdin.isatty() or quiet:
|
|
660
|
+
typer.echo(
|
|
661
|
+
"Error: No API key configured. Run: praisonai setup",
|
|
662
|
+
err=True
|
|
663
|
+
)
|
|
664
|
+
raise typer.Exit(1)
|
|
665
|
+
|
|
666
|
+
# In interactive mode, offer to run setup
|
|
667
|
+
typer.echo("No API key configured.")
|
|
668
|
+
run_setup = typer.confirm("Would you like to run the setup wizard now?")
|
|
669
|
+
|
|
670
|
+
if run_setup:
|
|
671
|
+
# Import and run setup
|
|
672
|
+
from .commands.setup import _run_setup
|
|
673
|
+
exit_code = _run_setup(
|
|
674
|
+
non_interactive=False,
|
|
675
|
+
provider=None,
|
|
676
|
+
api_key=None,
|
|
677
|
+
model=None
|
|
678
|
+
)
|
|
679
|
+
if exit_code != 0:
|
|
680
|
+
typer.echo("Setup failed. Exiting.", err=True)
|
|
681
|
+
raise typer.Exit(exit_code)
|
|
682
|
+
|
|
683
|
+
# Re-check credentials after setup
|
|
684
|
+
if not is_configured():
|
|
685
|
+
typer.echo("Setup completed but credentials still not detected.", err=True)
|
|
686
|
+
raise typer.Exit(1)
|
|
687
|
+
|
|
688
|
+
# After successful setup, continue to TUI
|
|
689
|
+
typer.echo("\nSetup complete! Starting interactive mode...\n")
|
|
690
|
+
else:
|
|
691
|
+
typer.echo("\nTo configure credentials later, run: praisonai setup")
|
|
692
|
+
typer.echo("or set environment variables like OPENAI_API_KEY")
|
|
693
|
+
raise typer.Exit(0)
|
|
694
|
+
|
|
695
|
+
from .interactive.async_tui import AsyncTUI, AsyncTUIConfig
|
|
696
|
+
|
|
697
|
+
tui_config = AsyncTUIConfig(
|
|
698
|
+
model="gpt-4o-mini",
|
|
699
|
+
show_logo=True,
|
|
700
|
+
show_status_bar=state.output_format != OutputFormat.json,
|
|
701
|
+
)
|
|
702
|
+
|
|
703
|
+
tui = AsyncTUI(config=tui_config)
|
|
704
|
+
tui.run()
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def get_output_controller() -> OutputController:
|
|
708
|
+
"""Get the current output controller."""
|
|
709
|
+
if state.output_controller is None:
|
|
710
|
+
state.output_controller = OutputController()
|
|
711
|
+
return state.output_controller
|
|
712
|
+
|
|
713
|
+
|
|
714
|
+
# Import and register command groups
|
|
715
|
+
_commands_registered = False
|
|
716
|
+
|
|
717
|
+
def get_command_names():
|
|
718
|
+
"""Get all available command names without importing the modules.
|
|
719
|
+
|
|
720
|
+
Derived from the authoritative ``_LAZY_COMMANDS`` registry (the single
|
|
721
|
+
source of truth that also drives ``--help``) so routing can never drift
|
|
722
|
+
from the advertised command set. Special and dynamically-registered
|
|
723
|
+
commands that are not in ``_LAZY_COMMANDS`` are added explicitly.
|
|
724
|
+
"""
|
|
725
|
+
names = set(_LAZY_COMMANDS.keys())
|
|
726
|
+
# Special commands with custom handling (tui, queue)
|
|
727
|
+
names.update(_SPECIAL_COMMANDS.keys())
|
|
728
|
+
# Inline special commands handled outside the registries
|
|
729
|
+
names.update({"app", "standardise", "standardize"})
|
|
730
|
+
# Dynamically registered retrieval commands (no static module entry)
|
|
731
|
+
# NOTE: retrieval_module.register_commands(app) adds these commands dynamically
|
|
732
|
+
names.update({"index", "query"})
|
|
733
|
+
return names
|
|
734
|
+
|
|
735
|
+
def register_commands():
|
|
736
|
+
"""Register all command groups (idempotent).
|
|
737
|
+
|
|
738
|
+
With lazy loading, this function now only registers the retrieval commands
|
|
739
|
+
that need special handling. All other commands are loaded on-demand through
|
|
740
|
+
the LazyCommandGroup.
|
|
741
|
+
"""
|
|
742
|
+
global _commands_registered
|
|
743
|
+
if _commands_registered:
|
|
744
|
+
return
|
|
745
|
+
|
|
746
|
+
# Register retrieval commands (these need special handling)
|
|
747
|
+
try:
|
|
748
|
+
from .commands import retrieval as retrieval_module
|
|
749
|
+
retrieval_module.register_commands(app)
|
|
750
|
+
except ImportError:
|
|
751
|
+
pass # Graceful degradation
|
|
752
|
+
|
|
753
|
+
# Mark registration complete
|
|
754
|
+
_commands_registered = True
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
# Commands will be registered lazily when needed
|