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,372 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Worker Pool for PraisonAI Queue System.
|
|
3
|
+
|
|
4
|
+
Manages async workers that execute queued runs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import time
|
|
10
|
+
from collections import deque
|
|
11
|
+
from typing import Any, Callable, Coroutine, Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
from .models import QueuedRun, RunState, StreamChunk, QueueEvent
|
|
14
|
+
from .scheduler import QueueScheduler
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StreamBuffer:
|
|
20
|
+
"""Bounded buffer with backpressure for streaming output."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, max_size: int = 1000, drop_strategy: str = "oldest"):
|
|
23
|
+
"""
|
|
24
|
+
Initialize stream buffer.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
max_size: Maximum number of chunks to buffer.
|
|
28
|
+
drop_strategy: "oldest" to drop oldest, "newest" to reject new.
|
|
29
|
+
"""
|
|
30
|
+
self._buffer: deque = deque(maxlen=max_size)
|
|
31
|
+
self._lock = asyncio.Lock()
|
|
32
|
+
self._drop_strategy = drop_strategy
|
|
33
|
+
self._dropped_count = 0
|
|
34
|
+
self._max_size = max_size
|
|
35
|
+
|
|
36
|
+
async def push(self, chunk: StreamChunk) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Push a chunk to the buffer.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if added, False if dropped.
|
|
42
|
+
"""
|
|
43
|
+
async with self._lock:
|
|
44
|
+
if len(self._buffer) >= self._max_size:
|
|
45
|
+
if self._drop_strategy == "oldest":
|
|
46
|
+
self._buffer.popleft()
|
|
47
|
+
self._dropped_count += 1
|
|
48
|
+
elif self._drop_strategy == "newest":
|
|
49
|
+
self._dropped_count += 1
|
|
50
|
+
return False
|
|
51
|
+
self._buffer.append(chunk)
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
async def drain(self, batch_size: int = 50) -> List[StreamChunk]:
|
|
55
|
+
"""Drain up to batch_size chunks."""
|
|
56
|
+
async with self._lock:
|
|
57
|
+
result = []
|
|
58
|
+
for _ in range(min(batch_size, len(self._buffer))):
|
|
59
|
+
result.append(self._buffer.popleft())
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
async def drain_all(self) -> List[StreamChunk]:
|
|
63
|
+
"""Drain all chunks."""
|
|
64
|
+
async with self._lock:
|
|
65
|
+
result = list(self._buffer)
|
|
66
|
+
self._buffer.clear()
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def size(self) -> int:
|
|
71
|
+
"""Current buffer size."""
|
|
72
|
+
return len(self._buffer)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def dropped_count(self) -> int:
|
|
76
|
+
"""Number of dropped chunks."""
|
|
77
|
+
return self._dropped_count
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class WorkerPool:
|
|
81
|
+
"""
|
|
82
|
+
Manages worker tasks for executing queued runs.
|
|
83
|
+
|
|
84
|
+
Workers poll the scheduler for runs and execute them with streaming output.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
scheduler: QueueScheduler,
|
|
90
|
+
on_output: Optional[Callable[[str, str], Coroutine[Any, Any, None]]] = None,
|
|
91
|
+
on_complete: Optional[Callable[[str, QueuedRun], Coroutine[Any, Any, None]]] = None,
|
|
92
|
+
on_error: Optional[Callable[[str, Exception], Coroutine[Any, Any, None]]] = None,
|
|
93
|
+
on_event: Optional[Callable[[QueueEvent], Coroutine[Any, Any, None]]] = None,
|
|
94
|
+
max_workers: int = 4,
|
|
95
|
+
poll_interval: float = 0.1,
|
|
96
|
+
stream_buffer_size: int = 1000,
|
|
97
|
+
):
|
|
98
|
+
"""
|
|
99
|
+
Initialize worker pool.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
scheduler: The queue scheduler.
|
|
103
|
+
on_output: Callback for streaming output (run_id, chunk).
|
|
104
|
+
on_complete: Callback when run completes (run_id, run).
|
|
105
|
+
on_error: Callback when run fails (run_id, exception).
|
|
106
|
+
on_event: Callback for queue events.
|
|
107
|
+
max_workers: Maximum number of concurrent workers.
|
|
108
|
+
poll_interval: Seconds between scheduler polls.
|
|
109
|
+
stream_buffer_size: Size of stream buffer per run.
|
|
110
|
+
"""
|
|
111
|
+
self.scheduler = scheduler
|
|
112
|
+
self.on_output = on_output
|
|
113
|
+
self.on_complete = on_complete
|
|
114
|
+
self.on_error = on_error
|
|
115
|
+
self.on_event = on_event
|
|
116
|
+
self.max_workers = max_workers
|
|
117
|
+
self.poll_interval = poll_interval
|
|
118
|
+
self.stream_buffer_size = stream_buffer_size
|
|
119
|
+
|
|
120
|
+
self._workers: List[asyncio.Task] = []
|
|
121
|
+
self._running = False
|
|
122
|
+
self._stream_buffers: Dict[str, StreamBuffer] = {}
|
|
123
|
+
self._active_runs: Set[str] = set()
|
|
124
|
+
|
|
125
|
+
async def start(self) -> None:
|
|
126
|
+
"""Start the worker pool."""
|
|
127
|
+
if self._running:
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
self._running = True
|
|
131
|
+
|
|
132
|
+
for i in range(self.max_workers):
|
|
133
|
+
task = asyncio.create_task(
|
|
134
|
+
self._worker_loop(i),
|
|
135
|
+
name=f"queue-worker-{i}"
|
|
136
|
+
)
|
|
137
|
+
self._workers.append(task)
|
|
138
|
+
|
|
139
|
+
logger.info(f"Started {self.max_workers} queue workers")
|
|
140
|
+
|
|
141
|
+
async def stop(self, timeout: float = 5.0) -> None:
|
|
142
|
+
"""
|
|
143
|
+
Stop all workers gracefully.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
timeout: Seconds to wait for workers to finish.
|
|
147
|
+
"""
|
|
148
|
+
self._running = False
|
|
149
|
+
|
|
150
|
+
if not self._workers:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
# Wait for workers to finish current work
|
|
154
|
+
try:
|
|
155
|
+
await asyncio.wait_for(
|
|
156
|
+
asyncio.gather(*self._workers, return_exceptions=True),
|
|
157
|
+
timeout=timeout
|
|
158
|
+
)
|
|
159
|
+
except asyncio.TimeoutError:
|
|
160
|
+
# Cancel remaining workers
|
|
161
|
+
for task in self._workers:
|
|
162
|
+
if not task.done():
|
|
163
|
+
task.cancel()
|
|
164
|
+
|
|
165
|
+
await asyncio.gather(*self._workers, return_exceptions=True)
|
|
166
|
+
|
|
167
|
+
self._workers.clear()
|
|
168
|
+
logger.info("Stopped queue workers")
|
|
169
|
+
|
|
170
|
+
async def _worker_loop(self, worker_id: int) -> None:
|
|
171
|
+
"""Main worker loop."""
|
|
172
|
+
logger.debug(f"Worker {worker_id} started")
|
|
173
|
+
|
|
174
|
+
while self._running:
|
|
175
|
+
try:
|
|
176
|
+
# Get next run from scheduler
|
|
177
|
+
run = await self.scheduler.next()
|
|
178
|
+
|
|
179
|
+
if run is None:
|
|
180
|
+
# No work available, wait and retry
|
|
181
|
+
await asyncio.sleep(self.poll_interval)
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
# Execute the run
|
|
185
|
+
self._active_runs.add(run.run_id)
|
|
186
|
+
try:
|
|
187
|
+
await self._execute_run(run, worker_id)
|
|
188
|
+
finally:
|
|
189
|
+
self._active_runs.discard(run.run_id)
|
|
190
|
+
|
|
191
|
+
except asyncio.CancelledError:
|
|
192
|
+
logger.debug(f"Worker {worker_id} cancelled")
|
|
193
|
+
break
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.error(f"Worker {worker_id} error: {e}", exc_info=True)
|
|
196
|
+
await asyncio.sleep(self.poll_interval)
|
|
197
|
+
|
|
198
|
+
logger.debug(f"Worker {worker_id} stopped")
|
|
199
|
+
|
|
200
|
+
async def _execute_run(self, run: QueuedRun, worker_id: int) -> None:
|
|
201
|
+
"""Execute a single run with streaming output."""
|
|
202
|
+
logger.debug(f"Worker {worker_id} executing run {run.run_id}")
|
|
203
|
+
|
|
204
|
+
# Create stream buffer for this run
|
|
205
|
+
buffer = StreamBuffer(max_size=self.stream_buffer_size)
|
|
206
|
+
self._stream_buffers[run.run_id] = buffer
|
|
207
|
+
|
|
208
|
+
output_chunks: List[str] = []
|
|
209
|
+
|
|
210
|
+
try:
|
|
211
|
+
# Import agent lazily to avoid circular imports
|
|
212
|
+
from praisonaiagents import Agent
|
|
213
|
+
|
|
214
|
+
# Get agent configuration
|
|
215
|
+
agent_config = run.config.get("agent_config", {})
|
|
216
|
+
agent_name = run.agent_name or agent_config.get("name", "Assistant")
|
|
217
|
+
|
|
218
|
+
# Create agent
|
|
219
|
+
agent = Agent(
|
|
220
|
+
name=agent_name,
|
|
221
|
+
instructions=agent_config.get("instructions", "You are a helpful assistant."),
|
|
222
|
+
model=agent_config.get("model"),
|
|
223
|
+
tools=agent_config.get("tools", []),
|
|
224
|
+
verbose=agent_config.get("verbose", False),
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
# Check for cancellation before starting
|
|
228
|
+
if self.scheduler.is_cancelled(run.run_id):
|
|
229
|
+
logger.debug(f"Run {run.run_id} was cancelled before execution")
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
# Execute with streaming if available
|
|
233
|
+
chunk_index = 0
|
|
234
|
+
|
|
235
|
+
# Try streaming first
|
|
236
|
+
try:
|
|
237
|
+
async for chunk in self._stream_agent(agent, run.input_content):
|
|
238
|
+
# Check for cancellation
|
|
239
|
+
if self.scheduler.is_cancelled(run.run_id):
|
|
240
|
+
logger.debug(f"Run {run.run_id} cancelled during execution")
|
|
241
|
+
self.scheduler.clear_cancel_token(run.run_id)
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
# Check for pause
|
|
245
|
+
while run.state == RunState.PAUSED:
|
|
246
|
+
await asyncio.sleep(0.1)
|
|
247
|
+
if self.scheduler.is_cancelled(run.run_id):
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
output_chunks.append(chunk)
|
|
251
|
+
|
|
252
|
+
# Buffer the chunk
|
|
253
|
+
stream_chunk = StreamChunk(
|
|
254
|
+
run_id=run.run_id,
|
|
255
|
+
content=chunk,
|
|
256
|
+
chunk_index=chunk_index,
|
|
257
|
+
)
|
|
258
|
+
await buffer.push(stream_chunk)
|
|
259
|
+
chunk_index += 1
|
|
260
|
+
|
|
261
|
+
# Emit output callback
|
|
262
|
+
if self.on_output:
|
|
263
|
+
try:
|
|
264
|
+
await self.on_output(run.run_id, chunk)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
logger.error(f"Error in output callback: {e}")
|
|
267
|
+
|
|
268
|
+
except Exception as stream_error:
|
|
269
|
+
# Fallback to non-streaming
|
|
270
|
+
logger.debug(f"Streaming failed, falling back to sync: {stream_error}")
|
|
271
|
+
result = agent.chat(run.input_content)
|
|
272
|
+
output_chunks = [result]
|
|
273
|
+
|
|
274
|
+
if self.on_output:
|
|
275
|
+
try:
|
|
276
|
+
await self.on_output(run.run_id, result)
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.error(f"Error in output callback: {e}")
|
|
279
|
+
|
|
280
|
+
# Combine output
|
|
281
|
+
full_output = "".join(output_chunks)
|
|
282
|
+
|
|
283
|
+
# Mark final chunk
|
|
284
|
+
final_chunk = StreamChunk(
|
|
285
|
+
run_id=run.run_id,
|
|
286
|
+
content="",
|
|
287
|
+
chunk_index=chunk_index,
|
|
288
|
+
is_final=True,
|
|
289
|
+
)
|
|
290
|
+
await buffer.push(final_chunk)
|
|
291
|
+
|
|
292
|
+
# Complete the run
|
|
293
|
+
completed_run = await self.scheduler.complete(
|
|
294
|
+
run.run_id,
|
|
295
|
+
output=full_output,
|
|
296
|
+
metrics={
|
|
297
|
+
"chunks": chunk_index,
|
|
298
|
+
"output_length": len(full_output),
|
|
299
|
+
}
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if completed_run and self.on_complete:
|
|
303
|
+
try:
|
|
304
|
+
await self.on_complete(run.run_id, completed_run)
|
|
305
|
+
except Exception as e:
|
|
306
|
+
logger.error(f"Error in complete callback: {e}")
|
|
307
|
+
|
|
308
|
+
except asyncio.CancelledError:
|
|
309
|
+
# Worker was cancelled
|
|
310
|
+
await self.scheduler.cancel(run.run_id)
|
|
311
|
+
raise
|
|
312
|
+
|
|
313
|
+
except Exception as e:
|
|
314
|
+
logger.error(f"Run {run.run_id} failed: {e}", exc_info=True)
|
|
315
|
+
|
|
316
|
+
# Mark as failed
|
|
317
|
+
failed_run = await self.scheduler.fail(run.run_id, str(e))
|
|
318
|
+
|
|
319
|
+
if self.on_error:
|
|
320
|
+
try:
|
|
321
|
+
await self.on_error(run.run_id, e)
|
|
322
|
+
except Exception as callback_error:
|
|
323
|
+
logger.error(f"Error in error callback: {callback_error}")
|
|
324
|
+
|
|
325
|
+
finally:
|
|
326
|
+
# Cleanup buffer
|
|
327
|
+
self._stream_buffers.pop(run.run_id, None)
|
|
328
|
+
|
|
329
|
+
async def _stream_agent(self, agent, input_content: str):
|
|
330
|
+
"""
|
|
331
|
+
Stream output from agent.
|
|
332
|
+
|
|
333
|
+
Yields chunks of output text.
|
|
334
|
+
"""
|
|
335
|
+
# Try to use agent's async streaming if available
|
|
336
|
+
if hasattr(agent, 'astream'):
|
|
337
|
+
async for chunk in agent.astream(input_content):
|
|
338
|
+
yield chunk
|
|
339
|
+
elif hasattr(agent, 'stream'):
|
|
340
|
+
# Wrap sync stream in async
|
|
341
|
+
for chunk in agent.stream(input_content):
|
|
342
|
+
yield chunk
|
|
343
|
+
await asyncio.sleep(0) # Yield control
|
|
344
|
+
else:
|
|
345
|
+
# Fallback: get full response and yield in chunks
|
|
346
|
+
result = agent.chat(input_content)
|
|
347
|
+
|
|
348
|
+
# Simulate streaming by yielding words
|
|
349
|
+
words = result.split()
|
|
350
|
+
chunk_size = 3 # Words per chunk
|
|
351
|
+
|
|
352
|
+
for i in range(0, len(words), chunk_size):
|
|
353
|
+
chunk_words = words[i:i + chunk_size]
|
|
354
|
+
chunk = " ".join(chunk_words)
|
|
355
|
+
if i + chunk_size < len(words):
|
|
356
|
+
chunk += " "
|
|
357
|
+
yield chunk
|
|
358
|
+
await asyncio.sleep(0.05) # Small delay for streaming effect
|
|
359
|
+
|
|
360
|
+
def get_stream_buffer(self, run_id: str) -> Optional[StreamBuffer]:
|
|
361
|
+
"""Get the stream buffer for a run."""
|
|
362
|
+
return self._stream_buffers.get(run_id)
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def active_run_count(self) -> int:
|
|
366
|
+
"""Number of currently executing runs."""
|
|
367
|
+
return len(self._active_runs)
|
|
368
|
+
|
|
369
|
+
@property
|
|
370
|
+
def is_running(self) -> bool:
|
|
371
|
+
"""Whether the worker pool is running."""
|
|
372
|
+
return self._running
|