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,395 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Queue Manager for PraisonAI.
|
|
3
|
+
|
|
4
|
+
High-level interface that coordinates scheduler, workers, and persistence.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
import time
|
|
11
|
+
from typing import Any, Callable, Coroutine, Dict, List, Optional
|
|
12
|
+
|
|
13
|
+
from .models import QueuedRun, RunState, RunPriority, QueueConfig, QueueStats, QueueEvent
|
|
14
|
+
from .scheduler import QueueScheduler, QueueFullError
|
|
15
|
+
from .worker import WorkerPool
|
|
16
|
+
from .persistence import QueuePersistence
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class QueueManager:
|
|
22
|
+
"""
|
|
23
|
+
High-level queue manager that coordinates all queue components.
|
|
24
|
+
|
|
25
|
+
Provides a unified interface for:
|
|
26
|
+
- Submitting and managing runs
|
|
27
|
+
- Starting/stopping workers
|
|
28
|
+
- Persistence and crash recovery
|
|
29
|
+
- Event handling
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
config: Optional[QueueConfig] = None,
|
|
35
|
+
on_output: Optional[Callable[[str, str], Coroutine[Any, Any, None]]] = None,
|
|
36
|
+
on_complete: Optional[Callable[[str, QueuedRun], Coroutine[Any, Any, None]]] = None,
|
|
37
|
+
on_error: Optional[Callable[[str, Exception], Coroutine[Any, Any, None]]] = None,
|
|
38
|
+
on_event: Optional[Callable[[QueueEvent], Coroutine[Any, Any, None]]] = None,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Initialize queue manager.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
config: Queue configuration.
|
|
45
|
+
on_output: Callback for streaming output.
|
|
46
|
+
on_complete: Callback when run completes.
|
|
47
|
+
on_error: Callback when run fails.
|
|
48
|
+
on_event: Callback for queue events.
|
|
49
|
+
"""
|
|
50
|
+
self.config = config or QueueConfig()
|
|
51
|
+
|
|
52
|
+
# Initialize components
|
|
53
|
+
self.scheduler = QueueScheduler(self.config)
|
|
54
|
+
self.persistence = QueuePersistence(self.config.db_path) if self.config.enable_persistence else None
|
|
55
|
+
|
|
56
|
+
# Store callbacks
|
|
57
|
+
self._on_output = on_output
|
|
58
|
+
self._on_complete = on_complete
|
|
59
|
+
self._on_error = on_error
|
|
60
|
+
self._on_event = on_event
|
|
61
|
+
|
|
62
|
+
# Worker pool (created on start)
|
|
63
|
+
self.workers: Optional[WorkerPool] = None
|
|
64
|
+
|
|
65
|
+
# Autosave task
|
|
66
|
+
self._autosave_task: Optional[asyncio.Task] = None
|
|
67
|
+
|
|
68
|
+
# Running state
|
|
69
|
+
self._running = False
|
|
70
|
+
|
|
71
|
+
# Session tracking
|
|
72
|
+
self._current_session_id: Optional[str] = None
|
|
73
|
+
|
|
74
|
+
async def start(self, recover: bool = True) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Start the queue manager.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
recover: If True, recover pending runs from persistence.
|
|
80
|
+
"""
|
|
81
|
+
if self._running:
|
|
82
|
+
return
|
|
83
|
+
|
|
84
|
+
logger.info("Starting queue manager")
|
|
85
|
+
|
|
86
|
+
# Initialize persistence
|
|
87
|
+
if self.persistence:
|
|
88
|
+
self.persistence.initialize()
|
|
89
|
+
|
|
90
|
+
if recover:
|
|
91
|
+
await self._recover_runs()
|
|
92
|
+
|
|
93
|
+
# Create and start worker pool
|
|
94
|
+
self.workers = WorkerPool(
|
|
95
|
+
scheduler=self.scheduler,
|
|
96
|
+
on_output=self._on_output,
|
|
97
|
+
on_complete=self._wrap_complete_callback(),
|
|
98
|
+
on_error=self._wrap_error_callback(),
|
|
99
|
+
on_event=self._on_event,
|
|
100
|
+
max_workers=self.config.max_concurrent_global,
|
|
101
|
+
poll_interval=self.config.worker_poll_interval,
|
|
102
|
+
stream_buffer_size=self.config.stream_buffer_size,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
await self.workers.start()
|
|
106
|
+
|
|
107
|
+
# Start autosave
|
|
108
|
+
if self.persistence and self.config.autosave_interval_seconds > 0:
|
|
109
|
+
self._autosave_task = asyncio.create_task(
|
|
110
|
+
self._autosave_loop(),
|
|
111
|
+
name="queue-autosave"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
self._running = True
|
|
115
|
+
logger.info("Queue manager started")
|
|
116
|
+
|
|
117
|
+
async def stop(self, timeout: float = 10.0) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Stop the queue manager gracefully.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
timeout: Seconds to wait for workers to finish.
|
|
123
|
+
"""
|
|
124
|
+
if not self._running:
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
logger.info("Stopping queue manager")
|
|
128
|
+
|
|
129
|
+
# Stop autosave
|
|
130
|
+
if self._autosave_task:
|
|
131
|
+
self._autosave_task.cancel()
|
|
132
|
+
try:
|
|
133
|
+
await self._autosave_task
|
|
134
|
+
except asyncio.CancelledError:
|
|
135
|
+
pass
|
|
136
|
+
self._autosave_task = None
|
|
137
|
+
|
|
138
|
+
# Stop workers
|
|
139
|
+
if self.workers:
|
|
140
|
+
await self.workers.stop(timeout=timeout)
|
|
141
|
+
self.workers = None
|
|
142
|
+
|
|
143
|
+
# Final save
|
|
144
|
+
if self.persistence:
|
|
145
|
+
await self._save_all_runs()
|
|
146
|
+
self.persistence.close()
|
|
147
|
+
|
|
148
|
+
self._running = False
|
|
149
|
+
logger.info("Queue manager stopped")
|
|
150
|
+
|
|
151
|
+
async def submit(
|
|
152
|
+
self,
|
|
153
|
+
input_content: str,
|
|
154
|
+
agent_name: str = "Assistant",
|
|
155
|
+
priority: RunPriority = RunPriority.NORMAL,
|
|
156
|
+
session_id: Optional[str] = None,
|
|
157
|
+
workspace: Optional[str] = None,
|
|
158
|
+
config: Optional[Dict[str, Any]] = None,
|
|
159
|
+
run_id: Optional[str] = None,
|
|
160
|
+
) -> str:
|
|
161
|
+
"""
|
|
162
|
+
Submit a new run to the queue.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
input_content: The input/prompt for the agent.
|
|
166
|
+
agent_name: Name of the agent to use.
|
|
167
|
+
priority: Run priority.
|
|
168
|
+
session_id: Session ID for grouping.
|
|
169
|
+
workspace: Workspace path.
|
|
170
|
+
config: Additional configuration.
|
|
171
|
+
run_id: Optional specific run ID.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
The run ID.
|
|
175
|
+
"""
|
|
176
|
+
run = QueuedRun(
|
|
177
|
+
agent_name=agent_name,
|
|
178
|
+
input_content=input_content,
|
|
179
|
+
priority=priority,
|
|
180
|
+
session_id=session_id or self._current_session_id,
|
|
181
|
+
workspace=workspace or os.getcwd(),
|
|
182
|
+
config=config or {},
|
|
183
|
+
max_retries=self.config.default_max_retries,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if run_id:
|
|
187
|
+
run.run_id = run_id
|
|
188
|
+
|
|
189
|
+
# Submit to scheduler
|
|
190
|
+
result_id = await self.scheduler.submit(run)
|
|
191
|
+
|
|
192
|
+
# Persist
|
|
193
|
+
if self.persistence:
|
|
194
|
+
self.persistence.save_run(run)
|
|
195
|
+
|
|
196
|
+
return result_id
|
|
197
|
+
|
|
198
|
+
async def cancel(self, run_id: str) -> bool:
|
|
199
|
+
"""Cancel a run."""
|
|
200
|
+
result = await self.scheduler.cancel(run_id)
|
|
201
|
+
|
|
202
|
+
if result and self.persistence:
|
|
203
|
+
run = self.scheduler.get_run(run_id)
|
|
204
|
+
if run:
|
|
205
|
+
self.persistence.save_run(run)
|
|
206
|
+
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
async def retry(self, run_id: str) -> Optional[str]:
|
|
210
|
+
"""Retry a failed run."""
|
|
211
|
+
new_id = await self.scheduler.retry(run_id)
|
|
212
|
+
|
|
213
|
+
if new_id and self.persistence:
|
|
214
|
+
run = self.scheduler.get_run(new_id)
|
|
215
|
+
if run:
|
|
216
|
+
self.persistence.save_run(run)
|
|
217
|
+
|
|
218
|
+
return new_id
|
|
219
|
+
|
|
220
|
+
async def pause(self, run_id: str) -> bool:
|
|
221
|
+
"""Pause a running run."""
|
|
222
|
+
return await self.scheduler.pause(run_id)
|
|
223
|
+
|
|
224
|
+
async def resume(self, run_id: str) -> bool:
|
|
225
|
+
"""Resume a paused run."""
|
|
226
|
+
return await self.scheduler.resume(run_id)
|
|
227
|
+
|
|
228
|
+
def get_run(self, run_id: str) -> Optional[QueuedRun]:
|
|
229
|
+
"""Get a run by ID."""
|
|
230
|
+
return self.scheduler.get_run(run_id)
|
|
231
|
+
|
|
232
|
+
def get_queued(self) -> List[QueuedRun]:
|
|
233
|
+
"""Get all queued runs."""
|
|
234
|
+
return self.scheduler.get_queued()
|
|
235
|
+
|
|
236
|
+
def get_running(self) -> List[QueuedRun]:
|
|
237
|
+
"""Get all running runs."""
|
|
238
|
+
return self.scheduler.get_running()
|
|
239
|
+
|
|
240
|
+
def list_runs(
|
|
241
|
+
self,
|
|
242
|
+
state: Optional[RunState] = None,
|
|
243
|
+
session_id: Optional[str] = None,
|
|
244
|
+
limit: int = 100,
|
|
245
|
+
) -> List[QueuedRun]:
|
|
246
|
+
"""
|
|
247
|
+
List runs with optional filters.
|
|
248
|
+
|
|
249
|
+
For active runs, uses scheduler. For historical, uses persistence.
|
|
250
|
+
"""
|
|
251
|
+
if state and state.is_terminal() and self.persistence:
|
|
252
|
+
return self.persistence.list_runs(
|
|
253
|
+
state=state,
|
|
254
|
+
session_id=session_id,
|
|
255
|
+
limit=limit,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Get from scheduler
|
|
259
|
+
runs = self.scheduler.get_all()
|
|
260
|
+
|
|
261
|
+
if state:
|
|
262
|
+
runs = [r for r in runs if r.state == state]
|
|
263
|
+
if session_id:
|
|
264
|
+
runs = [r for r in runs if r.session_id == session_id]
|
|
265
|
+
|
|
266
|
+
return runs[:limit]
|
|
267
|
+
|
|
268
|
+
async def clear_queue(self) -> int:
|
|
269
|
+
"""Clear all queued runs."""
|
|
270
|
+
count = await self.scheduler.clear_queue()
|
|
271
|
+
return count
|
|
272
|
+
|
|
273
|
+
def get_stats(self) -> QueueStats:
|
|
274
|
+
"""Get queue statistics."""
|
|
275
|
+
if self.persistence:
|
|
276
|
+
return self.persistence.get_stats(self._current_session_id)
|
|
277
|
+
|
|
278
|
+
# Calculate from scheduler
|
|
279
|
+
queued = self.scheduler.get_queued()
|
|
280
|
+
running = self.scheduler.get_running()
|
|
281
|
+
|
|
282
|
+
return QueueStats(
|
|
283
|
+
queued_count=len(queued),
|
|
284
|
+
running_count=len(running),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
# Session management
|
|
288
|
+
|
|
289
|
+
def set_session(self, session_id: str) -> None:
|
|
290
|
+
"""Set the current session ID."""
|
|
291
|
+
self._current_session_id = session_id
|
|
292
|
+
|
|
293
|
+
def save_session_state(self, state: Dict[str, Any]) -> None:
|
|
294
|
+
"""Save session state."""
|
|
295
|
+
if self.persistence and self._current_session_id:
|
|
296
|
+
self.persistence.save_session(
|
|
297
|
+
self._current_session_id,
|
|
298
|
+
state=state,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
def load_session_state(self, session_id: str) -> Optional[Dict[str, Any]]:
|
|
302
|
+
"""Load session state."""
|
|
303
|
+
if self.persistence:
|
|
304
|
+
return self.persistence.load_session(session_id)
|
|
305
|
+
return None
|
|
306
|
+
|
|
307
|
+
def list_sessions(self, limit: int = 50) -> List[Dict[str, Any]]:
|
|
308
|
+
"""List recent sessions."""
|
|
309
|
+
if self.persistence:
|
|
310
|
+
return self.persistence.list_sessions(limit)
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
# Internal methods
|
|
314
|
+
|
|
315
|
+
async def _recover_runs(self) -> None:
|
|
316
|
+
"""Recover pending runs from persistence."""
|
|
317
|
+
if not self.persistence:
|
|
318
|
+
return
|
|
319
|
+
|
|
320
|
+
# Mark interrupted runs as failed
|
|
321
|
+
interrupted = self.persistence.mark_interrupted_as_failed()
|
|
322
|
+
if interrupted:
|
|
323
|
+
logger.warning(f"Marked {interrupted} interrupted runs as failed")
|
|
324
|
+
|
|
325
|
+
# Load pending runs
|
|
326
|
+
pending = self.persistence.load_pending_runs()
|
|
327
|
+
if pending:
|
|
328
|
+
logger.info(f"Recovering {len(pending)} pending runs")
|
|
329
|
+
self.scheduler.load_runs(pending)
|
|
330
|
+
|
|
331
|
+
for run in pending:
|
|
332
|
+
self.persistence.mark_recovered(run.run_id)
|
|
333
|
+
|
|
334
|
+
async def _save_all_runs(self) -> None:
|
|
335
|
+
"""Save all active runs to persistence."""
|
|
336
|
+
if not self.persistence:
|
|
337
|
+
return
|
|
338
|
+
|
|
339
|
+
for run in self.scheduler.get_all():
|
|
340
|
+
self.persistence.save_run(run)
|
|
341
|
+
|
|
342
|
+
async def _autosave_loop(self) -> None:
|
|
343
|
+
"""Periodically save runs to persistence."""
|
|
344
|
+
while True:
|
|
345
|
+
try:
|
|
346
|
+
await asyncio.sleep(self.config.autosave_interval_seconds)
|
|
347
|
+
await self._save_all_runs()
|
|
348
|
+
logger.debug("Autosaved queue state")
|
|
349
|
+
except asyncio.CancelledError:
|
|
350
|
+
break
|
|
351
|
+
except Exception as e:
|
|
352
|
+
logger.error(f"Autosave error: {e}")
|
|
353
|
+
|
|
354
|
+
def _wrap_complete_callback(self):
|
|
355
|
+
"""Wrap complete callback to include persistence."""
|
|
356
|
+
async def wrapper(run_id: str, run: QueuedRun):
|
|
357
|
+
# Persist
|
|
358
|
+
if self.persistence:
|
|
359
|
+
self.persistence.save_run(run)
|
|
360
|
+
|
|
361
|
+
# Call user callback
|
|
362
|
+
if self._on_complete:
|
|
363
|
+
await self._on_complete(run_id, run)
|
|
364
|
+
|
|
365
|
+
return wrapper
|
|
366
|
+
|
|
367
|
+
def _wrap_error_callback(self):
|
|
368
|
+
"""Wrap error callback to include persistence."""
|
|
369
|
+
async def wrapper(run_id: str, error: Exception):
|
|
370
|
+
# Persist
|
|
371
|
+
if self.persistence:
|
|
372
|
+
run = self.scheduler.get_run(run_id)
|
|
373
|
+
if run:
|
|
374
|
+
self.persistence.save_run(run)
|
|
375
|
+
|
|
376
|
+
# Call user callback
|
|
377
|
+
if self._on_error:
|
|
378
|
+
await self._on_error(run_id, error)
|
|
379
|
+
|
|
380
|
+
return wrapper
|
|
381
|
+
|
|
382
|
+
@property
|
|
383
|
+
def is_running(self) -> bool:
|
|
384
|
+
"""Whether the queue manager is running."""
|
|
385
|
+
return self._running
|
|
386
|
+
|
|
387
|
+
@property
|
|
388
|
+
def queued_count(self) -> int:
|
|
389
|
+
"""Number of queued runs."""
|
|
390
|
+
return self.scheduler.queued_count
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
def running_count(self) -> int:
|
|
394
|
+
"""Number of running runs."""
|
|
395
|
+
return self.scheduler.running_count
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Data models for the PraisonAI Queue System.
|
|
3
|
+
|
|
4
|
+
Defines QueuedRun, RunState, RunPriority, and QueueConfig.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum, IntEnum
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
import time
|
|
11
|
+
import uuid
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RunState(str, Enum):
|
|
15
|
+
"""State of a queued run."""
|
|
16
|
+
QUEUED = "queued"
|
|
17
|
+
RUNNING = "running"
|
|
18
|
+
PAUSED = "paused"
|
|
19
|
+
SUCCEEDED = "succeeded"
|
|
20
|
+
FAILED = "failed"
|
|
21
|
+
CANCELLED = "cancelled"
|
|
22
|
+
|
|
23
|
+
def is_terminal(self) -> bool:
|
|
24
|
+
"""Check if this is a terminal state."""
|
|
25
|
+
return self in (RunState.SUCCEEDED, RunState.FAILED, RunState.CANCELLED)
|
|
26
|
+
|
|
27
|
+
def is_active(self) -> bool:
|
|
28
|
+
"""Check if this is an active (non-terminal) state."""
|
|
29
|
+
return not self.is_terminal()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class RunPriority(IntEnum):
|
|
33
|
+
"""Priority levels for queued runs. Higher value = higher priority."""
|
|
34
|
+
LOW = 0
|
|
35
|
+
NORMAL = 1
|
|
36
|
+
HIGH = 2
|
|
37
|
+
URGENT = 3
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_string(cls, s: str) -> "RunPriority":
|
|
41
|
+
"""Parse priority from string."""
|
|
42
|
+
mapping = {
|
|
43
|
+
"low": cls.LOW,
|
|
44
|
+
"normal": cls.NORMAL,
|
|
45
|
+
"high": cls.HIGH,
|
|
46
|
+
"urgent": cls.URGENT,
|
|
47
|
+
}
|
|
48
|
+
return mapping.get(s.lower(), cls.NORMAL)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class QueuedRun:
|
|
53
|
+
"""A single queued agent run."""
|
|
54
|
+
|
|
55
|
+
# Identity
|
|
56
|
+
run_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
|
|
57
|
+
agent_name: str = ""
|
|
58
|
+
input_content: str = ""
|
|
59
|
+
|
|
60
|
+
# State
|
|
61
|
+
state: RunState = RunState.QUEUED
|
|
62
|
+
priority: RunPriority = RunPriority.NORMAL
|
|
63
|
+
|
|
64
|
+
# Attribution / tracing
|
|
65
|
+
session_id: Optional[str] = None
|
|
66
|
+
trace_id: Optional[str] = None
|
|
67
|
+
workspace: Optional[str] = None
|
|
68
|
+
user_id: Optional[str] = None
|
|
69
|
+
|
|
70
|
+
# Timing
|
|
71
|
+
created_at: float = field(default_factory=time.time)
|
|
72
|
+
started_at: Optional[float] = None
|
|
73
|
+
ended_at: Optional[float] = None
|
|
74
|
+
|
|
75
|
+
# Results
|
|
76
|
+
output_content: Optional[str] = None
|
|
77
|
+
error: Optional[str] = None
|
|
78
|
+
metrics: Dict[str, Any] = field(default_factory=dict)
|
|
79
|
+
|
|
80
|
+
# Retry tracking
|
|
81
|
+
retry_count: int = 0
|
|
82
|
+
max_retries: int = 3
|
|
83
|
+
parent_run_id: Optional[str] = None # Link to original run if this is a retry
|
|
84
|
+
|
|
85
|
+
# Configuration
|
|
86
|
+
config: Dict[str, Any] = field(default_factory=dict)
|
|
87
|
+
|
|
88
|
+
# Streaming state
|
|
89
|
+
output_chunks: List[str] = field(default_factory=list)
|
|
90
|
+
|
|
91
|
+
def __post_init__(self):
|
|
92
|
+
"""Ensure proper types after initialization."""
|
|
93
|
+
if isinstance(self.state, str):
|
|
94
|
+
self.state = RunState(self.state)
|
|
95
|
+
if isinstance(self.priority, int) and not isinstance(self.priority, RunPriority):
|
|
96
|
+
self.priority = RunPriority(self.priority)
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def duration_seconds(self) -> Optional[float]:
|
|
100
|
+
"""Get run duration in seconds."""
|
|
101
|
+
if self.started_at is None:
|
|
102
|
+
return None
|
|
103
|
+
end = self.ended_at or time.time()
|
|
104
|
+
return end - self.started_at
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def wait_seconds(self) -> float:
|
|
108
|
+
"""Get time spent waiting in queue."""
|
|
109
|
+
start = self.started_at or time.time()
|
|
110
|
+
return start - self.created_at
|
|
111
|
+
|
|
112
|
+
def can_retry(self) -> bool:
|
|
113
|
+
"""Check if this run can be retried."""
|
|
114
|
+
return (
|
|
115
|
+
self.state == RunState.FAILED and
|
|
116
|
+
self.retry_count < self.max_retries
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
120
|
+
"""Convert to dictionary for persistence."""
|
|
121
|
+
return {
|
|
122
|
+
"run_id": self.run_id,
|
|
123
|
+
"agent_name": self.agent_name,
|
|
124
|
+
"input_content": self.input_content,
|
|
125
|
+
"state": self.state.value,
|
|
126
|
+
"priority": int(self.priority),
|
|
127
|
+
"session_id": self.session_id,
|
|
128
|
+
"trace_id": self.trace_id,
|
|
129
|
+
"workspace": self.workspace,
|
|
130
|
+
"user_id": self.user_id,
|
|
131
|
+
"created_at": self.created_at,
|
|
132
|
+
"started_at": self.started_at,
|
|
133
|
+
"ended_at": self.ended_at,
|
|
134
|
+
"output_content": self.output_content,
|
|
135
|
+
"error": self.error,
|
|
136
|
+
"metrics": self.metrics,
|
|
137
|
+
"retry_count": self.retry_count,
|
|
138
|
+
"max_retries": self.max_retries,
|
|
139
|
+
"parent_run_id": self.parent_run_id,
|
|
140
|
+
"config": self.config,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@classmethod
|
|
144
|
+
def from_dict(cls, data: Dict[str, Any]) -> "QueuedRun":
|
|
145
|
+
"""Create from dictionary."""
|
|
146
|
+
# Handle state conversion
|
|
147
|
+
state = data.get("state", "queued")
|
|
148
|
+
if isinstance(state, str):
|
|
149
|
+
state = RunState(state)
|
|
150
|
+
|
|
151
|
+
# Handle priority conversion
|
|
152
|
+
priority = data.get("priority", 1)
|
|
153
|
+
if isinstance(priority, int):
|
|
154
|
+
priority = RunPriority(priority)
|
|
155
|
+
|
|
156
|
+
return cls(
|
|
157
|
+
run_id=data.get("run_id", str(uuid.uuid4())[:8]),
|
|
158
|
+
agent_name=data.get("agent_name", ""),
|
|
159
|
+
input_content=data.get("input_content", ""),
|
|
160
|
+
state=state,
|
|
161
|
+
priority=priority,
|
|
162
|
+
session_id=data.get("session_id"),
|
|
163
|
+
trace_id=data.get("trace_id"),
|
|
164
|
+
workspace=data.get("workspace"),
|
|
165
|
+
user_id=data.get("user_id"),
|
|
166
|
+
created_at=data.get("created_at", time.time()),
|
|
167
|
+
started_at=data.get("started_at"),
|
|
168
|
+
ended_at=data.get("ended_at"),
|
|
169
|
+
output_content=data.get("output_content"),
|
|
170
|
+
error=data.get("error"),
|
|
171
|
+
metrics=data.get("metrics", {}),
|
|
172
|
+
retry_count=data.get("retry_count", 0),
|
|
173
|
+
max_retries=data.get("max_retries", 3),
|
|
174
|
+
parent_run_id=data.get("parent_run_id"),
|
|
175
|
+
config=data.get("config", {}),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass
|
|
180
|
+
class QueueConfig:
|
|
181
|
+
"""Configuration for the queue system."""
|
|
182
|
+
|
|
183
|
+
# Concurrency limits
|
|
184
|
+
max_concurrent_global: int = 4
|
|
185
|
+
max_concurrent_per_agent: int = 2
|
|
186
|
+
max_concurrent_per_workspace: int = 4
|
|
187
|
+
|
|
188
|
+
# Queue limits
|
|
189
|
+
max_queue_size: int = 100
|
|
190
|
+
|
|
191
|
+
# Defaults
|
|
192
|
+
default_priority: RunPriority = RunPriority.NORMAL
|
|
193
|
+
default_max_retries: int = 3
|
|
194
|
+
|
|
195
|
+
# Persistence
|
|
196
|
+
enable_persistence: bool = True
|
|
197
|
+
db_path: str = ".praison/queue.db"
|
|
198
|
+
|
|
199
|
+
# Autosave
|
|
200
|
+
autosave_interval_seconds: float = 30.0
|
|
201
|
+
|
|
202
|
+
# Backpressure
|
|
203
|
+
stream_buffer_size: int = 1000
|
|
204
|
+
drop_strategy: str = "oldest" # "oldest" or "newest"
|
|
205
|
+
|
|
206
|
+
# Timeouts
|
|
207
|
+
run_timeout_seconds: Optional[float] = None # None = no timeout
|
|
208
|
+
worker_poll_interval: float = 0.1
|
|
209
|
+
|
|
210
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
211
|
+
"""Convert to dictionary."""
|
|
212
|
+
return {
|
|
213
|
+
"max_concurrent_global": self.max_concurrent_global,
|
|
214
|
+
"max_concurrent_per_agent": self.max_concurrent_per_agent,
|
|
215
|
+
"max_concurrent_per_workspace": self.max_concurrent_per_workspace,
|
|
216
|
+
"max_queue_size": self.max_queue_size,
|
|
217
|
+
"default_priority": int(self.default_priority),
|
|
218
|
+
"default_max_retries": self.default_max_retries,
|
|
219
|
+
"enable_persistence": self.enable_persistence,
|
|
220
|
+
"db_path": self.db_path,
|
|
221
|
+
"autosave_interval_seconds": self.autosave_interval_seconds,
|
|
222
|
+
"stream_buffer_size": self.stream_buffer_size,
|
|
223
|
+
"drop_strategy": self.drop_strategy,
|
|
224
|
+
"run_timeout_seconds": self.run_timeout_seconds,
|
|
225
|
+
"worker_poll_interval": self.worker_poll_interval,
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
def from_dict(cls, data: Dict[str, Any]) -> "QueueConfig":
|
|
230
|
+
"""Create from dictionary."""
|
|
231
|
+
priority = data.get("default_priority", 1)
|
|
232
|
+
if isinstance(priority, int):
|
|
233
|
+
priority = RunPriority(priority)
|
|
234
|
+
|
|
235
|
+
return cls(
|
|
236
|
+
max_concurrent_global=data.get("max_concurrent_global", 4),
|
|
237
|
+
max_concurrent_per_agent=data.get("max_concurrent_per_agent", 2),
|
|
238
|
+
max_concurrent_per_workspace=data.get("max_concurrent_per_workspace", 4),
|
|
239
|
+
max_queue_size=data.get("max_queue_size", 100),
|
|
240
|
+
default_priority=priority,
|
|
241
|
+
default_max_retries=data.get("default_max_retries", 3),
|
|
242
|
+
enable_persistence=data.get("enable_persistence", True),
|
|
243
|
+
db_path=data.get("db_path", ".praison/queue.db"),
|
|
244
|
+
autosave_interval_seconds=data.get("autosave_interval_seconds", 30.0),
|
|
245
|
+
stream_buffer_size=data.get("stream_buffer_size", 1000),
|
|
246
|
+
drop_strategy=data.get("drop_strategy", "oldest"),
|
|
247
|
+
run_timeout_seconds=data.get("run_timeout_seconds"),
|
|
248
|
+
worker_poll_interval=data.get("worker_poll_interval", 0.1),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@dataclass
|
|
253
|
+
class QueueStats:
|
|
254
|
+
"""Statistics about the queue."""
|
|
255
|
+
queued_count: int = 0
|
|
256
|
+
running_count: int = 0
|
|
257
|
+
succeeded_count: int = 0
|
|
258
|
+
failed_count: int = 0
|
|
259
|
+
cancelled_count: int = 0
|
|
260
|
+
total_runs: int = 0
|
|
261
|
+
avg_wait_seconds: float = 0.0
|
|
262
|
+
avg_duration_seconds: float = 0.0
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def active_count(self) -> int:
|
|
266
|
+
"""Count of active (non-terminal) runs."""
|
|
267
|
+
return self.queued_count + self.running_count
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
@dataclass
|
|
271
|
+
class StreamChunk:
|
|
272
|
+
"""A chunk of streaming output."""
|
|
273
|
+
run_id: str
|
|
274
|
+
content: str
|
|
275
|
+
timestamp: float = field(default_factory=time.time)
|
|
276
|
+
chunk_index: int = 0
|
|
277
|
+
is_final: bool = False
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
@dataclass
|
|
281
|
+
class QueueEvent:
|
|
282
|
+
"""An event from the queue system."""
|
|
283
|
+
event_type: str # "run_started", "run_completed", "run_failed", "output_chunk", "state_changed"
|
|
284
|
+
run_id: str
|
|
285
|
+
timestamp: float = field(default_factory=time.time)
|
|
286
|
+
data: Dict[str, Any] = field(default_factory=dict)
|