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,200 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Queue Panel Widget for PraisonAI TUI.
|
|
3
|
+
|
|
4
|
+
Displays queue status and allows queue management.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from textual.widget import Widget
|
|
12
|
+
from textual.widgets import Static, DataTable, Button
|
|
13
|
+
from textual.containers import Vertical
|
|
14
|
+
from textual.reactive import reactive
|
|
15
|
+
from textual.message import Message
|
|
16
|
+
from rich.text import Text
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
TEXTUAL_AVAILABLE = True
|
|
19
|
+
except ImportError:
|
|
20
|
+
TEXTUAL_AVAILABLE = False
|
|
21
|
+
Widget = object
|
|
22
|
+
Message = object
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class QueueItem:
|
|
27
|
+
"""A queue item for display."""
|
|
28
|
+
run_id: str
|
|
29
|
+
agent_name: str
|
|
30
|
+
input_preview: str
|
|
31
|
+
state: str
|
|
32
|
+
priority: str
|
|
33
|
+
wait_time: str
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if TEXTUAL_AVAILABLE:
|
|
37
|
+
class QueuePanelWidget(Vertical):
|
|
38
|
+
"""
|
|
39
|
+
Queue panel widget.
|
|
40
|
+
|
|
41
|
+
Displays:
|
|
42
|
+
- Queue summary (queued/running counts)
|
|
43
|
+
- List of queued runs
|
|
44
|
+
- Cancel/retry buttons
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
DEFAULT_CSS = """
|
|
48
|
+
QueuePanelWidget {
|
|
49
|
+
height: 100%;
|
|
50
|
+
border: solid $secondary;
|
|
51
|
+
background: $surface;
|
|
52
|
+
padding: 0 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
QueuePanelWidget .queue-header {
|
|
56
|
+
height: 1;
|
|
57
|
+
background: $secondary;
|
|
58
|
+
padding: 0 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
QueuePanelWidget DataTable {
|
|
62
|
+
height: 1fr;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
QueuePanelWidget .queue-item-queued {
|
|
66
|
+
color: $warning;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
QueuePanelWidget .queue-item-running {
|
|
70
|
+
color: $success;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
QueuePanelWidget .queue-item-failed {
|
|
74
|
+
color: $error;
|
|
75
|
+
}
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
class RunSelected(Message):
|
|
79
|
+
"""Event when a run is selected."""
|
|
80
|
+
def __init__(self, run_id: str):
|
|
81
|
+
self.run_id = run_id
|
|
82
|
+
super().__init__()
|
|
83
|
+
|
|
84
|
+
class CancelRequested(Message):
|
|
85
|
+
"""Event when cancel is requested."""
|
|
86
|
+
def __init__(self, run_id: str):
|
|
87
|
+
self.run_id = run_id
|
|
88
|
+
super().__init__()
|
|
89
|
+
|
|
90
|
+
class RetryRequested(Message):
|
|
91
|
+
"""Event when retry is requested."""
|
|
92
|
+
def __init__(self, run_id: str):
|
|
93
|
+
self.run_id = run_id
|
|
94
|
+
super().__init__()
|
|
95
|
+
|
|
96
|
+
queued_count: reactive[int] = reactive(0)
|
|
97
|
+
running_count: reactive[int] = reactive(0)
|
|
98
|
+
|
|
99
|
+
def __init__(
|
|
100
|
+
self,
|
|
101
|
+
name: Optional[str] = None,
|
|
102
|
+
id: Optional[str] = None,
|
|
103
|
+
classes: Optional[str] = None,
|
|
104
|
+
):
|
|
105
|
+
super().__init__(name=name, id=id, classes=classes)
|
|
106
|
+
self._items: List[QueueItem] = []
|
|
107
|
+
self._selected_run_id: Optional[str] = None
|
|
108
|
+
|
|
109
|
+
def compose(self):
|
|
110
|
+
"""Compose the widget."""
|
|
111
|
+
yield Static("Queue", id="queue-title", classes="queue-header")
|
|
112
|
+
yield DataTable(id="queue-table")
|
|
113
|
+
|
|
114
|
+
def on_mount(self) -> None:
|
|
115
|
+
"""Handle mount."""
|
|
116
|
+
table = self.query_one("#queue-table", DataTable)
|
|
117
|
+
table.add_columns("ID", "Agent", "Input", "State", "Priority")
|
|
118
|
+
table.cursor_type = "row"
|
|
119
|
+
self._update_header()
|
|
120
|
+
|
|
121
|
+
def watch_queued_count(self, value: int) -> None:
|
|
122
|
+
self._update_header()
|
|
123
|
+
|
|
124
|
+
def watch_running_count(self, value: int) -> None:
|
|
125
|
+
self._update_header()
|
|
126
|
+
|
|
127
|
+
def _update_header(self) -> None:
|
|
128
|
+
"""Update the header text."""
|
|
129
|
+
try:
|
|
130
|
+
header = self.query_one("#queue-title", Static)
|
|
131
|
+
text = Text()
|
|
132
|
+
text.append("Queue ", style="bold")
|
|
133
|
+
text.append(f"⏳ {self.queued_count} queued", style="yellow")
|
|
134
|
+
text.append(" │ ", style="dim")
|
|
135
|
+
text.append(f"▶ {self.running_count} running", style="green")
|
|
136
|
+
header.update(text)
|
|
137
|
+
except Exception:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
def update_items(self, items: List[QueueItem]) -> None:
|
|
141
|
+
"""Update the queue items."""
|
|
142
|
+
self._items = items
|
|
143
|
+
|
|
144
|
+
table = self.query_one("#queue-table", DataTable)
|
|
145
|
+
table.clear()
|
|
146
|
+
|
|
147
|
+
for item in items:
|
|
148
|
+
# Style based on state
|
|
149
|
+
state_style = {
|
|
150
|
+
"queued": "yellow",
|
|
151
|
+
"running": "green",
|
|
152
|
+
"failed": "red",
|
|
153
|
+
"cancelled": "dim",
|
|
154
|
+
"succeeded": "cyan",
|
|
155
|
+
}.get(item.state.lower(), "")
|
|
156
|
+
|
|
157
|
+
state_text = Text(item.state, style=state_style)
|
|
158
|
+
|
|
159
|
+
# Truncate input preview
|
|
160
|
+
input_preview = item.input_preview[:30]
|
|
161
|
+
if len(item.input_preview) > 30:
|
|
162
|
+
input_preview += "..."
|
|
163
|
+
|
|
164
|
+
table.add_row(
|
|
165
|
+
item.run_id[:8],
|
|
166
|
+
item.agent_name,
|
|
167
|
+
input_preview,
|
|
168
|
+
state_text,
|
|
169
|
+
item.priority,
|
|
170
|
+
key=item.run_id,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
|
|
174
|
+
"""Handle row selection."""
|
|
175
|
+
if event.row_key:
|
|
176
|
+
self._selected_run_id = str(event.row_key.value)
|
|
177
|
+
self.post_message(self.RunSelected(self._selected_run_id))
|
|
178
|
+
|
|
179
|
+
def cancel_selected(self) -> None:
|
|
180
|
+
"""Cancel the selected run."""
|
|
181
|
+
if self._selected_run_id:
|
|
182
|
+
self.post_message(self.CancelRequested(self._selected_run_id))
|
|
183
|
+
|
|
184
|
+
def retry_selected(self) -> None:
|
|
185
|
+
"""Retry the selected run."""
|
|
186
|
+
if self._selected_run_id:
|
|
187
|
+
self.post_message(self.RetryRequested(self._selected_run_id))
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def selected_run_id(self) -> Optional[str]:
|
|
191
|
+
"""Get the selected run ID."""
|
|
192
|
+
return self._selected_run_id
|
|
193
|
+
|
|
194
|
+
else:
|
|
195
|
+
class QueuePanelWidget:
|
|
196
|
+
"""Placeholder when Textual is not available."""
|
|
197
|
+
def __init__(self, *args, **kwargs):
|
|
198
|
+
raise ImportError(
|
|
199
|
+
"Textual is required for TUI. Install with: pip install praisonai[tui]"
|
|
200
|
+
)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Status Widget for PraisonAI TUI.
|
|
3
|
+
|
|
4
|
+
Displays status bar with session info, model, tokens, and cost.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from textual.widget import Widget
|
|
12
|
+
from textual.widgets import Static
|
|
13
|
+
from textual.containers import Horizontal
|
|
14
|
+
from textual.reactive import reactive
|
|
15
|
+
from rich.text import Text
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
TEXTUAL_AVAILABLE = True
|
|
18
|
+
except ImportError:
|
|
19
|
+
TEXTUAL_AVAILABLE = False
|
|
20
|
+
Widget = object
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class StatusInfo:
|
|
25
|
+
"""Status information."""
|
|
26
|
+
session_id: str = ""
|
|
27
|
+
model: str = "gpt-4o-mini"
|
|
28
|
+
total_tokens: int = 0
|
|
29
|
+
total_cost: float = 0.0
|
|
30
|
+
queued_count: int = 0
|
|
31
|
+
running_count: int = 0
|
|
32
|
+
is_processing: bool = False
|
|
33
|
+
status_message: str = ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
if TEXTUAL_AVAILABLE:
|
|
37
|
+
class StatusWidget(Static):
|
|
38
|
+
"""
|
|
39
|
+
Status bar widget.
|
|
40
|
+
|
|
41
|
+
Displays:
|
|
42
|
+
- Session ID
|
|
43
|
+
- Current model
|
|
44
|
+
- Token usage
|
|
45
|
+
- Cost
|
|
46
|
+
- Queue status
|
|
47
|
+
- Processing indicator
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
DEFAULT_CSS = """
|
|
51
|
+
StatusWidget {
|
|
52
|
+
height: 1;
|
|
53
|
+
background: $primary;
|
|
54
|
+
color: $text;
|
|
55
|
+
padding: 0 1;
|
|
56
|
+
}
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
session_id: reactive[str] = reactive("")
|
|
60
|
+
model: reactive[str] = reactive("gpt-4o-mini")
|
|
61
|
+
total_tokens: reactive[int] = reactive(0)
|
|
62
|
+
total_cost: reactive[float] = reactive(0.0)
|
|
63
|
+
queued_count: reactive[int] = reactive(0)
|
|
64
|
+
running_count: reactive[int] = reactive(0)
|
|
65
|
+
is_processing: reactive[bool] = reactive(False)
|
|
66
|
+
status_message: reactive[str] = reactive("")
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
name: Optional[str] = None,
|
|
71
|
+
id: Optional[str] = None,
|
|
72
|
+
classes: Optional[str] = None,
|
|
73
|
+
):
|
|
74
|
+
super().__init__("", name=name, id=id, classes=classes)
|
|
75
|
+
|
|
76
|
+
def on_mount(self) -> None:
|
|
77
|
+
"""Handle mount."""
|
|
78
|
+
self._update_display()
|
|
79
|
+
|
|
80
|
+
def watch_session_id(self, value: str) -> None:
|
|
81
|
+
self._update_display()
|
|
82
|
+
|
|
83
|
+
def watch_model(self, value: str) -> None:
|
|
84
|
+
self._update_display()
|
|
85
|
+
|
|
86
|
+
def watch_total_tokens(self, value: int) -> None:
|
|
87
|
+
self._update_display()
|
|
88
|
+
|
|
89
|
+
def watch_total_cost(self, value: float) -> None:
|
|
90
|
+
self._update_display()
|
|
91
|
+
|
|
92
|
+
def watch_queued_count(self, value: int) -> None:
|
|
93
|
+
self._update_display()
|
|
94
|
+
|
|
95
|
+
def watch_running_count(self, value: int) -> None:
|
|
96
|
+
self._update_display()
|
|
97
|
+
|
|
98
|
+
def watch_is_processing(self, value: bool) -> None:
|
|
99
|
+
self._update_display()
|
|
100
|
+
|
|
101
|
+
def watch_status_message(self, value: str) -> None:
|
|
102
|
+
self._update_display()
|
|
103
|
+
|
|
104
|
+
def _update_display(self) -> None:
|
|
105
|
+
"""Update the status display."""
|
|
106
|
+
parts = []
|
|
107
|
+
|
|
108
|
+
# App name
|
|
109
|
+
parts.append(("◉ PraisonAI", "bold cyan"))
|
|
110
|
+
|
|
111
|
+
# Session
|
|
112
|
+
if self.session_id:
|
|
113
|
+
parts.append((f"Session: {self.session_id[:8]}", ""))
|
|
114
|
+
|
|
115
|
+
# Model
|
|
116
|
+
parts.append((f"Model: {self.model}", ""))
|
|
117
|
+
|
|
118
|
+
# Tokens
|
|
119
|
+
if self.total_tokens > 0:
|
|
120
|
+
parts.append((f"Tokens: {self.total_tokens:,}", ""))
|
|
121
|
+
|
|
122
|
+
# Cost
|
|
123
|
+
if self.total_cost > 0:
|
|
124
|
+
parts.append((f"${self.total_cost:.4f}", "green"))
|
|
125
|
+
|
|
126
|
+
# Queue status
|
|
127
|
+
if self.queued_count > 0 or self.running_count > 0:
|
|
128
|
+
queue_text = f"⏳ {self.queued_count} queued"
|
|
129
|
+
if self.running_count > 0:
|
|
130
|
+
queue_text += f" │ ▶ {self.running_count} running"
|
|
131
|
+
parts.append((queue_text, "yellow"))
|
|
132
|
+
|
|
133
|
+
# Processing indicator
|
|
134
|
+
if self.is_processing:
|
|
135
|
+
parts.append(("⟳ Processing...", "bold magenta"))
|
|
136
|
+
|
|
137
|
+
# Status message
|
|
138
|
+
if self.status_message:
|
|
139
|
+
parts.append((self.status_message, "italic"))
|
|
140
|
+
|
|
141
|
+
# Build text
|
|
142
|
+
text = Text()
|
|
143
|
+
for i, (content, style) in enumerate(parts):
|
|
144
|
+
if i > 0:
|
|
145
|
+
text.append(" │ ", style="dim")
|
|
146
|
+
text.append(content, style=style)
|
|
147
|
+
|
|
148
|
+
self.update(text)
|
|
149
|
+
|
|
150
|
+
def update_info(self, info: StatusInfo) -> None:
|
|
151
|
+
"""Update all status info at once."""
|
|
152
|
+
self.session_id = info.session_id
|
|
153
|
+
self.model = info.model
|
|
154
|
+
self.total_tokens = info.total_tokens
|
|
155
|
+
self.total_cost = info.total_cost
|
|
156
|
+
self.queued_count = info.queued_count
|
|
157
|
+
self.running_count = info.running_count
|
|
158
|
+
self.is_processing = info.is_processing
|
|
159
|
+
self.status_message = info.status_message
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
class StatusWidget:
|
|
163
|
+
"""Placeholder when Textual is not available."""
|
|
164
|
+
def __init__(self, *args, **kwargs):
|
|
165
|
+
raise ImportError(
|
|
166
|
+
"Textual is required for TUI. Install with: pip install praisonai[tui]"
|
|
167
|
+
)
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tool Panel Widget for PraisonAI TUI.
|
|
3
|
+
|
|
4
|
+
Displays tool execution status and approval dialogs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from textual.widget import Widget
|
|
13
|
+
from textual.widgets import Static, ListView, ListItem
|
|
14
|
+
from textual.containers import Vertical
|
|
15
|
+
from textual.reactive import reactive
|
|
16
|
+
from textual.message import Message
|
|
17
|
+
from rich.text import Text
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
TEXTUAL_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
TEXTUAL_AVAILABLE = False
|
|
22
|
+
Widget = object
|
|
23
|
+
Message = object
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ToolCall:
|
|
28
|
+
"""A tool call for display."""
|
|
29
|
+
call_id: str
|
|
30
|
+
tool_name: str
|
|
31
|
+
args_preview: str
|
|
32
|
+
status: str # "pending", "running", "completed", "failed", "approved", "rejected"
|
|
33
|
+
result_preview: Optional[str] = None
|
|
34
|
+
started_at: float = field(default_factory=time.time)
|
|
35
|
+
ended_at: Optional[float] = None
|
|
36
|
+
requires_approval: bool = False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if TEXTUAL_AVAILABLE:
|
|
40
|
+
class ToolPanelWidget(Vertical):
|
|
41
|
+
"""
|
|
42
|
+
Tool panel widget.
|
|
43
|
+
|
|
44
|
+
Displays:
|
|
45
|
+
- Recent tool calls
|
|
46
|
+
- Tool execution status
|
|
47
|
+
- Approval requests
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
DEFAULT_CSS = """
|
|
51
|
+
ToolPanelWidget {
|
|
52
|
+
height: 100%;
|
|
53
|
+
border: solid $accent;
|
|
54
|
+
background: $surface;
|
|
55
|
+
padding: 0 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
ToolPanelWidget .tool-header {
|
|
59
|
+
height: 1;
|
|
60
|
+
background: $accent;
|
|
61
|
+
padding: 0 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ToolPanelWidget .tool-pending {
|
|
65
|
+
color: $warning;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
ToolPanelWidget .tool-running {
|
|
69
|
+
color: $primary;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
ToolPanelWidget .tool-completed {
|
|
73
|
+
color: $success;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
ToolPanelWidget .tool-failed {
|
|
77
|
+
color: $error;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ToolPanelWidget .tool-approval {
|
|
81
|
+
background: $warning-darken-2;
|
|
82
|
+
border: solid $warning;
|
|
83
|
+
padding: 1;
|
|
84
|
+
}
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
class ApprovalResponse(Message):
|
|
88
|
+
"""Event when user responds to approval request."""
|
|
89
|
+
def __init__(self, call_id: str, approved: bool):
|
|
90
|
+
self.call_id = call_id
|
|
91
|
+
self.approved = approved
|
|
92
|
+
super().__init__()
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
max_items: int = 50,
|
|
97
|
+
name: Optional[str] = None,
|
|
98
|
+
id: Optional[str] = None,
|
|
99
|
+
classes: Optional[str] = None,
|
|
100
|
+
):
|
|
101
|
+
super().__init__(name=name, id=id, classes=classes)
|
|
102
|
+
self._calls: List[ToolCall] = []
|
|
103
|
+
self._max_items = max_items
|
|
104
|
+
self._pending_approval: Optional[ToolCall] = None
|
|
105
|
+
|
|
106
|
+
def compose(self):
|
|
107
|
+
"""Compose the widget."""
|
|
108
|
+
yield Static("Tools", id="tool-title", classes="tool-header")
|
|
109
|
+
yield Vertical(id="tool-list")
|
|
110
|
+
|
|
111
|
+
def on_mount(self) -> None:
|
|
112
|
+
"""Handle mount."""
|
|
113
|
+
self._update_display()
|
|
114
|
+
|
|
115
|
+
def add_call(self, call: ToolCall) -> None:
|
|
116
|
+
"""Add a tool call."""
|
|
117
|
+
self._calls.append(call)
|
|
118
|
+
|
|
119
|
+
# Trim old calls
|
|
120
|
+
if len(self._calls) > self._max_items:
|
|
121
|
+
self._calls = self._calls[-self._max_items:]
|
|
122
|
+
|
|
123
|
+
# Check for approval
|
|
124
|
+
if call.requires_approval and call.status == "pending":
|
|
125
|
+
self._pending_approval = call
|
|
126
|
+
|
|
127
|
+
self._update_display()
|
|
128
|
+
|
|
129
|
+
def update_call(self, call_id: str, status: str, result: Optional[str] = None) -> None:
|
|
130
|
+
"""Update a tool call status."""
|
|
131
|
+
for call in self._calls:
|
|
132
|
+
if call.call_id == call_id:
|
|
133
|
+
call.status = status
|
|
134
|
+
if result:
|
|
135
|
+
call.result_preview = result[:100]
|
|
136
|
+
if status in ("completed", "failed"):
|
|
137
|
+
call.ended_at = time.time()
|
|
138
|
+
break
|
|
139
|
+
|
|
140
|
+
# Clear pending approval if this was it
|
|
141
|
+
if self._pending_approval and self._pending_approval.call_id == call_id:
|
|
142
|
+
self._pending_approval = None
|
|
143
|
+
|
|
144
|
+
self._update_display()
|
|
145
|
+
|
|
146
|
+
def _update_display(self) -> None:
|
|
147
|
+
"""Update the display."""
|
|
148
|
+
try:
|
|
149
|
+
container = self.query_one("#tool-list", Vertical)
|
|
150
|
+
except Exception:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
# Clear existing
|
|
154
|
+
container.remove_children()
|
|
155
|
+
|
|
156
|
+
# Show pending approval first
|
|
157
|
+
if self._pending_approval:
|
|
158
|
+
approval_text = Text()
|
|
159
|
+
approval_text.append("⚠ Approval Required\n", style="bold yellow")
|
|
160
|
+
approval_text.append(f"Tool: {self._pending_approval.tool_name}\n")
|
|
161
|
+
approval_text.append(f"Args: {self._pending_approval.args_preview}\n")
|
|
162
|
+
approval_text.append("[Y] Approve [N] Reject", style="dim")
|
|
163
|
+
|
|
164
|
+
container.mount(Static(
|
|
165
|
+
Panel(approval_text, border_style="yellow"),
|
|
166
|
+
classes="tool-approval"
|
|
167
|
+
))
|
|
168
|
+
|
|
169
|
+
# Show recent calls
|
|
170
|
+
for call in reversed(self._calls[-10:]):
|
|
171
|
+
if call == self._pending_approval:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
# Status icon
|
|
175
|
+
status_icons = {
|
|
176
|
+
"pending": "⏳",
|
|
177
|
+
"running": "⟳",
|
|
178
|
+
"completed": "✓",
|
|
179
|
+
"failed": "✗",
|
|
180
|
+
"approved": "✓",
|
|
181
|
+
"rejected": "✗",
|
|
182
|
+
}
|
|
183
|
+
icon = status_icons.get(call.status, "?")
|
|
184
|
+
|
|
185
|
+
# Status style
|
|
186
|
+
status_styles = {
|
|
187
|
+
"pending": "yellow",
|
|
188
|
+
"running": "cyan",
|
|
189
|
+
"completed": "green",
|
|
190
|
+
"failed": "red",
|
|
191
|
+
"approved": "green",
|
|
192
|
+
"rejected": "red",
|
|
193
|
+
}
|
|
194
|
+
style = status_styles.get(call.status, "")
|
|
195
|
+
|
|
196
|
+
text = Text()
|
|
197
|
+
text.append(f"{icon} ", style=style)
|
|
198
|
+
text.append(call.tool_name, style="bold")
|
|
199
|
+
|
|
200
|
+
if call.result_preview:
|
|
201
|
+
text.append(f" → {call.result_preview[:30]}...", style="dim")
|
|
202
|
+
|
|
203
|
+
container.mount(Static(text, classes=f"tool-{call.status}"))
|
|
204
|
+
|
|
205
|
+
def handle_approval_key(self, key: str) -> bool:
|
|
206
|
+
"""
|
|
207
|
+
Handle approval key press.
|
|
208
|
+
|
|
209
|
+
Returns True if handled.
|
|
210
|
+
"""
|
|
211
|
+
if not self._pending_approval:
|
|
212
|
+
return False
|
|
213
|
+
|
|
214
|
+
if key.lower() == "y":
|
|
215
|
+
self.post_message(self.ApprovalResponse(
|
|
216
|
+
self._pending_approval.call_id,
|
|
217
|
+
approved=True
|
|
218
|
+
))
|
|
219
|
+
self.update_call(self._pending_approval.call_id, "approved")
|
|
220
|
+
return True
|
|
221
|
+
elif key.lower() == "n":
|
|
222
|
+
self.post_message(self.ApprovalResponse(
|
|
223
|
+
self._pending_approval.call_id,
|
|
224
|
+
approved=False
|
|
225
|
+
))
|
|
226
|
+
self.update_call(self._pending_approval.call_id, "rejected")
|
|
227
|
+
return True
|
|
228
|
+
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def has_pending_approval(self) -> bool:
|
|
233
|
+
"""Check if there's a pending approval."""
|
|
234
|
+
return self._pending_approval is not None
|
|
235
|
+
|
|
236
|
+
def clear(self) -> None:
|
|
237
|
+
"""Clear all tool calls."""
|
|
238
|
+
self._calls.clear()
|
|
239
|
+
self._pending_approval = None
|
|
240
|
+
self._update_display()
|
|
241
|
+
|
|
242
|
+
else:
|
|
243
|
+
class ToolPanelWidget:
|
|
244
|
+
"""Placeholder when Textual is not available."""
|
|
245
|
+
def __init__(self, *args, **kwargs):
|
|
246
|
+
raise ImportError(
|
|
247
|
+
"Textual is required for TUI. Install with: pip install praisonai[tui]"
|
|
248
|
+
)
|