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,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Async Jobs API for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Provides HTTP API endpoints for long-running agent tasks:
|
|
5
|
+
- Submit jobs (POST /api/v1/runs)
|
|
6
|
+
- Check status (GET /api/v1/runs/{job_id})
|
|
7
|
+
- Get results (GET /api/v1/runs/{job_id}/result)
|
|
8
|
+
- Cancel jobs (POST /api/v1/runs/{job_id}/cancel)
|
|
9
|
+
- Stream progress (GET /api/v1/runs/{job_id}/stream)
|
|
10
|
+
|
|
11
|
+
Zero Performance Impact:
|
|
12
|
+
- All imports are lazy loaded via __getattr__
|
|
13
|
+
- Server only starts when explicitly requested
|
|
14
|
+
- No overhead when not in use
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
# Start the jobs server
|
|
18
|
+
praisonai serve --port 8005
|
|
19
|
+
|
|
20
|
+
# Or programmatically
|
|
21
|
+
from praisonai.jobs import start_server
|
|
22
|
+
start_server(port=8005)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Models
|
|
27
|
+
"Job",
|
|
28
|
+
"JobStatus",
|
|
29
|
+
"JobSubmitRequest",
|
|
30
|
+
"JobStatusResponse",
|
|
31
|
+
"JobResultResponse",
|
|
32
|
+
# Store
|
|
33
|
+
"JobStore",
|
|
34
|
+
"InMemoryJobStore",
|
|
35
|
+
# Executor
|
|
36
|
+
"JobExecutor",
|
|
37
|
+
# Router
|
|
38
|
+
"create_router",
|
|
39
|
+
# Server
|
|
40
|
+
"start_server",
|
|
41
|
+
"create_app",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def __getattr__(name: str):
|
|
46
|
+
"""Lazy load module components to avoid import overhead."""
|
|
47
|
+
if name in ("Job", "JobStatus", "JobSubmitRequest", "JobStatusResponse", "JobResultResponse"):
|
|
48
|
+
from .models import Job, JobStatus, JobSubmitRequest, JobStatusResponse, JobResultResponse
|
|
49
|
+
return locals()[name]
|
|
50
|
+
|
|
51
|
+
if name in ("JobStore", "InMemoryJobStore"):
|
|
52
|
+
from .store import JobStore, InMemoryJobStore
|
|
53
|
+
return locals()[name]
|
|
54
|
+
|
|
55
|
+
if name == "JobExecutor":
|
|
56
|
+
from .executor import JobExecutor
|
|
57
|
+
return JobExecutor
|
|
58
|
+
|
|
59
|
+
if name == "create_router":
|
|
60
|
+
from .router import create_router
|
|
61
|
+
return create_router
|
|
62
|
+
|
|
63
|
+
if name in ("start_server", "create_app"):
|
|
64
|
+
from .server import start_server, create_app
|
|
65
|
+
return locals()[name]
|
|
66
|
+
|
|
67
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Job Executor for PraisonAI Async Jobs API.
|
|
3
|
+
|
|
4
|
+
Handles background execution of agent jobs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import logging
|
|
9
|
+
import os
|
|
10
|
+
from typing import Optional, Callable, Any, Dict
|
|
11
|
+
|
|
12
|
+
from .models import Job, JobStatus
|
|
13
|
+
from .store import JobStore
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class JobExecutor:
|
|
19
|
+
"""
|
|
20
|
+
Executes jobs in the background using asyncio.
|
|
21
|
+
|
|
22
|
+
Features:
|
|
23
|
+
- Concurrent job execution with configurable limits
|
|
24
|
+
- Timeout handling
|
|
25
|
+
- Cancellation support
|
|
26
|
+
- Progress callbacks
|
|
27
|
+
- Webhook notifications
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
store: JobStore,
|
|
33
|
+
max_concurrent: int = 10,
|
|
34
|
+
default_timeout: int = 3600,
|
|
35
|
+
cleanup_interval: int = 300
|
|
36
|
+
):
|
|
37
|
+
self.store = store
|
|
38
|
+
self.max_concurrent = max_concurrent
|
|
39
|
+
self.default_timeout = default_timeout
|
|
40
|
+
self.cleanup_interval = cleanup_interval
|
|
41
|
+
|
|
42
|
+
self._semaphore: Optional[asyncio.Semaphore] = None
|
|
43
|
+
self._running_tasks: Dict[str, asyncio.Task] = {}
|
|
44
|
+
self._cleanup_task: Optional[asyncio.Task] = None
|
|
45
|
+
self._shutdown = False
|
|
46
|
+
self._progress_callbacks: Dict[str, Callable] = {}
|
|
47
|
+
|
|
48
|
+
def _get_semaphore(self) -> asyncio.Semaphore:
|
|
49
|
+
"""Lazily create semaphore to avoid event loop issues."""
|
|
50
|
+
if self._semaphore is None:
|
|
51
|
+
self._semaphore = asyncio.Semaphore(self.max_concurrent)
|
|
52
|
+
return self._semaphore
|
|
53
|
+
|
|
54
|
+
async def start(self):
|
|
55
|
+
"""Start the executor and cleanup loop."""
|
|
56
|
+
self._shutdown = False
|
|
57
|
+
self._cleanup_task = asyncio.create_task(self._cleanup_loop())
|
|
58
|
+
logger.info(f"JobExecutor started (max_concurrent={self.max_concurrent})")
|
|
59
|
+
|
|
60
|
+
async def stop(self):
|
|
61
|
+
"""Stop the executor and cancel all running jobs."""
|
|
62
|
+
self._shutdown = True
|
|
63
|
+
|
|
64
|
+
# Cancel cleanup task
|
|
65
|
+
if self._cleanup_task:
|
|
66
|
+
self._cleanup_task.cancel()
|
|
67
|
+
try:
|
|
68
|
+
await self._cleanup_task
|
|
69
|
+
except asyncio.CancelledError:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
# Cancel all running jobs
|
|
73
|
+
for job_id, task in list(self._running_tasks.items()):
|
|
74
|
+
task.cancel()
|
|
75
|
+
try:
|
|
76
|
+
await task
|
|
77
|
+
except asyncio.CancelledError:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
self._running_tasks.clear()
|
|
81
|
+
logger.info("JobExecutor stopped")
|
|
82
|
+
|
|
83
|
+
async def _cleanup_loop(self):
|
|
84
|
+
"""Periodically clean up old completed jobs."""
|
|
85
|
+
while not self._shutdown:
|
|
86
|
+
try:
|
|
87
|
+
await asyncio.sleep(self.cleanup_interval)
|
|
88
|
+
await self.store.cleanup_old_jobs(max_age_seconds=86400) # 24 hours
|
|
89
|
+
except asyncio.CancelledError:
|
|
90
|
+
break
|
|
91
|
+
except Exception as e:
|
|
92
|
+
logger.error(f"Cleanup error: {e}")
|
|
93
|
+
|
|
94
|
+
async def submit(self, job: Job) -> Job:
|
|
95
|
+
"""
|
|
96
|
+
Submit a job for execution.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
job: Job to execute
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
The submitted job
|
|
103
|
+
"""
|
|
104
|
+
# Save job to store
|
|
105
|
+
await self.store.save(job)
|
|
106
|
+
|
|
107
|
+
# Start execution task
|
|
108
|
+
task = asyncio.create_task(self._execute_job(job))
|
|
109
|
+
self._running_tasks[job.id] = task
|
|
110
|
+
|
|
111
|
+
# Clean up task reference when done
|
|
112
|
+
task.add_done_callback(lambda t: self._running_tasks.pop(job.id, None))
|
|
113
|
+
|
|
114
|
+
logger.info(f"Job submitted: {job.id}")
|
|
115
|
+
return job
|
|
116
|
+
|
|
117
|
+
async def cancel(self, job_id: str) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Cancel a running job.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
job_id: ID of job to cancel
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
True if cancelled, False if not found or already complete
|
|
126
|
+
"""
|
|
127
|
+
job = await self.store.get(job_id)
|
|
128
|
+
if not job:
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
if job.is_terminal:
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
# Mark as cancelled
|
|
135
|
+
job.cancel()
|
|
136
|
+
await self.store.save(job)
|
|
137
|
+
|
|
138
|
+
# Cancel the task if running
|
|
139
|
+
task = self._running_tasks.get(job_id)
|
|
140
|
+
if task and not task.done():
|
|
141
|
+
task.cancel()
|
|
142
|
+
|
|
143
|
+
logger.info(f"Job cancelled: {job_id}")
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
def register_progress_callback(self, job_id: str, callback: Callable):
|
|
147
|
+
"""Register a callback for job progress updates."""
|
|
148
|
+
self._progress_callbacks[job_id] = callback
|
|
149
|
+
|
|
150
|
+
def unregister_progress_callback(self, job_id: str):
|
|
151
|
+
"""Unregister a progress callback."""
|
|
152
|
+
self._progress_callbacks.pop(job_id, None)
|
|
153
|
+
|
|
154
|
+
async def _execute_job(self, job: Job):
|
|
155
|
+
"""Execute a job."""
|
|
156
|
+
async with self._get_semaphore():
|
|
157
|
+
try:
|
|
158
|
+
# Mark as running
|
|
159
|
+
job.start()
|
|
160
|
+
await self.store.save(job)
|
|
161
|
+
await self._notify_progress(job)
|
|
162
|
+
|
|
163
|
+
logger.info(f"Job started: {job.id}")
|
|
164
|
+
|
|
165
|
+
# Execute with timeout
|
|
166
|
+
timeout = job.timeout or self.default_timeout
|
|
167
|
+
result = await asyncio.wait_for(
|
|
168
|
+
self._run_agent(job),
|
|
169
|
+
timeout=timeout
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Mark as succeeded
|
|
173
|
+
job.succeed(result)
|
|
174
|
+
await self.store.save(job)
|
|
175
|
+
await self._notify_progress(job)
|
|
176
|
+
|
|
177
|
+
logger.info(f"Job succeeded: {job.id}")
|
|
178
|
+
|
|
179
|
+
# Send webhook if configured
|
|
180
|
+
if job.webhook_url:
|
|
181
|
+
await self._send_webhook(job)
|
|
182
|
+
|
|
183
|
+
except asyncio.TimeoutError:
|
|
184
|
+
job.fail(f"Job timed out after {job.timeout}s")
|
|
185
|
+
await self.store.save(job)
|
|
186
|
+
await self._notify_progress(job)
|
|
187
|
+
logger.warning(f"Job timed out: {job.id}")
|
|
188
|
+
|
|
189
|
+
except asyncio.CancelledError:
|
|
190
|
+
if job.status != JobStatus.CANCELLED:
|
|
191
|
+
job.cancel()
|
|
192
|
+
await self.store.save(job)
|
|
193
|
+
await self._notify_progress(job)
|
|
194
|
+
logger.info(f"Job cancelled: {job.id}")
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
except Exception as e:
|
|
198
|
+
job.fail(str(e))
|
|
199
|
+
await self.store.save(job)
|
|
200
|
+
await self._notify_progress(job)
|
|
201
|
+
logger.error(f"Job failed: {job.id} - {e}")
|
|
202
|
+
|
|
203
|
+
# Send webhook for failures too
|
|
204
|
+
if job.webhook_url:
|
|
205
|
+
await self._send_webhook(job)
|
|
206
|
+
|
|
207
|
+
async def _run_agent(self, job: Job) -> Any:
|
|
208
|
+
"""
|
|
209
|
+
Run the agent for a job.
|
|
210
|
+
|
|
211
|
+
This is the core execution logic that runs the PraisonAI agent.
|
|
212
|
+
Supports both direct agent execution and recipe-based execution.
|
|
213
|
+
"""
|
|
214
|
+
# Check if this is a recipe job
|
|
215
|
+
if job.recipe_name:
|
|
216
|
+
return await self._run_recipe(job)
|
|
217
|
+
|
|
218
|
+
# Imports are done lazily in _run_praisonai_agents and _run_legacy_praisonai
|
|
219
|
+
|
|
220
|
+
# Determine agent configuration
|
|
221
|
+
agent_file = job.agent_file or "agents.yaml"
|
|
222
|
+
framework = job.framework or "praisonai"
|
|
223
|
+
|
|
224
|
+
# Check if we should use inline YAML
|
|
225
|
+
if job.agent_yaml:
|
|
226
|
+
# Write to temp file
|
|
227
|
+
import tempfile
|
|
228
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
|
|
229
|
+
f.write(job.agent_yaml)
|
|
230
|
+
agent_file = f.name
|
|
231
|
+
|
|
232
|
+
# Update progress
|
|
233
|
+
job.update_progress(percentage=10.0, step="Initializing agent")
|
|
234
|
+
await self.store.save(job)
|
|
235
|
+
await self._notify_progress(job)
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
# Try praisonaiagents first (preferred)
|
|
239
|
+
if framework == "praisonai":
|
|
240
|
+
result = await self._run_praisonai_agents(job, agent_file)
|
|
241
|
+
else:
|
|
242
|
+
# Use legacy PraisonAI for crewai/autogen
|
|
243
|
+
result = await self._run_legacy_praisonai(job, agent_file, framework)
|
|
244
|
+
|
|
245
|
+
return result
|
|
246
|
+
|
|
247
|
+
finally:
|
|
248
|
+
# Clean up temp file if created
|
|
249
|
+
if job.agent_yaml and agent_file != "agents.yaml":
|
|
250
|
+
try:
|
|
251
|
+
os.unlink(agent_file)
|
|
252
|
+
except Exception:
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
async def _run_recipe(self, job: Job) -> Any:
|
|
256
|
+
"""Run a recipe-based job."""
|
|
257
|
+
try:
|
|
258
|
+
from praisonai.recipe.bridge import resolve, execute_resolved_recipe
|
|
259
|
+
except ImportError:
|
|
260
|
+
raise RuntimeError("Recipe execution requires praisonai.recipe module")
|
|
261
|
+
|
|
262
|
+
# Update progress
|
|
263
|
+
job.update_progress(percentage=10.0, step=f"Resolving recipe: {job.recipe_name}")
|
|
264
|
+
await self.store.save(job)
|
|
265
|
+
await self._notify_progress(job)
|
|
266
|
+
|
|
267
|
+
# Resolve the recipe
|
|
268
|
+
resolved = resolve(
|
|
269
|
+
job.recipe_name,
|
|
270
|
+
input_data=job.prompt,
|
|
271
|
+
config=job.recipe_config or {},
|
|
272
|
+
session_id=job.session_id,
|
|
273
|
+
options={'timeout_sec': job.timeout},
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# Update job with recipe info
|
|
277
|
+
job.agent_id = f"recipe:{resolved.name}"
|
|
278
|
+
job.run_id = resolved.run_id
|
|
279
|
+
|
|
280
|
+
# Update progress
|
|
281
|
+
job.update_progress(percentage=20.0, step=f"Executing recipe: {resolved.name}")
|
|
282
|
+
await self.store.save(job)
|
|
283
|
+
await self._notify_progress(job)
|
|
284
|
+
|
|
285
|
+
# Execute the recipe
|
|
286
|
+
loop = asyncio.get_event_loop()
|
|
287
|
+
result = await loop.run_in_executor(
|
|
288
|
+
None,
|
|
289
|
+
lambda: execute_resolved_recipe(resolved)
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
# Update progress
|
|
293
|
+
job.update_progress(percentage=90.0, step="Finalizing")
|
|
294
|
+
await self.store.save(job)
|
|
295
|
+
await self._notify_progress(job)
|
|
296
|
+
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
async def _run_praisonai_agents(self, job: Job, agent_file: str) -> Any:
|
|
300
|
+
"""Run using praisonaiagents framework."""
|
|
301
|
+
try:
|
|
302
|
+
from praisonaiagents import Agent
|
|
303
|
+
except ImportError:
|
|
304
|
+
raise RuntimeError("praisonaiagents not installed")
|
|
305
|
+
|
|
306
|
+
# Update progress
|
|
307
|
+
job.update_progress(percentage=20.0, step="Creating agent")
|
|
308
|
+
await self.store.save(job)
|
|
309
|
+
await self._notify_progress(job)
|
|
310
|
+
|
|
311
|
+
# Create agent
|
|
312
|
+
agent = Agent(
|
|
313
|
+
instructions="You are a helpful AI assistant.",
|
|
314
|
+
verbose=False
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Update job with agent info
|
|
318
|
+
job.agent_id = getattr(agent, 'name', 'agent')
|
|
319
|
+
job.run_id = getattr(agent, 'run_id', None)
|
|
320
|
+
|
|
321
|
+
# Update progress
|
|
322
|
+
job.update_progress(percentage=30.0, step="Running agent")
|
|
323
|
+
await self.store.save(job)
|
|
324
|
+
await self._notify_progress(job)
|
|
325
|
+
|
|
326
|
+
# Run the agent
|
|
327
|
+
loop = asyncio.get_event_loop()
|
|
328
|
+
result = await loop.run_in_executor(
|
|
329
|
+
None,
|
|
330
|
+
lambda: agent.start(job.prompt)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
# Update progress
|
|
334
|
+
job.update_progress(percentage=90.0, step="Finalizing")
|
|
335
|
+
await self.store.save(job)
|
|
336
|
+
await self._notify_progress(job)
|
|
337
|
+
|
|
338
|
+
return result
|
|
339
|
+
|
|
340
|
+
async def _run_legacy_praisonai(self, job: Job, agent_file: str, framework: str) -> Any:
|
|
341
|
+
"""Run using legacy PraisonAI (crewai/autogen)."""
|
|
342
|
+
try:
|
|
343
|
+
from praisonai import PraisonAI
|
|
344
|
+
except ImportError:
|
|
345
|
+
raise RuntimeError("praisonai not installed")
|
|
346
|
+
|
|
347
|
+
# Update progress
|
|
348
|
+
job.update_progress(percentage=20.0, step="Creating PraisonAI instance")
|
|
349
|
+
await self.store.save(job)
|
|
350
|
+
await self._notify_progress(job)
|
|
351
|
+
|
|
352
|
+
# Create PraisonAI instance
|
|
353
|
+
praisonai = PraisonAI(
|
|
354
|
+
agent_file=agent_file,
|
|
355
|
+
framework=framework
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
# Update progress
|
|
359
|
+
job.update_progress(percentage=30.0, step="Running agents")
|
|
360
|
+
await self.store.save(job)
|
|
361
|
+
await self._notify_progress(job)
|
|
362
|
+
|
|
363
|
+
# Run in executor (blocking call)
|
|
364
|
+
loop = asyncio.get_event_loop()
|
|
365
|
+
result = await loop.run_in_executor(None, praisonai.run)
|
|
366
|
+
|
|
367
|
+
# Update progress
|
|
368
|
+
job.update_progress(percentage=90.0, step="Finalizing")
|
|
369
|
+
await self.store.save(job)
|
|
370
|
+
await self._notify_progress(job)
|
|
371
|
+
|
|
372
|
+
return result
|
|
373
|
+
|
|
374
|
+
async def _notify_progress(self, job: Job):
|
|
375
|
+
"""Notify progress callback if registered."""
|
|
376
|
+
callback = self._progress_callbacks.get(job.id)
|
|
377
|
+
if callback:
|
|
378
|
+
try:
|
|
379
|
+
if asyncio.iscoroutinefunction(callback):
|
|
380
|
+
await callback(job)
|
|
381
|
+
else:
|
|
382
|
+
callback(job)
|
|
383
|
+
except Exception as e:
|
|
384
|
+
logger.warning(f"Progress callback error for {job.id}: {e}")
|
|
385
|
+
|
|
386
|
+
async def _send_webhook(self, job: Job):
|
|
387
|
+
"""Send webhook notification for job completion."""
|
|
388
|
+
if not job.webhook_url:
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
import httpx
|
|
393
|
+
|
|
394
|
+
payload = {
|
|
395
|
+
"job_id": job.id,
|
|
396
|
+
"status": job.status.value,
|
|
397
|
+
"result": job.result if job.status == JobStatus.SUCCEEDED else None,
|
|
398
|
+
"error": job.error if job.status == JobStatus.FAILED else None,
|
|
399
|
+
"completed_at": job.completed_at.isoformat() if job.completed_at else None,
|
|
400
|
+
"duration_seconds": job.duration_seconds
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
404
|
+
response = await client.post(
|
|
405
|
+
job.webhook_url,
|
|
406
|
+
json=payload,
|
|
407
|
+
headers={"Content-Type": "application/json"}
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
if response.status_code >= 400:
|
|
411
|
+
logger.warning(f"Webhook failed for {job.id}: {response.status_code}")
|
|
412
|
+
else:
|
|
413
|
+
logger.info(f"Webhook sent for {job.id}")
|
|
414
|
+
|
|
415
|
+
except Exception as e:
|
|
416
|
+
logger.error(f"Webhook error for {job.id}: {e}")
|
|
417
|
+
|
|
418
|
+
def get_stats(self) -> Dict[str, Any]:
|
|
419
|
+
"""Get executor statistics."""
|
|
420
|
+
return {
|
|
421
|
+
"running_jobs": len(self._running_tasks),
|
|
422
|
+
"max_concurrent": self.max_concurrent,
|
|
423
|
+
"default_timeout": self.default_timeout,
|
|
424
|
+
"shutdown": self._shutdown
|
|
425
|
+
}
|