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,587 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Message Queue for Interactive Mode.
|
|
3
|
+
|
|
4
|
+
Provides FIFO message queuing while the AI agent is processing,
|
|
5
|
+
inspired by Claude Code, Windsurf Cascade, Cursor, and Gemini CLI.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Queue messages while agent is processing
|
|
9
|
+
- Auto-process queue when agent becomes idle
|
|
10
|
+
- Visual indicators for queue status
|
|
11
|
+
- Thread-safe operations
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import Callable, List, Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ============================================================================
|
|
22
|
+
# Enums
|
|
23
|
+
# ============================================================================
|
|
24
|
+
|
|
25
|
+
class ProcessingState(Enum):
|
|
26
|
+
"""State of the agent processing."""
|
|
27
|
+
IDLE = "idle"
|
|
28
|
+
PROCESSING = "processing"
|
|
29
|
+
WAITING_APPROVAL = "waiting_approval"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MessagePriority(Enum):
|
|
33
|
+
"""Priority levels for queued messages."""
|
|
34
|
+
LOW = "low"
|
|
35
|
+
NORMAL = "normal"
|
|
36
|
+
HIGH = "high"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ============================================================================
|
|
40
|
+
# Data Classes
|
|
41
|
+
# ============================================================================
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class QueuedMessage:
|
|
45
|
+
"""A message in the queue with metadata."""
|
|
46
|
+
content: str
|
|
47
|
+
timestamp: float = field(default_factory=time.time)
|
|
48
|
+
priority: MessagePriority = MessagePriority.NORMAL
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# ============================================================================
|
|
52
|
+
# MessageQueue - Thread-safe FIFO queue
|
|
53
|
+
# ============================================================================
|
|
54
|
+
|
|
55
|
+
class MessageQueue:
|
|
56
|
+
"""
|
|
57
|
+
Thread-safe FIFO message queue.
|
|
58
|
+
|
|
59
|
+
Provides basic queue operations with thread safety for concurrent access.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
def __init__(self):
|
|
63
|
+
self._messages: List[str] = []
|
|
64
|
+
self._lock = threading.Lock()
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def is_empty(self) -> bool:
|
|
68
|
+
"""Check if queue is empty."""
|
|
69
|
+
with self._lock:
|
|
70
|
+
return len(self._messages) == 0
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def count(self) -> int:
|
|
74
|
+
"""Get number of messages in queue."""
|
|
75
|
+
with self._lock:
|
|
76
|
+
return len(self._messages)
|
|
77
|
+
|
|
78
|
+
def add(self, message: str) -> bool:
|
|
79
|
+
"""
|
|
80
|
+
Add a message to the end of the queue.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
message: The message to add
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if message was added, False if empty/whitespace
|
|
87
|
+
"""
|
|
88
|
+
trimmed = message.strip() if message else ""
|
|
89
|
+
if not trimmed:
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
with self._lock:
|
|
93
|
+
self._messages.append(trimmed)
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
def pop(self) -> Optional[str]:
|
|
97
|
+
"""
|
|
98
|
+
Remove and return the first message (FIFO).
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
The first message, or None if queue is empty
|
|
102
|
+
"""
|
|
103
|
+
with self._lock:
|
|
104
|
+
if self._messages:
|
|
105
|
+
return self._messages.pop(0)
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
def peek(self) -> Optional[str]:
|
|
109
|
+
"""
|
|
110
|
+
View the first message without removing it.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
The first message, or None if queue is empty
|
|
114
|
+
"""
|
|
115
|
+
with self._lock:
|
|
116
|
+
if self._messages:
|
|
117
|
+
return self._messages[0]
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
def clear(self) -> None:
|
|
121
|
+
"""Remove all messages from the queue."""
|
|
122
|
+
with self._lock:
|
|
123
|
+
self._messages.clear()
|
|
124
|
+
|
|
125
|
+
def get_all(self) -> List[str]:
|
|
126
|
+
"""
|
|
127
|
+
Get all messages as a list (does not remove them).
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Copy of all messages in queue order
|
|
131
|
+
"""
|
|
132
|
+
with self._lock:
|
|
133
|
+
return self._messages.copy()
|
|
134
|
+
|
|
135
|
+
def remove_at(self, index: int) -> Optional[str]:
|
|
136
|
+
"""
|
|
137
|
+
Remove message at specific index.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
index: The index to remove (0-based)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The removed message, or None if index is invalid
|
|
144
|
+
"""
|
|
145
|
+
with self._lock:
|
|
146
|
+
if 0 <= index < len(self._messages):
|
|
147
|
+
return self._messages.pop(index)
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# ============================================================================
|
|
152
|
+
# StateManager - Processing state management
|
|
153
|
+
# ============================================================================
|
|
154
|
+
|
|
155
|
+
class StateManager:
|
|
156
|
+
"""
|
|
157
|
+
Manages the processing state with optional callbacks.
|
|
158
|
+
|
|
159
|
+
Tracks whether the agent is idle, processing, or waiting for approval.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, on_state_change: Optional[Callable[[ProcessingState, ProcessingState], None]] = None):
|
|
163
|
+
self._state = ProcessingState.IDLE
|
|
164
|
+
self._lock = threading.Lock()
|
|
165
|
+
self._on_state_change = on_state_change
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def current_state(self) -> ProcessingState:
|
|
169
|
+
"""Get current processing state."""
|
|
170
|
+
with self._lock:
|
|
171
|
+
return self._state
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def is_idle(self) -> bool:
|
|
175
|
+
"""Check if currently idle."""
|
|
176
|
+
return self.current_state == ProcessingState.IDLE
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def is_processing(self) -> bool:
|
|
180
|
+
"""Check if currently processing."""
|
|
181
|
+
return self.current_state == ProcessingState.PROCESSING
|
|
182
|
+
|
|
183
|
+
def set_state(self, new_state: ProcessingState) -> None:
|
|
184
|
+
"""
|
|
185
|
+
Set the processing state.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
new_state: The new state to set
|
|
189
|
+
"""
|
|
190
|
+
with self._lock:
|
|
191
|
+
old_state = self._state
|
|
192
|
+
self._state = new_state
|
|
193
|
+
|
|
194
|
+
# Call callback outside lock to prevent deadlock
|
|
195
|
+
if self._on_state_change and old_state != new_state:
|
|
196
|
+
self._on_state_change(old_state, new_state)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ============================================================================
|
|
200
|
+
# QueueDisplay - Visual formatting
|
|
201
|
+
# ============================================================================
|
|
202
|
+
|
|
203
|
+
class QueueDisplay:
|
|
204
|
+
"""
|
|
205
|
+
Formats queue and status for display.
|
|
206
|
+
|
|
207
|
+
Provides visual indicators for queue status, inspired by Codex CLI.
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
def __init__(
|
|
211
|
+
self,
|
|
212
|
+
queue: MessageQueue,
|
|
213
|
+
state_manager: Optional[StateManager] = None,
|
|
214
|
+
max_message_length: int = 50
|
|
215
|
+
):
|
|
216
|
+
self._queue = queue
|
|
217
|
+
self._state_manager = state_manager
|
|
218
|
+
self._max_length = max_message_length
|
|
219
|
+
|
|
220
|
+
def format_queue(self) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Format queued messages for display.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Formatted string with queued messages
|
|
226
|
+
"""
|
|
227
|
+
messages = self._queue.get_all()
|
|
228
|
+
if not messages:
|
|
229
|
+
return ""
|
|
230
|
+
|
|
231
|
+
lines = []
|
|
232
|
+
for msg in messages:
|
|
233
|
+
# Truncate long messages
|
|
234
|
+
display_msg = msg
|
|
235
|
+
if len(msg) > self._max_length:
|
|
236
|
+
display_msg = msg[:self._max_length - 3] + "..."
|
|
237
|
+
lines.append(f" ↳ {display_msg}")
|
|
238
|
+
|
|
239
|
+
return "\n".join(lines)
|
|
240
|
+
|
|
241
|
+
def format_status(self) -> str:
|
|
242
|
+
"""
|
|
243
|
+
Format current processing status.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Status string (e.g., "⏳ Processing..." or "")
|
|
247
|
+
"""
|
|
248
|
+
if not self._state_manager:
|
|
249
|
+
return ""
|
|
250
|
+
|
|
251
|
+
state = self._state_manager.current_state
|
|
252
|
+
if state == ProcessingState.PROCESSING:
|
|
253
|
+
return "⏳ Processing..."
|
|
254
|
+
elif state == ProcessingState.WAITING_APPROVAL:
|
|
255
|
+
return "🔒 Waiting for approval..."
|
|
256
|
+
return ""
|
|
257
|
+
|
|
258
|
+
def format_queue_count(self) -> str:
|
|
259
|
+
"""
|
|
260
|
+
Format queue count indicator.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
Count string (e.g., "📋 Queued (2)")
|
|
264
|
+
"""
|
|
265
|
+
count = self._queue.count
|
|
266
|
+
if count == 0:
|
|
267
|
+
return ""
|
|
268
|
+
return f"📋 Queued ({count})"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# ============================================================================
|
|
272
|
+
# MessageQueueHandler - Main handler class
|
|
273
|
+
# ============================================================================
|
|
274
|
+
|
|
275
|
+
class MessageQueueHandler:
|
|
276
|
+
"""
|
|
277
|
+
Main handler for message queue functionality.
|
|
278
|
+
|
|
279
|
+
Coordinates queue, state, and processing.
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
def __init__(
|
|
283
|
+
self,
|
|
284
|
+
processor: Optional[Callable[[str], str]] = None,
|
|
285
|
+
on_response: Optional[Callable[[str], None]] = None
|
|
286
|
+
):
|
|
287
|
+
self._processor = processor
|
|
288
|
+
self._on_response = on_response
|
|
289
|
+
self._queue = MessageQueue()
|
|
290
|
+
self._state_manager = StateManager(on_state_change=self._on_state_change)
|
|
291
|
+
self._processing_lock = threading.Lock()
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def queue(self) -> MessageQueue:
|
|
295
|
+
"""Get the message queue."""
|
|
296
|
+
return self._queue
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def state_manager(self) -> StateManager:
|
|
300
|
+
"""Get the state manager."""
|
|
301
|
+
return self._state_manager
|
|
302
|
+
|
|
303
|
+
def submit(self, message: str) -> bool:
|
|
304
|
+
"""
|
|
305
|
+
Submit a message for processing.
|
|
306
|
+
|
|
307
|
+
If idle, processes immediately. If processing, queues the message.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
message: The message to submit
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
True if processed/queued, False if empty message
|
|
314
|
+
"""
|
|
315
|
+
trimmed = message.strip() if message else ""
|
|
316
|
+
if not trimmed:
|
|
317
|
+
return False
|
|
318
|
+
|
|
319
|
+
if self._state_manager.is_idle:
|
|
320
|
+
# Process immediately
|
|
321
|
+
self._process_message(trimmed)
|
|
322
|
+
else:
|
|
323
|
+
# Queue for later
|
|
324
|
+
self._queue.add(trimmed)
|
|
325
|
+
|
|
326
|
+
return True
|
|
327
|
+
|
|
328
|
+
def on_processing_complete(self) -> None:
|
|
329
|
+
"""
|
|
330
|
+
Called when current processing completes.
|
|
331
|
+
|
|
332
|
+
Sets state to idle and processes next queued message if any.
|
|
333
|
+
"""
|
|
334
|
+
self._state_manager.set_state(ProcessingState.IDLE)
|
|
335
|
+
self._process_next_in_queue()
|
|
336
|
+
|
|
337
|
+
def get_status(self) -> dict:
|
|
338
|
+
"""
|
|
339
|
+
Get current status.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Dict with queue_count and state
|
|
343
|
+
"""
|
|
344
|
+
return {
|
|
345
|
+
'queue_count': self._queue.count,
|
|
346
|
+
'state': self._state_manager.current_state.value,
|
|
347
|
+
'messages': self._queue.get_all()
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
def clear_queue(self) -> None:
|
|
351
|
+
"""Clear all queued messages."""
|
|
352
|
+
self._queue.clear()
|
|
353
|
+
|
|
354
|
+
def _process_message(self, message: str) -> None:
|
|
355
|
+
"""Process a single message."""
|
|
356
|
+
if not self._processor:
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
with self._processing_lock:
|
|
360
|
+
self._state_manager.set_state(ProcessingState.PROCESSING)
|
|
361
|
+
try:
|
|
362
|
+
response = self._processor(message)
|
|
363
|
+
if self._on_response and response:
|
|
364
|
+
self._on_response(response)
|
|
365
|
+
finally:
|
|
366
|
+
self._state_manager.set_state(ProcessingState.IDLE)
|
|
367
|
+
|
|
368
|
+
def _process_next_in_queue(self) -> None:
|
|
369
|
+
"""Process the next message in queue if any."""
|
|
370
|
+
next_msg = self._queue.pop()
|
|
371
|
+
if next_msg:
|
|
372
|
+
self._process_message(next_msg)
|
|
373
|
+
|
|
374
|
+
def _on_state_change(self, old_state: ProcessingState, new_state: ProcessingState) -> None:
|
|
375
|
+
"""Handle state changes."""
|
|
376
|
+
# Auto-process queue when becoming idle
|
|
377
|
+
if new_state == ProcessingState.IDLE and old_state == ProcessingState.PROCESSING:
|
|
378
|
+
# Don't auto-process here to avoid recursion
|
|
379
|
+
# The on_processing_complete method handles this
|
|
380
|
+
pass
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
# ============================================================================
|
|
384
|
+
# AsyncProcessor - Background processing
|
|
385
|
+
# ============================================================================
|
|
386
|
+
|
|
387
|
+
class AsyncProcessor:
|
|
388
|
+
"""
|
|
389
|
+
Runs work functions in background threads.
|
|
390
|
+
|
|
391
|
+
Allows agent processing to happen asynchronously while
|
|
392
|
+
the main thread remains responsive for user input.
|
|
393
|
+
"""
|
|
394
|
+
|
|
395
|
+
def __init__(
|
|
396
|
+
self,
|
|
397
|
+
on_complete: Optional[Callable[[str], None]] = None,
|
|
398
|
+
on_status: Optional[Callable[[str], None]] = None,
|
|
399
|
+
on_error: Optional[Callable[[Exception], None]] = None
|
|
400
|
+
):
|
|
401
|
+
self._on_complete = on_complete
|
|
402
|
+
self._on_status = on_status
|
|
403
|
+
self._on_error = on_error
|
|
404
|
+
self._thread: Optional[threading.Thread] = None
|
|
405
|
+
self._running = False
|
|
406
|
+
self._lock = threading.Lock()
|
|
407
|
+
|
|
408
|
+
@property
|
|
409
|
+
def is_running(self) -> bool:
|
|
410
|
+
"""Check if currently processing."""
|
|
411
|
+
with self._lock:
|
|
412
|
+
return self._running
|
|
413
|
+
|
|
414
|
+
def start(self, work_fn: Callable[[], str]) -> None:
|
|
415
|
+
"""
|
|
416
|
+
Start processing work in background thread.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
work_fn: Function to execute (should return result string)
|
|
420
|
+
"""
|
|
421
|
+
def _worker():
|
|
422
|
+
try:
|
|
423
|
+
with self._lock:
|
|
424
|
+
self._running = True
|
|
425
|
+
|
|
426
|
+
result = work_fn()
|
|
427
|
+
|
|
428
|
+
with self._lock:
|
|
429
|
+
self._running = False
|
|
430
|
+
|
|
431
|
+
if self._on_complete:
|
|
432
|
+
self._on_complete(result)
|
|
433
|
+
except Exception as e:
|
|
434
|
+
with self._lock:
|
|
435
|
+
self._running = False
|
|
436
|
+
if self._on_error:
|
|
437
|
+
self._on_error(e)
|
|
438
|
+
|
|
439
|
+
self._thread = threading.Thread(target=_worker, daemon=True)
|
|
440
|
+
self._thread.start()
|
|
441
|
+
|
|
442
|
+
def update_status(self, status: str) -> None:
|
|
443
|
+
"""Update status (can be called from work function)."""
|
|
444
|
+
if self._on_status:
|
|
445
|
+
self._on_status(status)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# ============================================================================
|
|
449
|
+
# LiveStatusDisplay - Real-time status display
|
|
450
|
+
# ============================================================================
|
|
451
|
+
|
|
452
|
+
class LiveStatusDisplay:
|
|
453
|
+
"""
|
|
454
|
+
Tracks and displays real-time status during processing.
|
|
455
|
+
|
|
456
|
+
Shows tool calls, command executions, and current status.
|
|
457
|
+
"""
|
|
458
|
+
|
|
459
|
+
def __init__(self):
|
|
460
|
+
self._status = ""
|
|
461
|
+
self._tool_calls: List[dict] = []
|
|
462
|
+
self._commands: List[str] = []
|
|
463
|
+
self._lock = threading.Lock()
|
|
464
|
+
|
|
465
|
+
@property
|
|
466
|
+
def current_status(self) -> str:
|
|
467
|
+
"""Get current status message."""
|
|
468
|
+
with self._lock:
|
|
469
|
+
return self._status
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def tool_calls(self) -> List[dict]:
|
|
473
|
+
"""Get list of tool calls."""
|
|
474
|
+
with self._lock:
|
|
475
|
+
return self._tool_calls.copy()
|
|
476
|
+
|
|
477
|
+
@property
|
|
478
|
+
def commands(self) -> List[str]:
|
|
479
|
+
"""Get list of commands executed."""
|
|
480
|
+
with self._lock:
|
|
481
|
+
return self._commands.copy()
|
|
482
|
+
|
|
483
|
+
def update_status(self, status: str) -> None:
|
|
484
|
+
"""Update current status message."""
|
|
485
|
+
with self._lock:
|
|
486
|
+
self._status = status
|
|
487
|
+
|
|
488
|
+
def add_tool_call(self, name: str, args: dict) -> None:
|
|
489
|
+
"""Add a tool call to the display."""
|
|
490
|
+
with self._lock:
|
|
491
|
+
self._tool_calls.append({
|
|
492
|
+
'name': name,
|
|
493
|
+
'args': args,
|
|
494
|
+
'timestamp': time.time()
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
def add_command(self, command: str) -> None:
|
|
498
|
+
"""Add a command execution to the display."""
|
|
499
|
+
with self._lock:
|
|
500
|
+
self._commands.append(command)
|
|
501
|
+
|
|
502
|
+
def format(self) -> str:
|
|
503
|
+
"""Format current status for display."""
|
|
504
|
+
lines = []
|
|
505
|
+
|
|
506
|
+
with self._lock:
|
|
507
|
+
if self._status:
|
|
508
|
+
lines.append(f"⏳ {self._status}")
|
|
509
|
+
|
|
510
|
+
for tool in self._tool_calls[-3:]: # Show last 3 tools
|
|
511
|
+
lines.append(f" 🔧 {tool['name']}")
|
|
512
|
+
|
|
513
|
+
for cmd in self._commands[-2:]: # Show last 2 commands
|
|
514
|
+
display_cmd = cmd[:40] + "..." if len(cmd) > 40 else cmd
|
|
515
|
+
lines.append(f" 💻 {display_cmd}")
|
|
516
|
+
|
|
517
|
+
return "\n".join(lines)
|
|
518
|
+
|
|
519
|
+
def clear(self) -> None:
|
|
520
|
+
"""Clear all status."""
|
|
521
|
+
with self._lock:
|
|
522
|
+
self._status = ""
|
|
523
|
+
self._tool_calls.clear()
|
|
524
|
+
self._commands.clear()
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
# ============================================================================
|
|
528
|
+
# NonBlockingInput - Async input handling
|
|
529
|
+
# ============================================================================
|
|
530
|
+
|
|
531
|
+
class NonBlockingInput:
|
|
532
|
+
"""
|
|
533
|
+
Handles user input asynchronously.
|
|
534
|
+
|
|
535
|
+
Allows users to type new messages while processing is ongoing.
|
|
536
|
+
"""
|
|
537
|
+
|
|
538
|
+
def __init__(self):
|
|
539
|
+
self._queue = MessageQueue()
|
|
540
|
+
|
|
541
|
+
@property
|
|
542
|
+
def has_pending(self) -> bool:
|
|
543
|
+
"""Check if there are pending inputs."""
|
|
544
|
+
return not self._queue.is_empty
|
|
545
|
+
|
|
546
|
+
@property
|
|
547
|
+
def pending_count(self) -> int:
|
|
548
|
+
"""Get number of pending inputs."""
|
|
549
|
+
return self._queue.count
|
|
550
|
+
|
|
551
|
+
def submit(self, message: str) -> bool:
|
|
552
|
+
"""Submit a new input message."""
|
|
553
|
+
return self._queue.add(message)
|
|
554
|
+
|
|
555
|
+
def pop(self) -> Optional[str]:
|
|
556
|
+
"""Get next pending input."""
|
|
557
|
+
return self._queue.pop()
|
|
558
|
+
|
|
559
|
+
def peek(self) -> Optional[str]:
|
|
560
|
+
"""View next pending input without removing."""
|
|
561
|
+
return self._queue.peek()
|
|
562
|
+
|
|
563
|
+
def get_all(self) -> List[str]:
|
|
564
|
+
"""Get all pending inputs."""
|
|
565
|
+
return self._queue.get_all()
|
|
566
|
+
|
|
567
|
+
def clear(self) -> None:
|
|
568
|
+
"""Clear all pending inputs."""
|
|
569
|
+
self._queue.clear()
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
# ============================================================================
|
|
573
|
+
# Exports
|
|
574
|
+
# ============================================================================
|
|
575
|
+
|
|
576
|
+
__all__ = [
|
|
577
|
+
'ProcessingState',
|
|
578
|
+
'MessagePriority',
|
|
579
|
+
'QueuedMessage',
|
|
580
|
+
'MessageQueue',
|
|
581
|
+
'StateManager',
|
|
582
|
+
'QueueDisplay',
|
|
583
|
+
'MessageQueueHandler',
|
|
584
|
+
'AsyncProcessor',
|
|
585
|
+
'LiveStatusDisplay',
|
|
586
|
+
'NonBlockingInput',
|
|
587
|
+
]
|