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,773 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Scheduler CLI Feature.
|
|
3
|
+
|
|
4
|
+
Provides CLI commands for scheduling agents to run 24/7 at regular intervals.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentSchedulerHandler:
|
|
13
|
+
"""Handler for agent scheduler CLI commands."""
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def handle_daemon_command(subcommand: str, args, unknown_args=None) -> int:
|
|
17
|
+
"""
|
|
18
|
+
Handle daemon management commands (start, list, stop, logs, restart).
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
subcommand: Command to execute (start, list, stop, logs, restart)
|
|
22
|
+
args: Parsed command-line arguments
|
|
23
|
+
unknown_args: Additional arguments
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Exit code
|
|
27
|
+
"""
|
|
28
|
+
from praisonai.scheduler.state_manager import SchedulerStateManager
|
|
29
|
+
from praisonai.scheduler.daemon_manager import DaemonManager
|
|
30
|
+
|
|
31
|
+
state_manager = SchedulerStateManager()
|
|
32
|
+
daemon_manager = DaemonManager()
|
|
33
|
+
|
|
34
|
+
if subcommand == "start":
|
|
35
|
+
return AgentSchedulerHandler._handle_start(args, unknown_args, state_manager, daemon_manager)
|
|
36
|
+
elif subcommand == "list":
|
|
37
|
+
return AgentSchedulerHandler._handle_list(state_manager)
|
|
38
|
+
elif subcommand == "stop":
|
|
39
|
+
return AgentSchedulerHandler._handle_stop(unknown_args, state_manager, daemon_manager)
|
|
40
|
+
elif subcommand == "logs":
|
|
41
|
+
return AgentSchedulerHandler._handle_logs(unknown_args, daemon_manager)
|
|
42
|
+
elif subcommand == "restart":
|
|
43
|
+
return AgentSchedulerHandler._handle_restart(unknown_args, state_manager, daemon_manager)
|
|
44
|
+
elif subcommand == "delete":
|
|
45
|
+
return AgentSchedulerHandler._handle_delete(unknown_args, state_manager)
|
|
46
|
+
elif subcommand == "describe":
|
|
47
|
+
return AgentSchedulerHandler._handle_describe(unknown_args, state_manager, daemon_manager)
|
|
48
|
+
elif subcommand == "save":
|
|
49
|
+
return AgentSchedulerHandler._handle_save(unknown_args, state_manager)
|
|
50
|
+
elif subcommand == "stop-all":
|
|
51
|
+
return AgentSchedulerHandler._handle_stop_all(state_manager, daemon_manager)
|
|
52
|
+
elif subcommand == "stats":
|
|
53
|
+
return AgentSchedulerHandler._handle_stats(state_manager, unknown_args, daemon_manager)
|
|
54
|
+
else:
|
|
55
|
+
print(f"ā Unknown subcommand: {subcommand}")
|
|
56
|
+
print("\nAvailable commands:")
|
|
57
|
+
print(" start, list, stop, logs, restart, delete, describe, save, stop-all, stats")
|
|
58
|
+
return 1
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def _handle_start(args, unknown_args, state_manager, daemon_manager) -> int:
|
|
62
|
+
"""Handle 'schedule start' command."""
|
|
63
|
+
from datetime import datetime
|
|
64
|
+
|
|
65
|
+
if not unknown_args:
|
|
66
|
+
print("ā Error: Please provide scheduler name and task/recipe")
|
|
67
|
+
print("\nUsage:")
|
|
68
|
+
print(' praisonai schedule start <name> "Your task" --interval hourly')
|
|
69
|
+
print(' praisonai schedule start <name> --recipe <recipe-name> --interval hourly')
|
|
70
|
+
print("\nExample:")
|
|
71
|
+
print(' praisonai schedule start news-checker "Check AI news" --interval hourly')
|
|
72
|
+
print(' praisonai schedule start news-checker --recipe news-monitor --interval hourly')
|
|
73
|
+
return 1
|
|
74
|
+
|
|
75
|
+
# Parse arguments
|
|
76
|
+
name = unknown_args[0]
|
|
77
|
+
|
|
78
|
+
# Check for --recipe flag in unknown_args
|
|
79
|
+
recipe_name = None
|
|
80
|
+
task = None
|
|
81
|
+
if "--recipe" in unknown_args:
|
|
82
|
+
recipe_idx = unknown_args.index("--recipe")
|
|
83
|
+
if recipe_idx + 1 < len(unknown_args):
|
|
84
|
+
recipe_name = unknown_args[recipe_idx + 1]
|
|
85
|
+
else:
|
|
86
|
+
task = unknown_args[1] if len(unknown_args) > 1 else None
|
|
87
|
+
|
|
88
|
+
if not task and not recipe_name:
|
|
89
|
+
print("ā Error: Please provide a task or --recipe <name>")
|
|
90
|
+
return 1
|
|
91
|
+
|
|
92
|
+
# Get options
|
|
93
|
+
interval = getattr(args, 'schedule_interval', None) or 'hourly'
|
|
94
|
+
max_retries = getattr(args, 'schedule_max_retries', None) or 3
|
|
95
|
+
timeout = getattr(args, 'timeout', None)
|
|
96
|
+
max_cost = getattr(args, 'max_cost', None)
|
|
97
|
+
|
|
98
|
+
# Check if name already exists
|
|
99
|
+
existing = state_manager.load_state(name)
|
|
100
|
+
if existing and daemon_manager.get_status(existing.get('pid', 0))['is_alive']:
|
|
101
|
+
print(f"ā Error: Scheduler '{name}' is already running (PID: {existing['pid']})")
|
|
102
|
+
print(f" Use 'praisonai schedule stop {name}' to stop it first")
|
|
103
|
+
return 1
|
|
104
|
+
|
|
105
|
+
# Start daemon
|
|
106
|
+
print(f"š Starting scheduler '{name}'...")
|
|
107
|
+
if recipe_name:
|
|
108
|
+
print(f" Recipe: {recipe_name}")
|
|
109
|
+
else:
|
|
110
|
+
print(f" Task: {task}")
|
|
111
|
+
print(f" Interval: {interval}")
|
|
112
|
+
if timeout:
|
|
113
|
+
print(f" Timeout: {timeout}s")
|
|
114
|
+
if max_cost:
|
|
115
|
+
print(f" Budget: ${max_cost}")
|
|
116
|
+
|
|
117
|
+
pid = daemon_manager.start_scheduler_daemon(
|
|
118
|
+
name=name,
|
|
119
|
+
task=task,
|
|
120
|
+
recipe_name=recipe_name,
|
|
121
|
+
interval=interval,
|
|
122
|
+
max_cost=max_cost,
|
|
123
|
+
timeout=timeout,
|
|
124
|
+
max_retries=max_retries
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Save state
|
|
128
|
+
state = {
|
|
129
|
+
"name": name,
|
|
130
|
+
"pid": pid,
|
|
131
|
+
"task": task,
|
|
132
|
+
"recipe_name": recipe_name,
|
|
133
|
+
"interval": interval,
|
|
134
|
+
"timeout": timeout,
|
|
135
|
+
"max_cost": max_cost,
|
|
136
|
+
"max_retries": max_retries,
|
|
137
|
+
"status": "running",
|
|
138
|
+
"started_at": datetime.now().isoformat(),
|
|
139
|
+
"executions": 0,
|
|
140
|
+
"cost": 0.0
|
|
141
|
+
}
|
|
142
|
+
state_manager.save_state(name, state)
|
|
143
|
+
|
|
144
|
+
print(f"\nā
Scheduler '{name}' started successfully!")
|
|
145
|
+
print(f" PID: {pid}")
|
|
146
|
+
print(f" Logs: ~/.praisonai/logs/{name}.log")
|
|
147
|
+
print(f"\nManage:")
|
|
148
|
+
print(f" praisonai schedule list")
|
|
149
|
+
print(f" praisonai schedule describe {name}")
|
|
150
|
+
print(f" praisonai schedule logs {name} --follow")
|
|
151
|
+
print(f" praisonai schedule stop {name}")
|
|
152
|
+
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _handle_list(state_manager) -> int:
|
|
157
|
+
"""Handle 'schedule list' command."""
|
|
158
|
+
states = state_manager.list_all()
|
|
159
|
+
|
|
160
|
+
if not states:
|
|
161
|
+
print("No schedulers running.")
|
|
162
|
+
print("\nStart one with:")
|
|
163
|
+
print(' praisonai schedule start <name> "Your task" --interval hourly')
|
|
164
|
+
return 0
|
|
165
|
+
|
|
166
|
+
# Clean up dead processes
|
|
167
|
+
state_manager.cleanup_dead_processes()
|
|
168
|
+
states = state_manager.list_all()
|
|
169
|
+
|
|
170
|
+
# Display table
|
|
171
|
+
print(f"\n{'Name':<20} {'Status':<10} {'PID':<8} {'Interval':<12} {'Task':<40}")
|
|
172
|
+
print("=" * 90)
|
|
173
|
+
|
|
174
|
+
for state in states:
|
|
175
|
+
name = state.get('name', 'unknown')[:20]
|
|
176
|
+
pid = state.get('pid', 0)
|
|
177
|
+
status = "running" if state_manager.is_process_alive(pid) else "stopped"
|
|
178
|
+
interval = state.get('interval', 'unknown')[:12]
|
|
179
|
+
task = state.get('task', '')[:40]
|
|
180
|
+
|
|
181
|
+
status_icon = "š¢" if status == "running" else "š“"
|
|
182
|
+
print(f"{name:<20} {status_icon} {status:<8} {pid:<8} {interval:<12} {task:<40}")
|
|
183
|
+
|
|
184
|
+
print(f"\nTotal: {len(states)} scheduler(s)")
|
|
185
|
+
return 0
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def _handle_stats(state_manager, unknown_args=None, daemon_manager=None) -> int:
|
|
189
|
+
"""Handle 'schedule stats' command - show aggregate or individual statistics."""
|
|
190
|
+
# If a name is provided, show individual stats (alias for describe)
|
|
191
|
+
if unknown_args and len(unknown_args) > 0:
|
|
192
|
+
name = unknown_args[0]
|
|
193
|
+
return AgentSchedulerHandler._handle_describe([name], state_manager, daemon_manager)
|
|
194
|
+
|
|
195
|
+
# Otherwise show aggregate stats
|
|
196
|
+
states = state_manager.list_all()
|
|
197
|
+
|
|
198
|
+
if not states:
|
|
199
|
+
print("No schedulers found.")
|
|
200
|
+
print("\nStart one with:")
|
|
201
|
+
print(' praisonai schedule start <name> "Your task" --interval hourly')
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
# Calculate aggregate stats
|
|
205
|
+
total_schedulers = len(states)
|
|
206
|
+
running = sum(1 for s in states if s.get('status') == 'running')
|
|
207
|
+
stopped = total_schedulers - running
|
|
208
|
+
total_executions = sum(s.get('executions', 0) for s in states)
|
|
209
|
+
total_cost = sum(s.get('cost', 0.0) for s in states)
|
|
210
|
+
|
|
211
|
+
print(f"\n{'='*60}")
|
|
212
|
+
print(f"š Aggregate Scheduler Statistics")
|
|
213
|
+
print(f"{'='*60}")
|
|
214
|
+
print(f"Total Schedulers: {total_schedulers}")
|
|
215
|
+
print(f" š¢ Running: {running}")
|
|
216
|
+
print(f" š“ Stopped: {stopped}")
|
|
217
|
+
print(f"\nTotal Executions: {total_executions}")
|
|
218
|
+
print(f"Total Cost: ${total_cost:.4f}")
|
|
219
|
+
|
|
220
|
+
if total_executions > 0:
|
|
221
|
+
avg_cost = total_cost / total_executions
|
|
222
|
+
print(f"Avg Cost/Execution: ${avg_cost:.4f}")
|
|
223
|
+
|
|
224
|
+
print(f"{'='*60}\n")
|
|
225
|
+
|
|
226
|
+
# Show per-scheduler breakdown
|
|
227
|
+
print("Per-Scheduler Breakdown:")
|
|
228
|
+
print(f"{'Name':<20} {'Status':<10} {'Executions':<12} {'Cost':<12}")
|
|
229
|
+
print("="*60)
|
|
230
|
+
|
|
231
|
+
for state in sorted(states, key=lambda x: x.get('executions', 0), reverse=True):
|
|
232
|
+
name = state['name'][:20]
|
|
233
|
+
status = state.get('status', 'unknown')
|
|
234
|
+
executions = state.get('executions', 0)
|
|
235
|
+
cost = state.get('cost', 0.0)
|
|
236
|
+
|
|
237
|
+
status_icon = "š¢" if status == "running" else "š“"
|
|
238
|
+
print(f"{name:<20} {status_icon} {status:<8} {executions:<12} ${cost:<11.4f}")
|
|
239
|
+
|
|
240
|
+
return 0
|
|
241
|
+
|
|
242
|
+
@staticmethod
|
|
243
|
+
def _handle_stop_all(state_manager, daemon_manager) -> int:
|
|
244
|
+
"""Handle 'schedule stop-all' command."""
|
|
245
|
+
states = state_manager.list_all()
|
|
246
|
+
|
|
247
|
+
if not states:
|
|
248
|
+
print("No schedulers running.")
|
|
249
|
+
return 0
|
|
250
|
+
|
|
251
|
+
print(f"\nš Stopping all schedulers ({len(states)} total)...\n")
|
|
252
|
+
|
|
253
|
+
stopped = 0
|
|
254
|
+
failed = 0
|
|
255
|
+
|
|
256
|
+
for state in states:
|
|
257
|
+
name = state['name']
|
|
258
|
+
pid = state['pid']
|
|
259
|
+
|
|
260
|
+
try:
|
|
261
|
+
if daemon_manager.stop_daemon(pid):
|
|
262
|
+
state_manager.delete_state(name)
|
|
263
|
+
print(f"ā
Stopped '{name}' (PID: {pid})")
|
|
264
|
+
stopped += 1
|
|
265
|
+
else:
|
|
266
|
+
print(f"ā Failed to stop '{name}' (PID: {pid})")
|
|
267
|
+
failed += 1
|
|
268
|
+
except Exception as e:
|
|
269
|
+
print(f"ā Error stopping '{name}': {e}")
|
|
270
|
+
failed += 1
|
|
271
|
+
|
|
272
|
+
print(f"\nš Summary: {stopped} stopped, {failed} failed")
|
|
273
|
+
return 0 if failed == 0 else 1
|
|
274
|
+
|
|
275
|
+
@staticmethod
|
|
276
|
+
def _handle_stop(unknown_args, state_manager, daemon_manager) -> int:
|
|
277
|
+
"""Handle 'schedule stop' command."""
|
|
278
|
+
if not unknown_args:
|
|
279
|
+
print("ā Error: Please provide scheduler name")
|
|
280
|
+
print("\nUsage: praisonai schedule stop <name>")
|
|
281
|
+
return 1
|
|
282
|
+
|
|
283
|
+
name = unknown_args[0]
|
|
284
|
+
state = state_manager.load_state(name)
|
|
285
|
+
|
|
286
|
+
if not state:
|
|
287
|
+
print(f"ā Error: Scheduler '{name}' not found")
|
|
288
|
+
print("\nList schedulers with: praisonai schedule list")
|
|
289
|
+
return 1
|
|
290
|
+
|
|
291
|
+
pid = state.get('pid')
|
|
292
|
+
if not pid:
|
|
293
|
+
print(f"ā Error: No PID found for scheduler '{name}'")
|
|
294
|
+
return 1
|
|
295
|
+
|
|
296
|
+
print(f"š Stopping scheduler '{name}' (PID: {pid})...")
|
|
297
|
+
|
|
298
|
+
success = daemon_manager.stop_daemon(pid)
|
|
299
|
+
|
|
300
|
+
if success:
|
|
301
|
+
state['status'] = 'stopped'
|
|
302
|
+
state_manager.save_state(name, state)
|
|
303
|
+
print(f"ā
Scheduler '{name}' stopped successfully")
|
|
304
|
+
return 0
|
|
305
|
+
else:
|
|
306
|
+
print(f"ā Failed to stop scheduler '{name}'")
|
|
307
|
+
return 1
|
|
308
|
+
|
|
309
|
+
@staticmethod
|
|
310
|
+
def _handle_logs(unknown_args, daemon_manager) -> int:
|
|
311
|
+
"""Handle 'schedule logs' command."""
|
|
312
|
+
if not unknown_args:
|
|
313
|
+
print("ā Error: Please provide scheduler name")
|
|
314
|
+
print("\nUsage: praisonai schedule logs <name> [-f]")
|
|
315
|
+
return 1
|
|
316
|
+
|
|
317
|
+
name = unknown_args[0]
|
|
318
|
+
follow = '-f' in unknown_args or '--follow' in unknown_args
|
|
319
|
+
|
|
320
|
+
if follow:
|
|
321
|
+
print(f"š Following logs for '{name}' (Ctrl+C to stop)...")
|
|
322
|
+
import subprocess
|
|
323
|
+
log_file = daemon_manager.log_dir / f"{name}.log"
|
|
324
|
+
if not log_file.exists():
|
|
325
|
+
print(f"ā Error: Log file not found for '{name}'")
|
|
326
|
+
return 1
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
subprocess.run(['tail', '-f', str(log_file)])
|
|
330
|
+
except KeyboardInterrupt:
|
|
331
|
+
print("\nā
Stopped following logs")
|
|
332
|
+
return 0
|
|
333
|
+
else:
|
|
334
|
+
logs = daemon_manager.read_logs(name, lines=50)
|
|
335
|
+
if logs:
|
|
336
|
+
print(f"š Last 50 lines of logs for '{name}':\n")
|
|
337
|
+
print(logs)
|
|
338
|
+
return 0
|
|
339
|
+
else:
|
|
340
|
+
print(f"ā Error: No logs found for '{name}'")
|
|
341
|
+
return 1
|
|
342
|
+
|
|
343
|
+
@staticmethod
|
|
344
|
+
def _handle_restart(unknown_args, state_manager, daemon_manager) -> int:
|
|
345
|
+
"""Handle 'schedule restart' command."""
|
|
346
|
+
from datetime import datetime
|
|
347
|
+
import time
|
|
348
|
+
|
|
349
|
+
if not unknown_args:
|
|
350
|
+
print("ā Error: Please provide scheduler name")
|
|
351
|
+
print("\nUsage: praisonai schedule restart <name>")
|
|
352
|
+
return 1
|
|
353
|
+
|
|
354
|
+
name = unknown_args[0]
|
|
355
|
+
state = state_manager.load_state(name)
|
|
356
|
+
|
|
357
|
+
if not state:
|
|
358
|
+
print(f"ā Error: Scheduler '{name}' not found")
|
|
359
|
+
return 1
|
|
360
|
+
|
|
361
|
+
print(f"š Restarting scheduler '{name}'...")
|
|
362
|
+
|
|
363
|
+
# Stop if running
|
|
364
|
+
pid = state.get('pid')
|
|
365
|
+
if pid and state_manager.is_process_alive(pid):
|
|
366
|
+
daemon_manager.stop_daemon(pid)
|
|
367
|
+
time.sleep(1)
|
|
368
|
+
|
|
369
|
+
# Start again
|
|
370
|
+
new_pid = daemon_manager.start_scheduler_daemon(
|
|
371
|
+
name=name,
|
|
372
|
+
task=state['task'],
|
|
373
|
+
interval=state['interval'],
|
|
374
|
+
max_cost=state.get('max_cost'),
|
|
375
|
+
timeout=state.get('timeout'),
|
|
376
|
+
max_retries=state.get('max_retries', 3)
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
state['pid'] = new_pid
|
|
380
|
+
state['status'] = 'running'
|
|
381
|
+
state['started_at'] = datetime.now().isoformat()
|
|
382
|
+
state_manager.save_state(name, state)
|
|
383
|
+
|
|
384
|
+
print(f"ā
Scheduler '{name}' restarted (PID: {new_pid})")
|
|
385
|
+
return 0
|
|
386
|
+
|
|
387
|
+
@staticmethod
|
|
388
|
+
def _handle_delete(unknown_args, state_manager) -> int:
|
|
389
|
+
"""Handle 'schedule delete' command."""
|
|
390
|
+
if not unknown_args:
|
|
391
|
+
print("ā Error: Please provide scheduler name")
|
|
392
|
+
print("\nUsage: praisonai schedule delete <name>")
|
|
393
|
+
return 1
|
|
394
|
+
|
|
395
|
+
name = unknown_args[0]
|
|
396
|
+
|
|
397
|
+
if state_manager.delete_state(name):
|
|
398
|
+
print(f"ā
Scheduler '{name}' deleted from list")
|
|
399
|
+
return 0
|
|
400
|
+
else:
|
|
401
|
+
print(f"ā Error: Scheduler '{name}' not found")
|
|
402
|
+
return 1
|
|
403
|
+
|
|
404
|
+
@staticmethod
|
|
405
|
+
def _handle_describe(unknown_args, state_manager, daemon_manager) -> int:
|
|
406
|
+
"""Handle 'schedule describe' command."""
|
|
407
|
+
from datetime import datetime
|
|
408
|
+
|
|
409
|
+
if not unknown_args:
|
|
410
|
+
print("ā Error: Please provide scheduler name")
|
|
411
|
+
print("\nUsage: praisonai schedule describe <name>")
|
|
412
|
+
return 1
|
|
413
|
+
|
|
414
|
+
name = unknown_args[0]
|
|
415
|
+
state = state_manager.load_state(name)
|
|
416
|
+
|
|
417
|
+
if not state:
|
|
418
|
+
print(f"ā Error: Scheduler '{name}' not found")
|
|
419
|
+
print("\nList schedulers with: praisonai schedule list")
|
|
420
|
+
return 1
|
|
421
|
+
|
|
422
|
+
# Get process status
|
|
423
|
+
pid = state.get('pid', 0)
|
|
424
|
+
is_alive = state_manager.is_process_alive(pid)
|
|
425
|
+
status = "š¢ running" if is_alive else "š“ stopped"
|
|
426
|
+
|
|
427
|
+
# Calculate uptime
|
|
428
|
+
started_at = state.get('started_at')
|
|
429
|
+
uptime = "N/A"
|
|
430
|
+
if started_at:
|
|
431
|
+
try:
|
|
432
|
+
start_time = datetime.fromisoformat(started_at)
|
|
433
|
+
uptime_delta = datetime.now() - start_time
|
|
434
|
+
hours = int(uptime_delta.total_seconds() // 3600)
|
|
435
|
+
minutes = int((uptime_delta.total_seconds() % 3600) // 60)
|
|
436
|
+
uptime = f"{hours}h {minutes}m"
|
|
437
|
+
except:
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
# Display detailed info
|
|
441
|
+
print(f"\n{'='*60}")
|
|
442
|
+
print(f"š Scheduler Details: {name}")
|
|
443
|
+
print(f"{'='*60}")
|
|
444
|
+
print(f"Status: {status}")
|
|
445
|
+
print(f"PID: {pid}")
|
|
446
|
+
print(f"Uptime: {uptime}")
|
|
447
|
+
print(f"Task: {state.get('task', 'N/A')}")
|
|
448
|
+
print(f"Interval: {state.get('interval', 'N/A')}")
|
|
449
|
+
print(f"Max Retries: {state.get('max_retries', 'N/A')}")
|
|
450
|
+
|
|
451
|
+
if state.get('timeout'):
|
|
452
|
+
print(f"Timeout: {state['timeout']}s")
|
|
453
|
+
|
|
454
|
+
if state.get('max_cost'):
|
|
455
|
+
print(f"Budget: ${state['max_cost']}")
|
|
456
|
+
|
|
457
|
+
print(f"Executions: {state.get('executions', 0)}")
|
|
458
|
+
print(f"Total Cost: ${state.get('cost', 0.0):.4f}")
|
|
459
|
+
print(f"Started: {started_at or 'N/A'}")
|
|
460
|
+
|
|
461
|
+
# Log file location
|
|
462
|
+
log_file = daemon_manager.log_dir / f"{name}.log"
|
|
463
|
+
print(f"Logs: {log_file}")
|
|
464
|
+
|
|
465
|
+
print(f"{'='*60}\n")
|
|
466
|
+
|
|
467
|
+
return 0
|
|
468
|
+
|
|
469
|
+
@staticmethod
|
|
470
|
+
def _handle_save(unknown_args, state_manager) -> int:
|
|
471
|
+
"""Handle 'schedule save' command."""
|
|
472
|
+
import yaml
|
|
473
|
+
|
|
474
|
+
if not unknown_args:
|
|
475
|
+
print("ā Error: Please provide scheduler name")
|
|
476
|
+
print("\nUsage: praisonai schedule save <name> [output.yaml]")
|
|
477
|
+
return 1
|
|
478
|
+
|
|
479
|
+
name = unknown_args[0]
|
|
480
|
+
output_file = unknown_args[1] if len(unknown_args) > 1 else f"{name}.yaml"
|
|
481
|
+
|
|
482
|
+
state = state_manager.load_state(name)
|
|
483
|
+
|
|
484
|
+
if not state:
|
|
485
|
+
print(f"ā Error: Scheduler '{name}' not found")
|
|
486
|
+
return 1
|
|
487
|
+
|
|
488
|
+
# Create YAML config from state
|
|
489
|
+
yaml_config = {
|
|
490
|
+
'framework': 'praisonai',
|
|
491
|
+
'agents': [{
|
|
492
|
+
'name': name,
|
|
493
|
+
'role': 'Task Executor',
|
|
494
|
+
'goal': state.get('task', ''),
|
|
495
|
+
'instructions': state.get('task', ''),
|
|
496
|
+
'verbose': True
|
|
497
|
+
}],
|
|
498
|
+
'task': state.get('task', ''),
|
|
499
|
+
'schedule': {
|
|
500
|
+
'interval': state.get('interval', 'hourly'),
|
|
501
|
+
'max_retries': state.get('max_retries', 3),
|
|
502
|
+
'run_immediately': True
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
# Add optional fields
|
|
507
|
+
if state.get('timeout'):
|
|
508
|
+
yaml_config['schedule']['timeout'] = state['timeout']
|
|
509
|
+
|
|
510
|
+
if state.get('max_cost'):
|
|
511
|
+
yaml_config['schedule']['max_cost'] = state['max_cost']
|
|
512
|
+
|
|
513
|
+
# Write to file
|
|
514
|
+
try:
|
|
515
|
+
with open(output_file, 'w') as f:
|
|
516
|
+
yaml.dump(yaml_config, f, default_flow_style=False, sort_keys=False)
|
|
517
|
+
|
|
518
|
+
print(f"ā
Configuration saved to: {output_file}")
|
|
519
|
+
print(f"\nRun with:")
|
|
520
|
+
print(f" praisonai schedule {output_file}")
|
|
521
|
+
return 0
|
|
522
|
+
except Exception as e:
|
|
523
|
+
print(f"ā Error saving configuration: {e}")
|
|
524
|
+
return 1
|
|
525
|
+
|
|
526
|
+
@staticmethod
|
|
527
|
+
def handle_schedule_command(args, unknown_args, daemon_mode=False) -> int:
|
|
528
|
+
"""
|
|
529
|
+
Handle the schedule command for running agents periodically.
|
|
530
|
+
|
|
531
|
+
Supports two modes:
|
|
532
|
+
1. YAML mode: praisonai schedule agents.yaml
|
|
533
|
+
2. Prompt mode: praisonai schedule "Your task here" --interval hourly
|
|
534
|
+
|
|
535
|
+
Args:
|
|
536
|
+
args: Parsed command-line arguments
|
|
537
|
+
unknown_args: Additional arguments after the command
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
Exit code (0 for success, 1 for error)
|
|
541
|
+
"""
|
|
542
|
+
try:
|
|
543
|
+
from praisonai.scheduler import AgentScheduler
|
|
544
|
+
from praisonaiagents import Agent
|
|
545
|
+
except ImportError:
|
|
546
|
+
print("Error: praisonai.scheduler or praisonaiagents module not found")
|
|
547
|
+
print("Please ensure PraisonAI is properly installed")
|
|
548
|
+
print("pip install praisonai praisonaiagents")
|
|
549
|
+
return 1
|
|
550
|
+
|
|
551
|
+
# Check if first arg is a YAML file or a prompt
|
|
552
|
+
first_arg = unknown_args[0] if unknown_args else None
|
|
553
|
+
is_yaml_mode = first_arg and (first_arg.endswith('.yaml') or first_arg.endswith('.yml'))
|
|
554
|
+
|
|
555
|
+
# Get overrides from CLI
|
|
556
|
+
interval_override = getattr(args, 'schedule_interval', None) or 'hourly'
|
|
557
|
+
max_retries_override = getattr(args, 'schedule_max_retries', None) or 3
|
|
558
|
+
timeout_override = getattr(args, 'timeout', None)
|
|
559
|
+
max_cost_override = getattr(args, 'max_cost', None)
|
|
560
|
+
verbose = getattr(args, 'verbose', False)
|
|
561
|
+
|
|
562
|
+
# Set up logging - only show logs if verbose
|
|
563
|
+
if verbose:
|
|
564
|
+
logging.basicConfig(
|
|
565
|
+
level=logging.INFO,
|
|
566
|
+
format='[%(asctime)s] %(levelname)s - %(message)s',
|
|
567
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
568
|
+
)
|
|
569
|
+
else:
|
|
570
|
+
# Suppress scheduler logs in non-verbose mode
|
|
571
|
+
logging.getLogger('praisonai.scheduler').setLevel(logging.WARNING)
|
|
572
|
+
|
|
573
|
+
try:
|
|
574
|
+
# Check if this is a recipe name (not a file, not a prompt with spaces)
|
|
575
|
+
is_recipe_mode = False
|
|
576
|
+
if first_arg and not is_yaml_mode:
|
|
577
|
+
# Try to resolve as recipe if it looks like a recipe name (no spaces, no file extension)
|
|
578
|
+
if ' ' not in first_arg and not first_arg.endswith('.yaml') and not first_arg.endswith('.yml'):
|
|
579
|
+
try:
|
|
580
|
+
from praisonai.recipe.bridge import resolve
|
|
581
|
+
resolve(first_arg) # Just check if it resolves
|
|
582
|
+
is_recipe_mode = True
|
|
583
|
+
except Exception:
|
|
584
|
+
pass # Not a recipe, continue with prompt mode
|
|
585
|
+
|
|
586
|
+
if is_yaml_mode:
|
|
587
|
+
# YAML mode: Load from agents.yaml
|
|
588
|
+
yaml_path = first_arg
|
|
589
|
+
print(f"š¤ Loading agent configuration from: {yaml_path}")
|
|
590
|
+
scheduler = AgentScheduler.from_yaml(
|
|
591
|
+
yaml_path=yaml_path,
|
|
592
|
+
interval_override=interval_override,
|
|
593
|
+
max_retries_override=max_retries_override,
|
|
594
|
+
timeout_override=timeout_override,
|
|
595
|
+
max_cost_override=max_cost_override
|
|
596
|
+
)
|
|
597
|
+
elif is_recipe_mode:
|
|
598
|
+
# Recipe mode: Load from recipe name
|
|
599
|
+
print(f"š³ Loading recipe: {first_arg}")
|
|
600
|
+
scheduler = AgentScheduler.from_recipe(
|
|
601
|
+
recipe_name=first_arg,
|
|
602
|
+
interval_override=interval_override,
|
|
603
|
+
max_retries_override=max_retries_override,
|
|
604
|
+
timeout_override=timeout_override,
|
|
605
|
+
max_cost_override=max_cost_override
|
|
606
|
+
)
|
|
607
|
+
else:
|
|
608
|
+
# Prompt mode: Create agent from direct prompt
|
|
609
|
+
if not first_arg:
|
|
610
|
+
print("ā Error: Please provide either a YAML file, recipe name, or a task prompt")
|
|
611
|
+
print("\nExamples:")
|
|
612
|
+
print(" praisonai schedule agents.yaml")
|
|
613
|
+
print(" praisonai schedule my-recipe --interval hourly")
|
|
614
|
+
print(' praisonai schedule "Check news every hour" --interval hourly')
|
|
615
|
+
return 1
|
|
616
|
+
|
|
617
|
+
task = first_arg
|
|
618
|
+
|
|
619
|
+
# Create agent
|
|
620
|
+
agent = Agent(
|
|
621
|
+
name="Scheduled Agent",
|
|
622
|
+
role="Task Executor",
|
|
623
|
+
goal=task,
|
|
624
|
+
instructions=task,
|
|
625
|
+
verbose=True # Enable verbose to see output in logs
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
# Create scheduler
|
|
629
|
+
scheduler = AgentScheduler(
|
|
630
|
+
agent=agent,
|
|
631
|
+
task=task,
|
|
632
|
+
timeout=timeout_override,
|
|
633
|
+
max_cost=max_cost_override
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
# Get configuration
|
|
637
|
+
interval = interval_override
|
|
638
|
+
max_retries = max_retries_override
|
|
639
|
+
|
|
640
|
+
if is_yaml_mode:
|
|
641
|
+
schedule_config = scheduler._yaml_schedule_config
|
|
642
|
+
interval = interval_override or schedule_config.get('interval', 'hourly')
|
|
643
|
+
max_retries = max_retries_override or schedule_config.get('max_retries', 3)
|
|
644
|
+
|
|
645
|
+
print(f"Schedule: {interval}")
|
|
646
|
+
print(f"Max Retries: {max_retries}")
|
|
647
|
+
if scheduler.timeout:
|
|
648
|
+
print(f"Timeout: {scheduler.timeout}s")
|
|
649
|
+
if scheduler.max_cost:
|
|
650
|
+
print(f"Budget: ${scheduler.max_cost}")
|
|
651
|
+
print(f"{'='*60}\n")
|
|
652
|
+
|
|
653
|
+
# Start scheduler
|
|
654
|
+
if is_yaml_mode:
|
|
655
|
+
scheduler.start_from_yaml_config()
|
|
656
|
+
else:
|
|
657
|
+
scheduler.start(schedule_expr=interval, max_retries=max_retries, run_immediately=True)
|
|
658
|
+
|
|
659
|
+
# If daemon mode, block to keep process alive
|
|
660
|
+
if daemon_mode:
|
|
661
|
+
import time
|
|
662
|
+
try:
|
|
663
|
+
while scheduler.is_running:
|
|
664
|
+
time.sleep(1)
|
|
665
|
+
except KeyboardInterrupt:
|
|
666
|
+
scheduler.stop()
|
|
667
|
+
else:
|
|
668
|
+
# Foreground mode - wait for Ctrl+C
|
|
669
|
+
print("ā° Starting scheduler... (Press Ctrl+C to stop)\n")
|
|
670
|
+
import time
|
|
671
|
+
try:
|
|
672
|
+
while scheduler.is_running:
|
|
673
|
+
time.sleep(1)
|
|
674
|
+
except KeyboardInterrupt:
|
|
675
|
+
print("\n\nš Stopping scheduler...")
|
|
676
|
+
scheduler.stop()
|
|
677
|
+
|
|
678
|
+
# Display final stats
|
|
679
|
+
stats = scheduler.get_stats()
|
|
680
|
+
print("\nš Final Statistics:")
|
|
681
|
+
print(f" Total Executions: {stats['total_executions']}")
|
|
682
|
+
print(f" Successful: {stats['successful_executions']}")
|
|
683
|
+
print(f" Failed: {stats['failed_executions']}")
|
|
684
|
+
print(f" Success Rate: {stats['success_rate']:.1f}%")
|
|
685
|
+
print("\nā
Agent stopped successfully\n")
|
|
686
|
+
|
|
687
|
+
return 0
|
|
688
|
+
|
|
689
|
+
except FileNotFoundError as e:
|
|
690
|
+
print(f"ā Error: {e}")
|
|
691
|
+
print(f"\nMake sure {yaml_path} exists in the current directory.")
|
|
692
|
+
print("\nExample agents.yaml structure:")
|
|
693
|
+
print("""
|
|
694
|
+
framework: praisonai
|
|
695
|
+
|
|
696
|
+
agents:
|
|
697
|
+
- name: "AI News Monitor"
|
|
698
|
+
role: "News Analyst"
|
|
699
|
+
instructions: "Search and summarize AI news"
|
|
700
|
+
tools:
|
|
701
|
+
- search_tool
|
|
702
|
+
|
|
703
|
+
task: "Search for latest AI news"
|
|
704
|
+
|
|
705
|
+
schedule:
|
|
706
|
+
interval: "hourly"
|
|
707
|
+
max_retries: 3
|
|
708
|
+
run_immediately: true
|
|
709
|
+
""")
|
|
710
|
+
return 1
|
|
711
|
+
|
|
712
|
+
except ValueError as e:
|
|
713
|
+
print(f"ā Configuration Error: {e}")
|
|
714
|
+
return 1
|
|
715
|
+
|
|
716
|
+
except Exception as e:
|
|
717
|
+
print(f"ā Error: {e}")
|
|
718
|
+
if verbose:
|
|
719
|
+
import traceback
|
|
720
|
+
traceback.print_exc()
|
|
721
|
+
return 1
|
|
722
|
+
|
|
723
|
+
@staticmethod
|
|
724
|
+
def add_schedule_arguments(subparsers):
|
|
725
|
+
"""
|
|
726
|
+
Add schedule subcommand and arguments to parser.
|
|
727
|
+
|
|
728
|
+
Args:
|
|
729
|
+
subparsers: Subparsers object from argparse
|
|
730
|
+
"""
|
|
731
|
+
schedule_parser = subparsers.add_parser(
|
|
732
|
+
'schedule',
|
|
733
|
+
help='Schedule an agent to run continuously at regular intervals'
|
|
734
|
+
)
|
|
735
|
+
|
|
736
|
+
schedule_parser.add_argument(
|
|
737
|
+
'schedule_yaml',
|
|
738
|
+
nargs='?',
|
|
739
|
+
default='agents.yaml',
|
|
740
|
+
help='Path to agents.yaml file (default: agents.yaml)'
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
schedule_parser.add_argument(
|
|
744
|
+
'--interval',
|
|
745
|
+
dest='schedule_interval',
|
|
746
|
+
type=str,
|
|
747
|
+
help='Override schedule interval (e.g., "hourly", "*/30m", "daily")'
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
schedule_parser.add_argument(
|
|
751
|
+
'--max-retries',
|
|
752
|
+
dest='schedule_max_retries',
|
|
753
|
+
type=int,
|
|
754
|
+
help='Override maximum retry attempts (default: from YAML or 3)'
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
schedule_parser.add_argument(
|
|
758
|
+
'--verbose', '-v',
|
|
759
|
+
action='store_true',
|
|
760
|
+
help='Enable verbose logging'
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
schedule_parser.set_defaults(func=AgentSchedulerHandler.handle_schedule_command)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def setup_scheduler_cli(subparsers):
|
|
767
|
+
"""
|
|
768
|
+
Setup scheduler CLI commands.
|
|
769
|
+
|
|
770
|
+
Args:
|
|
771
|
+
subparsers: Subparsers object from argparse
|
|
772
|
+
"""
|
|
773
|
+
AgentSchedulerHandler.add_schedule_arguments(subparsers)
|