PraisonAI 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TUI (Terminal User Interface) for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Provides an app-like interactive experience with:
|
|
5
|
+
- Event-loop driven async UI
|
|
6
|
+
- Multi-pane layout (chat, tools, queue, status)
|
|
7
|
+
- Streaming output
|
|
8
|
+
- Queue management
|
|
9
|
+
- Session persistence
|
|
10
|
+
- Headless simulation mode for CI/testing
|
|
11
|
+
|
|
12
|
+
Requires the [tui] extra: pip install praisonai[tui]
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .app import TUIApp
|
|
19
|
+
from .events import TUIEvent, TUIEventType
|
|
20
|
+
from .orchestrator import TuiOrchestrator, UIStateModel, SimulationRunner
|
|
21
|
+
from .mock_provider import MockProvider, MockProviderConfig
|
|
22
|
+
|
|
23
|
+
_lazy_cache = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def __getattr__(name: str):
|
|
27
|
+
"""Lazy load TUI components."""
|
|
28
|
+
global _lazy_cache
|
|
29
|
+
|
|
30
|
+
if name in _lazy_cache:
|
|
31
|
+
return _lazy_cache[name]
|
|
32
|
+
|
|
33
|
+
if name == "TUIApp":
|
|
34
|
+
from .app import TUIApp
|
|
35
|
+
_lazy_cache[name] = TUIApp
|
|
36
|
+
return TUIApp
|
|
37
|
+
elif name == "TUIEvent":
|
|
38
|
+
from .events import TUIEvent
|
|
39
|
+
_lazy_cache[name] = TUIEvent
|
|
40
|
+
return TUIEvent
|
|
41
|
+
elif name == "TUIEventType":
|
|
42
|
+
from .events import TUIEventType
|
|
43
|
+
_lazy_cache[name] = TUIEventType
|
|
44
|
+
return TUIEventType
|
|
45
|
+
elif name == "run_tui":
|
|
46
|
+
from .app import run_tui
|
|
47
|
+
_lazy_cache[name] = run_tui
|
|
48
|
+
return run_tui
|
|
49
|
+
elif name == "TuiOrchestrator":
|
|
50
|
+
from .orchestrator import TuiOrchestrator
|
|
51
|
+
_lazy_cache[name] = TuiOrchestrator
|
|
52
|
+
return TuiOrchestrator
|
|
53
|
+
elif name == "UIStateModel":
|
|
54
|
+
from .orchestrator import UIStateModel
|
|
55
|
+
_lazy_cache[name] = UIStateModel
|
|
56
|
+
return UIStateModel
|
|
57
|
+
elif name == "SimulationRunner":
|
|
58
|
+
from .orchestrator import SimulationRunner
|
|
59
|
+
_lazy_cache[name] = SimulationRunner
|
|
60
|
+
return SimulationRunner
|
|
61
|
+
elif name == "MockProvider":
|
|
62
|
+
from .mock_provider import MockProvider
|
|
63
|
+
_lazy_cache[name] = MockProvider
|
|
64
|
+
return MockProvider
|
|
65
|
+
elif name == "MockProviderConfig":
|
|
66
|
+
from .mock_provider import MockProviderConfig
|
|
67
|
+
_lazy_cache[name] = MockProviderConfig
|
|
68
|
+
return MockProviderConfig
|
|
69
|
+
|
|
70
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
__all__ = [
|
|
74
|
+
"TUIApp",
|
|
75
|
+
"TUIEvent",
|
|
76
|
+
"TUIEventType",
|
|
77
|
+
"run_tui",
|
|
78
|
+
"TuiOrchestrator",
|
|
79
|
+
"UIStateModel",
|
|
80
|
+
"SimulationRunner",
|
|
81
|
+
"MockProvider",
|
|
82
|
+
"MockProviderConfig",
|
|
83
|
+
]
|
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main TUI Application for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Event-loop driven async TUI with multi-pane layout.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import uuid
|
|
11
|
+
from typing import Any, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
from textual.app import App, ComposeResult
|
|
15
|
+
from textual.binding import Binding
|
|
16
|
+
from textual.screen import Screen
|
|
17
|
+
from textual import events
|
|
18
|
+
TEXTUAL_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
TEXTUAL_AVAILABLE = False
|
|
21
|
+
App = object
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
if TEXTUAL_AVAILABLE:
|
|
27
|
+
from .screens.main import MainScreen
|
|
28
|
+
from .screens.queue import QueueScreen
|
|
29
|
+
from .screens.settings import SettingsScreen
|
|
30
|
+
from .screens.session import SessionScreen
|
|
31
|
+
from .widgets.chat import ChatMessage
|
|
32
|
+
from .widgets.status import StatusInfo
|
|
33
|
+
from .widgets.queue_panel import QueueItem
|
|
34
|
+
from ..queue import QueueManager, QueueConfig, QueuedRun, RunPriority
|
|
35
|
+
|
|
36
|
+
class TUIApp(App):
|
|
37
|
+
"""
|
|
38
|
+
PraisonAI Terminal User Interface Application.
|
|
39
|
+
|
|
40
|
+
Features:
|
|
41
|
+
- Event-loop driven async UI
|
|
42
|
+
- Multi-pane layout (chat, tools, queue, status)
|
|
43
|
+
- Streaming output
|
|
44
|
+
- Queue management
|
|
45
|
+
- Session persistence
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
TITLE = "PraisonAI"
|
|
49
|
+
SUB_TITLE = "AI Agent Terminal"
|
|
50
|
+
|
|
51
|
+
CSS = """
|
|
52
|
+
Screen {
|
|
53
|
+
background: $surface;
|
|
54
|
+
}
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
BINDINGS = [
|
|
58
|
+
Binding("ctrl+q", "quit", "Quit", show=True, priority=True),
|
|
59
|
+
Binding("ctrl+c", "cancel", "Cancel", show=True),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
SCREENS = {
|
|
63
|
+
"main": MainScreen,
|
|
64
|
+
"queue": QueueScreen,
|
|
65
|
+
"settings": SettingsScreen,
|
|
66
|
+
"sessions": SessionScreen,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
workspace: Optional[str] = None,
|
|
72
|
+
session_id: Optional[str] = None,
|
|
73
|
+
model: Optional[str] = None,
|
|
74
|
+
agent_config: Optional[Dict[str, Any]] = None,
|
|
75
|
+
queue_config: Optional[QueueConfig] = None,
|
|
76
|
+
enable_acp: bool = True,
|
|
77
|
+
enable_lsp: bool = True,
|
|
78
|
+
):
|
|
79
|
+
super().__init__()
|
|
80
|
+
|
|
81
|
+
self.workspace = workspace or os.getcwd()
|
|
82
|
+
self.session_id = session_id or str(uuid.uuid4())[:8]
|
|
83
|
+
self.model = model or os.environ.get("OPENAI_MODEL", "gpt-4o-mini")
|
|
84
|
+
self.agent_config = agent_config or {}
|
|
85
|
+
self.enable_acp = enable_acp
|
|
86
|
+
self.enable_lsp = enable_lsp
|
|
87
|
+
|
|
88
|
+
# Load default interactive tools (ACP + LSP) if not provided
|
|
89
|
+
if "tools" not in self.agent_config or not self.agent_config["tools"]:
|
|
90
|
+
self.agent_config["tools"] = self._load_default_tools()
|
|
91
|
+
|
|
92
|
+
# Queue manager
|
|
93
|
+
self.queue_config = queue_config or QueueConfig()
|
|
94
|
+
self.queue_manager: Optional[QueueManager] = None
|
|
95
|
+
|
|
96
|
+
# State
|
|
97
|
+
self._current_run_id: Optional[str] = None
|
|
98
|
+
self._streaming_content: str = ""
|
|
99
|
+
self._total_tokens: int = 0
|
|
100
|
+
self._total_cost: float = 0.0
|
|
101
|
+
|
|
102
|
+
# Slash command handlers
|
|
103
|
+
self._command_handlers: Dict[str, callable] = {}
|
|
104
|
+
self._setup_commands()
|
|
105
|
+
|
|
106
|
+
def _load_default_tools(self) -> list:
|
|
107
|
+
"""
|
|
108
|
+
Load default interactive tools (ACP + LSP) for TUI.
|
|
109
|
+
|
|
110
|
+
Uses the canonical interactive_tools provider.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
from ..interactive_tools import get_interactive_tools, ToolConfig
|
|
114
|
+
|
|
115
|
+
# Build disable list based on flags
|
|
116
|
+
disable = []
|
|
117
|
+
if not self.enable_acp:
|
|
118
|
+
disable.append('acp')
|
|
119
|
+
if not self.enable_lsp:
|
|
120
|
+
disable.append('lsp')
|
|
121
|
+
|
|
122
|
+
config = ToolConfig(
|
|
123
|
+
workspace=self.workspace,
|
|
124
|
+
enable_acp=self.enable_acp,
|
|
125
|
+
enable_lsp=self.enable_lsp,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
tools = get_interactive_tools(
|
|
129
|
+
config=config,
|
|
130
|
+
disable=disable if disable else None,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
logger.info(f"TUI loaded {len(tools)} default tools (ACP: {self.enable_acp}, LSP: {self.enable_lsp})")
|
|
134
|
+
return tools
|
|
135
|
+
|
|
136
|
+
except ImportError as e:
|
|
137
|
+
logger.warning(f"Could not load interactive tools: {e}")
|
|
138
|
+
return []
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.error(f"Error loading default tools: {e}")
|
|
141
|
+
return []
|
|
142
|
+
|
|
143
|
+
def _setup_commands(self) -> None:
|
|
144
|
+
"""Setup slash command handlers."""
|
|
145
|
+
self._command_handlers = {
|
|
146
|
+
"help": self._cmd_help,
|
|
147
|
+
"clear": self._cmd_clear,
|
|
148
|
+
"model": self._cmd_model,
|
|
149
|
+
"queue": self._cmd_queue,
|
|
150
|
+
"cancel": self._cmd_cancel,
|
|
151
|
+
"retry": self._cmd_retry,
|
|
152
|
+
"settings": self._cmd_settings,
|
|
153
|
+
"sessions": self._cmd_sessions,
|
|
154
|
+
"cost": self._cmd_cost,
|
|
155
|
+
"exit": self._cmd_exit,
|
|
156
|
+
"quit": self._cmd_exit,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async def on_mount(self) -> None:
|
|
160
|
+
"""Handle app mount."""
|
|
161
|
+
# Initialize queue manager
|
|
162
|
+
self.queue_manager = QueueManager(
|
|
163
|
+
config=self.queue_config,
|
|
164
|
+
on_output=self._handle_output,
|
|
165
|
+
on_complete=self._handle_complete,
|
|
166
|
+
on_error=self._handle_error,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
await self.queue_manager.start(recover=True)
|
|
170
|
+
self.queue_manager.set_session(self.session_id)
|
|
171
|
+
|
|
172
|
+
# Push main screen
|
|
173
|
+
await self.push_screen("main")
|
|
174
|
+
|
|
175
|
+
# Update status
|
|
176
|
+
self._update_status()
|
|
177
|
+
|
|
178
|
+
async def on_unmount(self) -> None:
|
|
179
|
+
"""Handle app unmount."""
|
|
180
|
+
if self.queue_manager:
|
|
181
|
+
await self.queue_manager.stop()
|
|
182
|
+
|
|
183
|
+
def compose(self) -> ComposeResult:
|
|
184
|
+
"""Compose the app."""
|
|
185
|
+
yield from ()
|
|
186
|
+
|
|
187
|
+
# Event handlers
|
|
188
|
+
|
|
189
|
+
async def on_main_screen_message_submitted(
|
|
190
|
+
self, event: MainScreen.MessageSubmitted
|
|
191
|
+
) -> None:
|
|
192
|
+
"""Handle message submission from main screen."""
|
|
193
|
+
content = event.content
|
|
194
|
+
|
|
195
|
+
# Add user message to chat
|
|
196
|
+
main_screen = self.screen
|
|
197
|
+
if isinstance(main_screen, MainScreen):
|
|
198
|
+
await main_screen.add_user_message(content)
|
|
199
|
+
main_screen.set_processing(True)
|
|
200
|
+
|
|
201
|
+
# Submit to queue
|
|
202
|
+
try:
|
|
203
|
+
run_id = await self.queue_manager.submit(
|
|
204
|
+
input_content=content,
|
|
205
|
+
agent_name=self.agent_config.get("name", "Assistant"),
|
|
206
|
+
config={
|
|
207
|
+
"agent_config": {
|
|
208
|
+
"name": self.agent_config.get("name", "Assistant"),
|
|
209
|
+
"instructions": self.agent_config.get(
|
|
210
|
+
"instructions",
|
|
211
|
+
"You are a helpful AI assistant."
|
|
212
|
+
),
|
|
213
|
+
"model": self.model,
|
|
214
|
+
"tools": self.agent_config.get("tools", []),
|
|
215
|
+
"verbose": False,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
self._current_run_id = run_id
|
|
221
|
+
self._streaming_content = ""
|
|
222
|
+
|
|
223
|
+
# Add streaming placeholder
|
|
224
|
+
if isinstance(main_screen, MainScreen):
|
|
225
|
+
await main_screen.add_assistant_message(
|
|
226
|
+
content="",
|
|
227
|
+
run_id=run_id,
|
|
228
|
+
agent_name=self.agent_config.get("name", "Assistant"),
|
|
229
|
+
streaming=True,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
except Exception as e:
|
|
233
|
+
logger.error(f"Failed to submit: {e}")
|
|
234
|
+
if isinstance(main_screen, MainScreen):
|
|
235
|
+
main_screen.set_processing(False)
|
|
236
|
+
await main_screen.add_assistant_message(
|
|
237
|
+
content=f"Error: {e}",
|
|
238
|
+
agent_name="System",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
self._update_status()
|
|
242
|
+
|
|
243
|
+
async def on_main_screen_command_executed(
|
|
244
|
+
self, event: MainScreen.CommandExecuted
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Handle command execution."""
|
|
247
|
+
command = event.command.lower()
|
|
248
|
+
args = event.args
|
|
249
|
+
|
|
250
|
+
handler = self._command_handlers.get(command)
|
|
251
|
+
if handler:
|
|
252
|
+
await handler(args)
|
|
253
|
+
else:
|
|
254
|
+
# Unknown command
|
|
255
|
+
main_screen = self.screen
|
|
256
|
+
if isinstance(main_screen, MainScreen):
|
|
257
|
+
await main_screen.add_assistant_message(
|
|
258
|
+
content=f"Unknown command: /{command}\nType /help for available commands.",
|
|
259
|
+
agent_name="System",
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
async def on_main_screen_cancel_requested(
|
|
263
|
+
self, event: MainScreen.CancelRequested
|
|
264
|
+
) -> None:
|
|
265
|
+
"""Handle cancel request."""
|
|
266
|
+
if self._current_run_id:
|
|
267
|
+
await self.queue_manager.cancel(self._current_run_id)
|
|
268
|
+
self._current_run_id = None
|
|
269
|
+
|
|
270
|
+
main_screen = self.screen
|
|
271
|
+
if isinstance(main_screen, MainScreen):
|
|
272
|
+
main_screen.set_processing(False)
|
|
273
|
+
await main_screen.add_assistant_message(
|
|
274
|
+
content="Operation cancelled.",
|
|
275
|
+
agent_name="System",
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
self._update_status()
|
|
279
|
+
|
|
280
|
+
async def on_queue_screen_run_cancelled(
|
|
281
|
+
self, event: QueueScreen.RunCancelled
|
|
282
|
+
) -> None:
|
|
283
|
+
"""Handle run cancellation from queue screen."""
|
|
284
|
+
await self.queue_manager.cancel(event.run_id)
|
|
285
|
+
self._update_queue_screen()
|
|
286
|
+
|
|
287
|
+
async def on_queue_screen_run_retried(
|
|
288
|
+
self, event: QueueScreen.RunRetried
|
|
289
|
+
) -> None:
|
|
290
|
+
"""Handle run retry from queue screen."""
|
|
291
|
+
await self.queue_manager.retry(event.run_id)
|
|
292
|
+
self._update_queue_screen()
|
|
293
|
+
|
|
294
|
+
async def on_queue_screen_queue_cleared(
|
|
295
|
+
self, event: QueueScreen.QueueCleared
|
|
296
|
+
) -> None:
|
|
297
|
+
"""Handle queue clear."""
|
|
298
|
+
await self.queue_manager.clear_queue()
|
|
299
|
+
self._update_queue_screen()
|
|
300
|
+
|
|
301
|
+
async def on_settings_screen_settings_saved(
|
|
302
|
+
self, event: SettingsScreen.SettingsSaved
|
|
303
|
+
) -> None:
|
|
304
|
+
"""Handle settings save."""
|
|
305
|
+
settings = event.settings
|
|
306
|
+
|
|
307
|
+
if "model" in settings:
|
|
308
|
+
self.model = settings["model"]
|
|
309
|
+
|
|
310
|
+
if "max_concurrent" in settings:
|
|
311
|
+
self.queue_config.max_concurrent_global = settings["max_concurrent"]
|
|
312
|
+
|
|
313
|
+
if "autosave_interval" in settings:
|
|
314
|
+
self.queue_config.autosave_interval_seconds = settings["autosave_interval"]
|
|
315
|
+
|
|
316
|
+
self._update_status()
|
|
317
|
+
|
|
318
|
+
async def on_session_screen_session_selected(
|
|
319
|
+
self, event: SessionScreen.SessionSelected
|
|
320
|
+
) -> None:
|
|
321
|
+
"""Handle session selection."""
|
|
322
|
+
self.session_id = event.session_id
|
|
323
|
+
self.queue_manager.set_session(self.session_id)
|
|
324
|
+
self._update_status()
|
|
325
|
+
|
|
326
|
+
# Queue callbacks
|
|
327
|
+
|
|
328
|
+
async def _handle_output(self, run_id: str, chunk: str) -> None:
|
|
329
|
+
"""Handle streaming output."""
|
|
330
|
+
if run_id != self._current_run_id:
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
self._streaming_content += chunk
|
|
334
|
+
|
|
335
|
+
main_screen = self.screen
|
|
336
|
+
if isinstance(main_screen, MainScreen):
|
|
337
|
+
await main_screen.update_streaming(run_id, self._streaming_content)
|
|
338
|
+
|
|
339
|
+
async def _handle_complete(self, run_id: str, run: QueuedRun) -> None:
|
|
340
|
+
"""Handle run completion."""
|
|
341
|
+
main_screen = self.screen
|
|
342
|
+
if isinstance(main_screen, MainScreen):
|
|
343
|
+
await main_screen.complete_streaming(
|
|
344
|
+
run_id,
|
|
345
|
+
run.output_content or self._streaming_content
|
|
346
|
+
)
|
|
347
|
+
main_screen.set_processing(False)
|
|
348
|
+
|
|
349
|
+
# Update metrics
|
|
350
|
+
if run.metrics:
|
|
351
|
+
self._total_tokens += run.metrics.get("tokens", 0)
|
|
352
|
+
self._total_cost += run.metrics.get("cost", 0.0)
|
|
353
|
+
|
|
354
|
+
if run_id == self._current_run_id:
|
|
355
|
+
self._current_run_id = None
|
|
356
|
+
self._streaming_content = ""
|
|
357
|
+
|
|
358
|
+
self._update_status()
|
|
359
|
+
|
|
360
|
+
async def _handle_error(self, run_id: str, error: Exception) -> None:
|
|
361
|
+
"""Handle run error."""
|
|
362
|
+
main_screen = self.screen
|
|
363
|
+
if isinstance(main_screen, MainScreen):
|
|
364
|
+
await main_screen.complete_streaming(run_id, f"Error: {error}")
|
|
365
|
+
main_screen.set_processing(False)
|
|
366
|
+
|
|
367
|
+
if run_id == self._current_run_id:
|
|
368
|
+
self._current_run_id = None
|
|
369
|
+
self._streaming_content = ""
|
|
370
|
+
|
|
371
|
+
self._update_status()
|
|
372
|
+
|
|
373
|
+
# Command handlers
|
|
374
|
+
|
|
375
|
+
async def _cmd_help(self, args: str) -> None:
|
|
376
|
+
"""Show help."""
|
|
377
|
+
help_text = """
|
|
378
|
+
**Available Commands:**
|
|
379
|
+
|
|
380
|
+
- `/help` - Show this help
|
|
381
|
+
- `/clear` - Clear chat history
|
|
382
|
+
- `/model [name]` - Show or set model
|
|
383
|
+
- `/queue` - Show queue screen
|
|
384
|
+
- `/cancel [run_id]` - Cancel a run
|
|
385
|
+
- `/retry [run_id]` - Retry a failed run
|
|
386
|
+
- `/settings` - Show settings
|
|
387
|
+
- `/sessions` - Browse sessions
|
|
388
|
+
- `/cost` - Show cost summary
|
|
389
|
+
- `/exit` or `/quit` - Exit TUI
|
|
390
|
+
|
|
391
|
+
**Keyboard Shortcuts:**
|
|
392
|
+
|
|
393
|
+
- `Ctrl+Enter` - Send message
|
|
394
|
+
- `Ctrl+C` - Cancel current operation
|
|
395
|
+
- `Ctrl+Q` - Quit
|
|
396
|
+
- `Ctrl+L` - Clear screen
|
|
397
|
+
- `F1` - Help
|
|
398
|
+
- `F2` - Toggle queue panel
|
|
399
|
+
- `F3` - Settings
|
|
400
|
+
- `F5` - Clear chat
|
|
401
|
+
- `Tab` - Cycle focus
|
|
402
|
+
- `Esc` - Cancel/close
|
|
403
|
+
"""
|
|
404
|
+
main_screen = self.screen
|
|
405
|
+
if isinstance(main_screen, MainScreen):
|
|
406
|
+
await main_screen.add_assistant_message(
|
|
407
|
+
content=help_text,
|
|
408
|
+
agent_name="Help",
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
async def _cmd_clear(self, args: str) -> None:
|
|
412
|
+
"""Clear chat."""
|
|
413
|
+
main_screen = self.screen
|
|
414
|
+
if isinstance(main_screen, MainScreen):
|
|
415
|
+
await main_screen.query_one("#chat-widget").clear()
|
|
416
|
+
|
|
417
|
+
async def _cmd_model(self, args: str) -> None:
|
|
418
|
+
"""Show or set model."""
|
|
419
|
+
main_screen = self.screen
|
|
420
|
+
if args:
|
|
421
|
+
self.model = args.strip()
|
|
422
|
+
msg = f"Model set to: {self.model}"
|
|
423
|
+
else:
|
|
424
|
+
msg = f"Current model: {self.model}"
|
|
425
|
+
|
|
426
|
+
if isinstance(main_screen, MainScreen):
|
|
427
|
+
await main_screen.add_assistant_message(content=msg, agent_name="System")
|
|
428
|
+
|
|
429
|
+
self._update_status()
|
|
430
|
+
|
|
431
|
+
async def _cmd_queue(self, args: str) -> None:
|
|
432
|
+
"""Show queue screen."""
|
|
433
|
+
await self.push_screen("queue")
|
|
434
|
+
self._update_queue_screen()
|
|
435
|
+
|
|
436
|
+
async def _cmd_cancel(self, args: str) -> None:
|
|
437
|
+
"""Cancel a run."""
|
|
438
|
+
run_id = args.strip() if args else self._current_run_id
|
|
439
|
+
if run_id:
|
|
440
|
+
await self.queue_manager.cancel(run_id)
|
|
441
|
+
main_screen = self.screen
|
|
442
|
+
if isinstance(main_screen, MainScreen):
|
|
443
|
+
await main_screen.add_assistant_message(
|
|
444
|
+
content=f"Cancelled run: {run_id}",
|
|
445
|
+
agent_name="System",
|
|
446
|
+
)
|
|
447
|
+
self._update_status()
|
|
448
|
+
|
|
449
|
+
async def _cmd_retry(self, args: str) -> None:
|
|
450
|
+
"""Retry a failed run."""
|
|
451
|
+
run_id = args.strip()
|
|
452
|
+
if run_id:
|
|
453
|
+
new_id = await self.queue_manager.retry(run_id)
|
|
454
|
+
main_screen = self.screen
|
|
455
|
+
if isinstance(main_screen, MainScreen):
|
|
456
|
+
if new_id:
|
|
457
|
+
await main_screen.add_assistant_message(
|
|
458
|
+
content=f"Retrying run {run_id} as {new_id}",
|
|
459
|
+
agent_name="System",
|
|
460
|
+
)
|
|
461
|
+
else:
|
|
462
|
+
await main_screen.add_assistant_message(
|
|
463
|
+
content=f"Cannot retry run: {run_id}",
|
|
464
|
+
agent_name="System",
|
|
465
|
+
)
|
|
466
|
+
self._update_status()
|
|
467
|
+
|
|
468
|
+
async def _cmd_settings(self, args: str) -> None:
|
|
469
|
+
"""Show settings."""
|
|
470
|
+
await self.push_screen(SettingsScreen(current_settings={
|
|
471
|
+
"model": self.model,
|
|
472
|
+
"max_concurrent": self.queue_config.max_concurrent_global,
|
|
473
|
+
"autosave_interval": int(self.queue_config.autosave_interval_seconds),
|
|
474
|
+
}))
|
|
475
|
+
|
|
476
|
+
async def _cmd_sessions(self, args: str) -> None:
|
|
477
|
+
"""Show sessions."""
|
|
478
|
+
await self.push_screen("sessions")
|
|
479
|
+
|
|
480
|
+
async def _cmd_cost(self, args: str) -> None:
|
|
481
|
+
"""Show cost summary."""
|
|
482
|
+
msg = f"""
|
|
483
|
+
**Cost Summary:**
|
|
484
|
+
|
|
485
|
+
- Total Tokens: {self._total_tokens:,}
|
|
486
|
+
- Total Cost: ${self._total_cost:.4f}
|
|
487
|
+
- Session: {self.session_id}
|
|
488
|
+
"""
|
|
489
|
+
main_screen = self.screen
|
|
490
|
+
if isinstance(main_screen, MainScreen):
|
|
491
|
+
await main_screen.add_assistant_message(content=msg, agent_name="System")
|
|
492
|
+
|
|
493
|
+
async def _cmd_exit(self, args: str) -> None:
|
|
494
|
+
"""Exit TUI."""
|
|
495
|
+
self.exit()
|
|
496
|
+
|
|
497
|
+
# Helper methods
|
|
498
|
+
|
|
499
|
+
def _update_status(self) -> None:
|
|
500
|
+
"""Update status bar."""
|
|
501
|
+
main_screen = self.screen
|
|
502
|
+
if isinstance(main_screen, MainScreen):
|
|
503
|
+
main_screen.update_status(StatusInfo(
|
|
504
|
+
session_id=self.session_id,
|
|
505
|
+
model=self.model,
|
|
506
|
+
total_tokens=self._total_tokens,
|
|
507
|
+
total_cost=self._total_cost,
|
|
508
|
+
queued_count=self.queue_manager.queued_count if self.queue_manager else 0,
|
|
509
|
+
running_count=self.queue_manager.running_count if self.queue_manager else 0,
|
|
510
|
+
is_processing=self._current_run_id is not None,
|
|
511
|
+
))
|
|
512
|
+
|
|
513
|
+
def _update_queue_screen(self) -> None:
|
|
514
|
+
"""Update queue screen if visible."""
|
|
515
|
+
if isinstance(self.screen, QueueScreen):
|
|
516
|
+
runs = self.queue_manager.list_runs(limit=100)
|
|
517
|
+
self.screen.update_runs([
|
|
518
|
+
{
|
|
519
|
+
"run_id": r.run_id,
|
|
520
|
+
"agent_name": r.agent_name,
|
|
521
|
+
"input": r.input_content,
|
|
522
|
+
"state": r.state.value,
|
|
523
|
+
"priority": r.priority.name.lower(),
|
|
524
|
+
"wait_time": f"{r.wait_seconds:.1f}s" if r.wait_seconds else "-",
|
|
525
|
+
"duration": f"{r.duration_seconds:.1f}s" if r.duration_seconds else "-",
|
|
526
|
+
}
|
|
527
|
+
for r in runs
|
|
528
|
+
])
|
|
529
|
+
|
|
530
|
+
def action_quit(self) -> None:
|
|
531
|
+
"""Quit the application."""
|
|
532
|
+
self.exit()
|
|
533
|
+
|
|
534
|
+
def action_cancel(self) -> None:
|
|
535
|
+
"""Cancel current operation."""
|
|
536
|
+
if self._current_run_id and self.queue_manager:
|
|
537
|
+
asyncio.create_task(self.queue_manager.cancel(self._current_run_id))
|
|
538
|
+
|
|
539
|
+
else:
|
|
540
|
+
class TUIApp:
|
|
541
|
+
"""Placeholder when Textual is not available."""
|
|
542
|
+
def __init__(self, *args, **kwargs):
|
|
543
|
+
raise ImportError(
|
|
544
|
+
"Textual is required for TUI. Install with: pip install praisonai[tui]"
|
|
545
|
+
)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def run_tui(
|
|
549
|
+
workspace: Optional[str] = None,
|
|
550
|
+
session_id: Optional[str] = None,
|
|
551
|
+
model: Optional[str] = None,
|
|
552
|
+
agent_config: Optional[Dict[str, Any]] = None,
|
|
553
|
+
enable_acp: bool = True,
|
|
554
|
+
enable_lsp: bool = True,
|
|
555
|
+
) -> None:
|
|
556
|
+
"""
|
|
557
|
+
Run the PraisonAI TUI.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
workspace: Working directory.
|
|
561
|
+
session_id: Session ID to resume.
|
|
562
|
+
model: Default model.
|
|
563
|
+
agent_config: Agent configuration.
|
|
564
|
+
enable_acp: Enable ACP tools (default: True).
|
|
565
|
+
enable_lsp: Enable LSP tools (default: True).
|
|
566
|
+
"""
|
|
567
|
+
if not TEXTUAL_AVAILABLE:
|
|
568
|
+
raise ImportError(
|
|
569
|
+
"Textual is required for TUI. Install with: pip install praisonai[tui]"
|
|
570
|
+
)
|
|
571
|
+
|
|
572
|
+
app = TUIApp(
|
|
573
|
+
workspace=workspace,
|
|
574
|
+
session_id=session_id,
|
|
575
|
+
model=model,
|
|
576
|
+
agent_config=agent_config,
|
|
577
|
+
enable_acp=enable_acp,
|
|
578
|
+
enable_lsp=enable_lsp,
|
|
579
|
+
)
|
|
580
|
+
app.run()
|