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,632 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Jobs CLI Feature for PraisonAI.
|
|
3
|
+
|
|
4
|
+
Provides CLI commands for managing async jobs.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
- praisonai run submit <prompt> # Submit a new job
|
|
8
|
+
- praisonai run status <job_id> # Get job status
|
|
9
|
+
- praisonai run result <job_id> # Get job result
|
|
10
|
+
- praisonai run cancel <job_id> # Cancel a job
|
|
11
|
+
- praisonai run list # List jobs
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
import json
|
|
16
|
+
import time
|
|
17
|
+
from typing import Optional, List
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class JobsHandler:
|
|
21
|
+
"""
|
|
22
|
+
Handler for jobs CLI commands.
|
|
23
|
+
|
|
24
|
+
Provides functionality to:
|
|
25
|
+
- Submit jobs to the API
|
|
26
|
+
- Check job status
|
|
27
|
+
- Get job results
|
|
28
|
+
- Cancel jobs
|
|
29
|
+
- List jobs
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
api_url: str = "http://127.0.0.1:8005",
|
|
35
|
+
verbose: bool = False
|
|
36
|
+
):
|
|
37
|
+
self.api_url = api_url.rstrip("/")
|
|
38
|
+
self.verbose = verbose
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def feature_name(self) -> str:
|
|
42
|
+
return "jobs"
|
|
43
|
+
|
|
44
|
+
def _get_client(self):
|
|
45
|
+
"""Get HTTP client."""
|
|
46
|
+
try:
|
|
47
|
+
import httpx
|
|
48
|
+
return httpx.Client(timeout=30.0)
|
|
49
|
+
except ImportError:
|
|
50
|
+
raise RuntimeError("httpx is required. Install with: pip install httpx")
|
|
51
|
+
|
|
52
|
+
def submit(
|
|
53
|
+
self,
|
|
54
|
+
prompt: str,
|
|
55
|
+
agent_file: Optional[str] = None,
|
|
56
|
+
recipe_name: Optional[str] = None,
|
|
57
|
+
recipe_config: Optional[dict] = None,
|
|
58
|
+
framework: str = "praisonai",
|
|
59
|
+
timeout: int = 3600,
|
|
60
|
+
wait: bool = False,
|
|
61
|
+
poll_interval: int = 5,
|
|
62
|
+
idempotency_key: Optional[str] = None,
|
|
63
|
+
idempotency_scope: str = "none",
|
|
64
|
+
webhook_url: Optional[str] = None,
|
|
65
|
+
session_id: Optional[str] = None,
|
|
66
|
+
output_json: bool = False
|
|
67
|
+
) -> dict:
|
|
68
|
+
"""
|
|
69
|
+
Submit a new job.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
prompt: The prompt/task for the agent
|
|
73
|
+
agent_file: Optional path to agents.yaml
|
|
74
|
+
recipe_name: Optional recipe name (mutually exclusive with agent_file)
|
|
75
|
+
recipe_config: Optional recipe configuration overrides
|
|
76
|
+
framework: Framework to use
|
|
77
|
+
timeout: Job timeout in seconds
|
|
78
|
+
wait: If True, wait for completion
|
|
79
|
+
poll_interval: Seconds between status polls
|
|
80
|
+
idempotency_key: Key for deduplication
|
|
81
|
+
idempotency_scope: Scope for deduplication (none, session, global)
|
|
82
|
+
webhook_url: URL for completion webhook
|
|
83
|
+
session_id: Session ID for conversation continuity
|
|
84
|
+
output_json: If True, output JSON format
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Job submission response
|
|
88
|
+
"""
|
|
89
|
+
# Validate mutually exclusive options
|
|
90
|
+
if agent_file and recipe_name:
|
|
91
|
+
raise ValueError("Cannot specify both --agent-file and --recipe. Choose one.")
|
|
92
|
+
|
|
93
|
+
client = self._get_client()
|
|
94
|
+
|
|
95
|
+
payload = {
|
|
96
|
+
"prompt": prompt,
|
|
97
|
+
"framework": framework,
|
|
98
|
+
"timeout": timeout
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if agent_file:
|
|
102
|
+
payload["agent_file"] = agent_file
|
|
103
|
+
if recipe_name:
|
|
104
|
+
payload["recipe_name"] = recipe_name
|
|
105
|
+
if recipe_config:
|
|
106
|
+
payload["recipe_config"] = recipe_config
|
|
107
|
+
if webhook_url:
|
|
108
|
+
payload["webhook_url"] = webhook_url
|
|
109
|
+
if session_id:
|
|
110
|
+
payload["session_id"] = session_id
|
|
111
|
+
if idempotency_key:
|
|
112
|
+
payload["idempotency_key"] = idempotency_key
|
|
113
|
+
if idempotency_scope and idempotency_scope != "none":
|
|
114
|
+
payload["idempotency_scope"] = idempotency_scope
|
|
115
|
+
|
|
116
|
+
headers = {}
|
|
117
|
+
if idempotency_key:
|
|
118
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
response = client.post(
|
|
122
|
+
f"{self.api_url}/api/v1/runs",
|
|
123
|
+
json=payload,
|
|
124
|
+
headers=headers if headers else None
|
|
125
|
+
)
|
|
126
|
+
response.raise_for_status()
|
|
127
|
+
result = response.json()
|
|
128
|
+
|
|
129
|
+
if output_json:
|
|
130
|
+
print(json.dumps(result, indent=2))
|
|
131
|
+
else:
|
|
132
|
+
self._print_success(f"Job submitted: {result['job_id']}")
|
|
133
|
+
self._print_info(f"Status URL: {result['poll_url']}")
|
|
134
|
+
|
|
135
|
+
if wait:
|
|
136
|
+
return self._wait_for_completion(result['job_id'], poll_interval, output_json)
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
except Exception as e:
|
|
141
|
+
self._print_error(f"Failed to submit job: {e}")
|
|
142
|
+
raise
|
|
143
|
+
|
|
144
|
+
def status(self, job_id: str) -> dict:
|
|
145
|
+
"""
|
|
146
|
+
Get job status.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
job_id: Job ID
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Job status response
|
|
153
|
+
"""
|
|
154
|
+
client = self._get_client()
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
response = client.get(f"{self.api_url}/api/v1/runs/{job_id}")
|
|
158
|
+
response.raise_for_status()
|
|
159
|
+
result = response.json()
|
|
160
|
+
|
|
161
|
+
self._print_job_status(result)
|
|
162
|
+
return result
|
|
163
|
+
|
|
164
|
+
except Exception as e:
|
|
165
|
+
self._print_error(f"Failed to get status: {e}")
|
|
166
|
+
raise
|
|
167
|
+
|
|
168
|
+
def result(self, job_id: str) -> dict:
|
|
169
|
+
"""
|
|
170
|
+
Get job result.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
job_id: Job ID
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Job result response
|
|
177
|
+
"""
|
|
178
|
+
client = self._get_client()
|
|
179
|
+
|
|
180
|
+
try:
|
|
181
|
+
response = client.get(f"{self.api_url}/api/v1/runs/{job_id}/result")
|
|
182
|
+
response.raise_for_status()
|
|
183
|
+
result = response.json()
|
|
184
|
+
|
|
185
|
+
self._print_job_result(result)
|
|
186
|
+
return result
|
|
187
|
+
|
|
188
|
+
except Exception as e:
|
|
189
|
+
self._print_error(f"Failed to get result: {e}")
|
|
190
|
+
raise
|
|
191
|
+
|
|
192
|
+
def cancel(self, job_id: str) -> dict:
|
|
193
|
+
"""
|
|
194
|
+
Cancel a job.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
job_id: Job ID
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Updated job status
|
|
201
|
+
"""
|
|
202
|
+
client = self._get_client()
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
response = client.post(f"{self.api_url}/api/v1/runs/{job_id}/cancel")
|
|
206
|
+
response.raise_for_status()
|
|
207
|
+
result = response.json()
|
|
208
|
+
|
|
209
|
+
self._print_success(f"Job cancelled: {job_id}")
|
|
210
|
+
return result
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
self._print_error(f"Failed to cancel job: {e}")
|
|
214
|
+
raise
|
|
215
|
+
|
|
216
|
+
def list_jobs(
|
|
217
|
+
self,
|
|
218
|
+
status: Optional[str] = None,
|
|
219
|
+
page: int = 1,
|
|
220
|
+
page_size: int = 20
|
|
221
|
+
) -> dict:
|
|
222
|
+
"""
|
|
223
|
+
List jobs.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
status: Optional status filter
|
|
227
|
+
page: Page number
|
|
228
|
+
page_size: Jobs per page
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Job list response
|
|
232
|
+
"""
|
|
233
|
+
client = self._get_client()
|
|
234
|
+
|
|
235
|
+
params = {"page": page, "page_size": page_size}
|
|
236
|
+
if status:
|
|
237
|
+
params["status"] = status
|
|
238
|
+
|
|
239
|
+
try:
|
|
240
|
+
response = client.get(
|
|
241
|
+
f"{self.api_url}/api/v1/runs",
|
|
242
|
+
params=params
|
|
243
|
+
)
|
|
244
|
+
response.raise_for_status()
|
|
245
|
+
result = response.json()
|
|
246
|
+
|
|
247
|
+
self._print_job_list(result)
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
except Exception as e:
|
|
251
|
+
self._print_error(f"Failed to list jobs: {e}")
|
|
252
|
+
raise
|
|
253
|
+
|
|
254
|
+
def _wait_for_completion(self, job_id: str, poll_interval: int = 5, output_json: bool = False) -> dict:
|
|
255
|
+
"""Wait for a job to complete."""
|
|
256
|
+
client = self._get_client()
|
|
257
|
+
|
|
258
|
+
if not output_json:
|
|
259
|
+
self._print_info("Waiting for job completion...")
|
|
260
|
+
|
|
261
|
+
while True:
|
|
262
|
+
try:
|
|
263
|
+
response = client.get(f"{self.api_url}/api/v1/runs/{job_id}")
|
|
264
|
+
response.raise_for_status()
|
|
265
|
+
result = response.json()
|
|
266
|
+
|
|
267
|
+
status = result.get("status")
|
|
268
|
+
progress = result.get("progress", {}).get("percentage", 0)
|
|
269
|
+
|
|
270
|
+
if not output_json:
|
|
271
|
+
self._print_progress(status, progress)
|
|
272
|
+
|
|
273
|
+
if status in ("succeeded", "failed", "cancelled"):
|
|
274
|
+
if output_json:
|
|
275
|
+
# Get full result for succeeded jobs
|
|
276
|
+
if status == "succeeded":
|
|
277
|
+
res = client.get(f"{self.api_url}/api/v1/runs/{job_id}/result")
|
|
278
|
+
res.raise_for_status()
|
|
279
|
+
print(json.dumps(res.json(), indent=2))
|
|
280
|
+
else:
|
|
281
|
+
print(json.dumps(result, indent=2))
|
|
282
|
+
return result
|
|
283
|
+
else:
|
|
284
|
+
if status == "succeeded":
|
|
285
|
+
return self.result(job_id)
|
|
286
|
+
else:
|
|
287
|
+
return result
|
|
288
|
+
|
|
289
|
+
# Honor Retry-After header if present
|
|
290
|
+
retry_after = result.get("retry_after") or poll_interval
|
|
291
|
+
time.sleep(retry_after)
|
|
292
|
+
|
|
293
|
+
except KeyboardInterrupt:
|
|
294
|
+
if not output_json:
|
|
295
|
+
self._print_info("\nInterrupted. Job continues running in background.")
|
|
296
|
+
return result
|
|
297
|
+
except Exception as e:
|
|
298
|
+
if not output_json:
|
|
299
|
+
self._print_error(f"Error polling status: {e}")
|
|
300
|
+
time.sleep(poll_interval)
|
|
301
|
+
|
|
302
|
+
def stream(self, job_id: str, output_json: bool = False) -> None:
|
|
303
|
+
"""
|
|
304
|
+
Stream job progress via SSE.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
job_id: Job ID
|
|
308
|
+
output_json: If True, output raw JSON events
|
|
309
|
+
"""
|
|
310
|
+
try:
|
|
311
|
+
import httpx
|
|
312
|
+
|
|
313
|
+
with httpx.stream(
|
|
314
|
+
"GET",
|
|
315
|
+
f"{self.api_url}/api/v1/runs/{job_id}/stream",
|
|
316
|
+
timeout=None
|
|
317
|
+
) as response:
|
|
318
|
+
response.raise_for_status()
|
|
319
|
+
|
|
320
|
+
for line in response.iter_lines():
|
|
321
|
+
if not line:
|
|
322
|
+
continue
|
|
323
|
+
if line.startswith("data:"):
|
|
324
|
+
data = line[5:].strip()
|
|
325
|
+
if data == "[DONE]":
|
|
326
|
+
if not output_json:
|
|
327
|
+
self._print_success("Stream completed")
|
|
328
|
+
break
|
|
329
|
+
try:
|
|
330
|
+
event = json.loads(data)
|
|
331
|
+
if output_json:
|
|
332
|
+
print(json.dumps(event))
|
|
333
|
+
else:
|
|
334
|
+
event_type = event.get("event", "unknown")
|
|
335
|
+
if event_type == "progress":
|
|
336
|
+
pct = event.get("data", {}).get("percentage", 0)
|
|
337
|
+
step = event.get("data", {}).get("current_step", "")
|
|
338
|
+
self._print_progress(f"{step}", pct)
|
|
339
|
+
elif event_type == "complete":
|
|
340
|
+
status = event.get("data", {}).get("status", "")
|
|
341
|
+
if status == "succeeded":
|
|
342
|
+
self._print_success(f"Job completed: {status}")
|
|
343
|
+
else:
|
|
344
|
+
self._print_error(f"Job ended: {status}")
|
|
345
|
+
elif event_type == "error":
|
|
346
|
+
self._print_error(event.get("data", {}).get("error", "Unknown error"))
|
|
347
|
+
except json.JSONDecodeError:
|
|
348
|
+
if not output_json:
|
|
349
|
+
print(data)
|
|
350
|
+
elif line.startswith(":"):
|
|
351
|
+
# Heartbeat comment, ignore
|
|
352
|
+
pass
|
|
353
|
+
|
|
354
|
+
except Exception as e:
|
|
355
|
+
self._print_error(f"Stream error: {e}")
|
|
356
|
+
raise
|
|
357
|
+
|
|
358
|
+
def _print_job_status(self, job: dict):
|
|
359
|
+
"""Print job status."""
|
|
360
|
+
try:
|
|
361
|
+
from rich.console import Console
|
|
362
|
+
from rich.panel import Panel
|
|
363
|
+
|
|
364
|
+
console = Console()
|
|
365
|
+
|
|
366
|
+
status = job.get("status", "unknown")
|
|
367
|
+
status_color = {
|
|
368
|
+
"queued": "dim",
|
|
369
|
+
"running": "yellow",
|
|
370
|
+
"succeeded": "green",
|
|
371
|
+
"failed": "red",
|
|
372
|
+
"cancelled": "dim"
|
|
373
|
+
}.get(status, "white")
|
|
374
|
+
|
|
375
|
+
progress = job.get("progress", {})
|
|
376
|
+
|
|
377
|
+
content = f"""
|
|
378
|
+
[bold]Job ID:[/bold] {job.get('job_id')}
|
|
379
|
+
[bold]Status:[/bold] [{status_color}]{status}[/{status_color}]
|
|
380
|
+
[bold]Progress:[/bold] {progress.get('percentage', 0):.0f}%
|
|
381
|
+
[bold]Step:[/bold] {progress.get('current_step') or '-'}
|
|
382
|
+
[bold]Created:[/bold] {job.get('created_at')}
|
|
383
|
+
[bold]Started:[/bold] {job.get('started_at') or '-'}
|
|
384
|
+
"""
|
|
385
|
+
if job.get("error"):
|
|
386
|
+
content += f"[bold]Error:[/bold] [red]{job.get('error')}[/red]\n"
|
|
387
|
+
|
|
388
|
+
console.print(Panel(content.strip(), title="Job Status"))
|
|
389
|
+
except ImportError:
|
|
390
|
+
print(f"Job: {job.get('job_id')}")
|
|
391
|
+
print(f" Status: {job.get('status')}")
|
|
392
|
+
print(f" Progress: {job.get('progress', {}).get('percentage', 0):.0f}%")
|
|
393
|
+
|
|
394
|
+
def _print_job_result(self, job: dict):
|
|
395
|
+
"""Print job result."""
|
|
396
|
+
try:
|
|
397
|
+
from rich.console import Console
|
|
398
|
+
from rich.panel import Panel
|
|
399
|
+
from rich.markdown import Markdown
|
|
400
|
+
|
|
401
|
+
console = Console()
|
|
402
|
+
|
|
403
|
+
status = job.get("status")
|
|
404
|
+
|
|
405
|
+
if status == "succeeded":
|
|
406
|
+
result = job.get("result", "")
|
|
407
|
+
if isinstance(result, str):
|
|
408
|
+
console.print(Panel(Markdown(result), title="Result"))
|
|
409
|
+
else:
|
|
410
|
+
console.print(Panel(json.dumps(result, indent=2), title="Result"))
|
|
411
|
+
else:
|
|
412
|
+
console.print(f"[red]Job {status}: {job.get('error')}[/red]")
|
|
413
|
+
|
|
414
|
+
except ImportError:
|
|
415
|
+
print(f"Result: {job.get('result')}")
|
|
416
|
+
|
|
417
|
+
def _print_job_list(self, response: dict):
|
|
418
|
+
"""Print job list."""
|
|
419
|
+
try:
|
|
420
|
+
from rich.console import Console
|
|
421
|
+
from rich.table import Table
|
|
422
|
+
|
|
423
|
+
console = Console()
|
|
424
|
+
jobs = response.get("jobs", [])
|
|
425
|
+
|
|
426
|
+
if not jobs:
|
|
427
|
+
console.print("[yellow]No jobs found[/yellow]")
|
|
428
|
+
return
|
|
429
|
+
|
|
430
|
+
table = Table(title=f"Jobs (Page {response.get('page')}/{(response.get('total', 0) + response.get('page_size', 20) - 1) // response.get('page_size', 20)})")
|
|
431
|
+
table.add_column("ID", style="cyan")
|
|
432
|
+
table.add_column("Status", style="yellow")
|
|
433
|
+
table.add_column("Progress", style="blue")
|
|
434
|
+
table.add_column("Created", style="dim")
|
|
435
|
+
|
|
436
|
+
for job in jobs:
|
|
437
|
+
status = job.get("status", "unknown")
|
|
438
|
+
status_color = {
|
|
439
|
+
"queued": "dim",
|
|
440
|
+
"running": "yellow",
|
|
441
|
+
"succeeded": "green",
|
|
442
|
+
"failed": "red",
|
|
443
|
+
"cancelled": "dim"
|
|
444
|
+
}.get(status, "white")
|
|
445
|
+
|
|
446
|
+
progress = job.get("progress", {}).get("percentage", 0)
|
|
447
|
+
|
|
448
|
+
table.add_row(
|
|
449
|
+
job.get("job_id", "?"),
|
|
450
|
+
f"[{status_color}]{status}[/{status_color}]",
|
|
451
|
+
f"{progress:.0f}%",
|
|
452
|
+
job.get("created_at", "")[:19]
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
console.print(table)
|
|
456
|
+
console.print(f"Total: {response.get('total', 0)} jobs")
|
|
457
|
+
|
|
458
|
+
except ImportError:
|
|
459
|
+
print("Jobs:")
|
|
460
|
+
for job in response.get("jobs", []):
|
|
461
|
+
print(f" {job.get('job_id')} - {job.get('status')}")
|
|
462
|
+
|
|
463
|
+
def _print_progress(self, status: str, percentage: float):
|
|
464
|
+
"""Print progress update."""
|
|
465
|
+
try:
|
|
466
|
+
from rich.console import Console
|
|
467
|
+
console = Console()
|
|
468
|
+
console.print(f" Status: {status} | Progress: {percentage:.0f}%", end="\r")
|
|
469
|
+
except ImportError:
|
|
470
|
+
print(f" Status: {status} | Progress: {percentage:.0f}%", end="\r")
|
|
471
|
+
|
|
472
|
+
def _print_success(self, message: str):
|
|
473
|
+
"""Print success message."""
|
|
474
|
+
try:
|
|
475
|
+
from rich.console import Console
|
|
476
|
+
Console().print(f"[green]✓[/green] {message}")
|
|
477
|
+
except ImportError:
|
|
478
|
+
print(f"✓ {message}")
|
|
479
|
+
|
|
480
|
+
def _print_error(self, message: str):
|
|
481
|
+
"""Print error message."""
|
|
482
|
+
try:
|
|
483
|
+
from rich.console import Console
|
|
484
|
+
Console().print(f"[red]✗[/red] {message}")
|
|
485
|
+
except ImportError:
|
|
486
|
+
print(f"✗ {message}")
|
|
487
|
+
|
|
488
|
+
def _print_info(self, message: str):
|
|
489
|
+
"""Print info message."""
|
|
490
|
+
try:
|
|
491
|
+
from rich.console import Console
|
|
492
|
+
Console().print(f"[blue]ℹ[/blue] {message}")
|
|
493
|
+
except ImportError:
|
|
494
|
+
print(f"ℹ {message}")
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def handle_run_command(args: List[str], verbose: bool = False):
|
|
498
|
+
"""
|
|
499
|
+
Handle run CLI commands.
|
|
500
|
+
|
|
501
|
+
Usage:
|
|
502
|
+
praisonai run submit "Your prompt here" [--wait] [--agent-file agents.yaml]
|
|
503
|
+
praisonai run status <job_id>
|
|
504
|
+
praisonai run result <job_id>
|
|
505
|
+
praisonai run cancel <job_id>
|
|
506
|
+
praisonai run list [--status <status>]
|
|
507
|
+
"""
|
|
508
|
+
import argparse
|
|
509
|
+
|
|
510
|
+
parser = argparse.ArgumentParser(prog="praisonai run", description="Manage async jobs")
|
|
511
|
+
subparsers = parser.add_subparsers(dest="subcommand", help="Available commands")
|
|
512
|
+
|
|
513
|
+
# Submit command
|
|
514
|
+
submit_parser = subparsers.add_parser("submit", help="Submit a new job")
|
|
515
|
+
submit_parser.add_argument("prompt", help="The prompt/task for the agent")
|
|
516
|
+
submit_parser.add_argument("--agent-file", help="Path to agents.yaml")
|
|
517
|
+
submit_parser.add_argument("--recipe", dest="recipe_name", help="Recipe name to execute (mutually exclusive with --agent-file)")
|
|
518
|
+
submit_parser.add_argument("--recipe-config", help="Recipe config as JSON string")
|
|
519
|
+
submit_parser.add_argument("--framework", default="praisonai", help="Framework to use")
|
|
520
|
+
submit_parser.add_argument("--timeout", type=int, default=3600, help="Timeout in seconds")
|
|
521
|
+
submit_parser.add_argument("--wait", action="store_true", help="Wait for completion")
|
|
522
|
+
submit_parser.add_argument("--stream", action="store_true", help="Stream job progress after submission")
|
|
523
|
+
submit_parser.add_argument("--idempotency-key", help="Idempotency key to prevent duplicates")
|
|
524
|
+
submit_parser.add_argument("--idempotency-scope", default="none", choices=["none", "session", "global"], help="Idempotency scope")
|
|
525
|
+
submit_parser.add_argument("--webhook-url", help="Webhook URL for completion callback")
|
|
526
|
+
submit_parser.add_argument("--session-id", help="Session ID for grouping jobs")
|
|
527
|
+
submit_parser.add_argument("--metadata", action="append", metavar="KEY=VALUE", help="Custom metadata (can be used multiple times)")
|
|
528
|
+
submit_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON for scripting")
|
|
529
|
+
submit_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
530
|
+
|
|
531
|
+
# Status command
|
|
532
|
+
status_parser = subparsers.add_parser("status", help="Get job status")
|
|
533
|
+
status_parser.add_argument("job_id", help="Job ID")
|
|
534
|
+
status_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
|
|
535
|
+
status_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
536
|
+
|
|
537
|
+
# Result command
|
|
538
|
+
result_parser = subparsers.add_parser("result", help="Get job result")
|
|
539
|
+
result_parser.add_argument("job_id", help="Job ID")
|
|
540
|
+
result_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
|
|
541
|
+
result_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
542
|
+
|
|
543
|
+
# Cancel command
|
|
544
|
+
cancel_parser = subparsers.add_parser("cancel", help="Cancel a job")
|
|
545
|
+
cancel_parser.add_argument("job_id", help="Job ID")
|
|
546
|
+
cancel_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
|
|
547
|
+
cancel_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
548
|
+
|
|
549
|
+
# List command
|
|
550
|
+
list_parser = subparsers.add_parser("list", help="List jobs")
|
|
551
|
+
list_parser.add_argument("--status", help="Filter by status")
|
|
552
|
+
list_parser.add_argument("--page", type=int, default=1, help="Page number")
|
|
553
|
+
list_parser.add_argument("--page-size", type=int, default=20, help="Jobs per page")
|
|
554
|
+
list_parser.add_argument("--json", dest="output_json", action="store_true", help="Output JSON")
|
|
555
|
+
list_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
556
|
+
|
|
557
|
+
# Stream command
|
|
558
|
+
stream_parser = subparsers.add_parser("stream", help="Stream job progress via SSE")
|
|
559
|
+
stream_parser.add_argument("job_id", help="Job ID")
|
|
560
|
+
stream_parser.add_argument("--json", dest="output_json", action="store_true", help="Output raw JSON events")
|
|
561
|
+
stream_parser.add_argument("--api-url", default="http://127.0.0.1:8005", help="API server URL")
|
|
562
|
+
|
|
563
|
+
if not args:
|
|
564
|
+
parser.print_help()
|
|
565
|
+
return
|
|
566
|
+
|
|
567
|
+
parsed = parser.parse_args(args)
|
|
568
|
+
|
|
569
|
+
if not parsed.subcommand:
|
|
570
|
+
parser.print_help()
|
|
571
|
+
return
|
|
572
|
+
|
|
573
|
+
handler = JobsHandler(
|
|
574
|
+
api_url=getattr(parsed, "api_url", "http://127.0.0.1:8005"),
|
|
575
|
+
verbose=verbose
|
|
576
|
+
)
|
|
577
|
+
|
|
578
|
+
try:
|
|
579
|
+
if parsed.subcommand == "submit":
|
|
580
|
+
# Parse recipe config if provided
|
|
581
|
+
recipe_config = None
|
|
582
|
+
if getattr(parsed, "recipe_config", None):
|
|
583
|
+
try:
|
|
584
|
+
recipe_config = json.loads(parsed.recipe_config)
|
|
585
|
+
except json.JSONDecodeError:
|
|
586
|
+
print("Error: --recipe-config must be valid JSON")
|
|
587
|
+
sys.exit(1)
|
|
588
|
+
|
|
589
|
+
handler.submit(
|
|
590
|
+
prompt=parsed.prompt,
|
|
591
|
+
agent_file=parsed.agent_file,
|
|
592
|
+
recipe_name=getattr(parsed, "recipe_name", None),
|
|
593
|
+
recipe_config=recipe_config,
|
|
594
|
+
framework=parsed.framework,
|
|
595
|
+
timeout=parsed.timeout,
|
|
596
|
+
wait=parsed.wait,
|
|
597
|
+
idempotency_key=getattr(parsed, "idempotency_key", None),
|
|
598
|
+
idempotency_scope=getattr(parsed, "idempotency_scope", "none"),
|
|
599
|
+
webhook_url=getattr(parsed, "webhook_url", None),
|
|
600
|
+
session_id=getattr(parsed, "session_id", None),
|
|
601
|
+
output_json=getattr(parsed, "output_json", False)
|
|
602
|
+
)
|
|
603
|
+
elif parsed.subcommand == "status":
|
|
604
|
+
result = handler.status(parsed.job_id)
|
|
605
|
+
if getattr(parsed, "output_json", False):
|
|
606
|
+
print(json.dumps(result, indent=2))
|
|
607
|
+
elif parsed.subcommand == "result":
|
|
608
|
+
result = handler.result(parsed.job_id)
|
|
609
|
+
if getattr(parsed, "output_json", False):
|
|
610
|
+
print(json.dumps(result, indent=2))
|
|
611
|
+
elif parsed.subcommand == "cancel":
|
|
612
|
+
result = handler.cancel(parsed.job_id)
|
|
613
|
+
if getattr(parsed, "output_json", False):
|
|
614
|
+
print(json.dumps(result, indent=2))
|
|
615
|
+
elif parsed.subcommand == "list":
|
|
616
|
+
result = handler.list_jobs(
|
|
617
|
+
status=parsed.status,
|
|
618
|
+
page=parsed.page,
|
|
619
|
+
page_size=parsed.page_size
|
|
620
|
+
)
|
|
621
|
+
if getattr(parsed, "output_json", False):
|
|
622
|
+
print(json.dumps(result, indent=2))
|
|
623
|
+
elif parsed.subcommand == "stream":
|
|
624
|
+
handler.stream(
|
|
625
|
+
job_id=parsed.job_id,
|
|
626
|
+
output_json=getattr(parsed, "output_json", False)
|
|
627
|
+
)
|
|
628
|
+
except Exception:
|
|
629
|
+
if verbose:
|
|
630
|
+
import traceback
|
|
631
|
+
traceback.print_exc()
|
|
632
|
+
sys.exit(1)
|