praisonAI 2.3.63__tar.gz → 2.3.65__tar.gz

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.
Files changed (117) hide show
  1. {praisonai-2.3.63 → praisonai-2.3.65}/PKG-INFO +33 -1
  2. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/PKG-INFO +33 -1
  3. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/SOURCES.txt +7 -0
  4. {praisonai-2.3.63 → praisonai-2.3.65}/README.md +32 -0
  5. praisonai-2.3.65/praisonai/agent_scheduler.py +337 -0
  6. praisonai-2.3.65/praisonai/cli/features/agent_scheduler.py +182 -0
  7. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/main.py +10 -1
  8. praisonai-2.3.65/praisonai/cli/schedule_cli.py +54 -0
  9. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/deploy.py +1 -1
  10. praisonai-2.3.65/praisonai/scheduler/__init__.py +36 -0
  11. praisonai-2.3.65/praisonai/scheduler/agent_scheduler.py +348 -0
  12. praisonai-2.3.65/praisonai/scheduler/base.py +124 -0
  13. praisonai-2.3.65/praisonai/scheduler/yaml_loader.py +197 -0
  14. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/scheduler.py +22 -43
  15. praisonai-2.3.65/praisonai/version.py +1 -0
  16. praisonai-2.3.63/praisonai/version.py +0 -1
  17. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/dependency_links.txt +0 -0
  18. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/entry_points.txt +0 -0
  19. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/requires.txt +0 -0
  20. {praisonai-2.3.63 → praisonai-2.3.65}/PraisonAI.egg-info/top_level.txt +0 -0
  21. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/__init__.py +0 -0
  22. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/__main__.py +0 -0
  23. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/agents_generator.py +0 -0
  24. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/api/call.py +0 -0
  25. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/auto.py +0 -0
  26. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/chainlit_ui.py +0 -0
  27. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/__init__.py +0 -0
  28. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/__init__.py +0 -0
  29. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/at_mentions.py +0 -0
  30. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/auto_memory.py +0 -0
  31. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/autonomy_mode.py +0 -0
  32. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/base.py +0 -0
  33. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/cost_tracker.py +0 -0
  34. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/external_agents.py +0 -0
  35. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/fast_context.py +0 -0
  36. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/flow_display.py +0 -0
  37. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/git_integration.py +0 -0
  38. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/guardrail.py +0 -0
  39. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/handoff.py +0 -0
  40. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/image.py +0 -0
  41. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/interactive_tui.py +0 -0
  42. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/knowledge.py +0 -0
  43. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/mcp.py +0 -0
  44. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/message_queue.py +0 -0
  45. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/metrics.py +0 -0
  46. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/n8n.py +0 -0
  47. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/repo_map.py +0 -0
  48. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/router.py +0 -0
  49. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/sandbox_executor.py +0 -0
  50. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/session.py +0 -0
  51. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/slash_commands.py +0 -0
  52. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/telemetry.py +0 -0
  53. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/todo.py +0 -0
  54. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/tools.py +0 -0
  55. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/cli/features/workflow.py +0 -0
  56. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/__init__.py +0 -0
  57. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/agent_tools.py +0 -0
  58. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/diff/__init__.py +0 -0
  59. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/diff/diff_strategy.py +0 -0
  60. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/__init__.py +0 -0
  61. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/apply_diff.py +0 -0
  62. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/execute_command.py +0 -0
  63. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/list_files.py +0 -0
  64. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/read_file.py +0 -0
  65. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/search_replace.py +0 -0
  66. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/tools/write_file.py +0 -0
  67. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/utils/__init__.py +0 -0
  68. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/utils/file_utils.py +0 -0
  69. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/utils/ignore_utils.py +0 -0
  70. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/code/utils/text_utils.py +0 -0
  71. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/inbuilt_tools/__init__.py +0 -0
  72. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/inbuilt_tools/autogen_tools.py +0 -0
  73. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/inc/__init__.py +0 -0
  74. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/inc/config.py +0 -0
  75. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/inc/models.py +0 -0
  76. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/__init__.py +0 -0
  77. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/base.py +0 -0
  78. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/claude_code.py +0 -0
  79. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/codex_cli.py +0 -0
  80. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/cursor_cli.py +0 -0
  81. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/integrations/gemini_cli.py +0 -0
  82. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/profiler.py +0 -0
  83. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/setup/__init__.py +0 -0
  84. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/setup/build.py +0 -0
  85. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/setup/post_install.py +0 -0
  86. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/setup/setup_conda_env.py +0 -0
  87. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/setup.py +0 -0
  88. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/test.py +0 -0
  89. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/train.py +0 -0
  90. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/train_vision.py +0 -0
  91. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/agents.py +0 -0
  92. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/callbacks.py +0 -0
  93. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/chat.py +0 -0
  94. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/code.py +0 -0
  95. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/colab.py +0 -0
  96. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/colab_chainlit.py +0 -0
  97. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/components/aicoder.py +0 -0
  98. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/context.py +0 -0
  99. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/database_config.py +0 -0
  100. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/db.py +0 -0
  101. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/realtime.py +0 -0
  102. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/realtimeclient/__init__.py +0 -0
  103. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/realtimeclient/tools.py +0 -0
  104. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/ui/sql_alchemy.py +0 -0
  105. {praisonai-2.3.63 → praisonai-2.3.65}/praisonai/upload_vision.py +0 -0
  106. {praisonai-2.3.63 → praisonai-2.3.65}/pyproject.toml +0 -0
  107. {praisonai-2.3.63 → praisonai-2.3.65}/setup.cfg +0 -0
  108. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test.py +0 -0
  109. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_agents_playbook.py +0 -0
  110. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_approval_system.py +0 -0
  111. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_basic.py +0 -0
  112. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_cli_features.py +0 -0
  113. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_custom_tools_fix.py +0 -0
  114. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_double_api_fix.py +0 -0
  115. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_n8n_integration.py +0 -0
  116. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_phase2_refactor.py +0 -0
  117. {praisonai-2.3.63 → praisonai-2.3.65}/tests/test_runner.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.63
3
+ Version: 2.3.65
4
4
  Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -363,6 +363,8 @@ npm install praisonai
363
363
  | ↳ Interactive TUI | [Example](examples/python/cli/interactive_tui_example.py) | [📖](https://docs.praison.ai/docs/cli/interactive-tui) |
364
364
  | ↳ Git Integration | [Example](examples/python/cli/git_integration_example.py) | [📖](https://docs.praison.ai/docs/cli/git-integration) |
365
365
  | ↳ Sandbox Execution | [Example](examples/python/cli/sandbox_execution_example.py) | [📖](https://docs.praison.ai/docs/cli/sandbox-execution) |
366
+ | **⏰ 24/7 Scheduling** | | |
367
+ | ↳ Agent Scheduler | [Example](examples/python/scheduled_agents/news_checker_live.py) | [📖](https://docs.praison.ai/docs/cli/scheduler) |
366
368
 
367
369
  ---
368
370
 
@@ -1339,6 +1341,36 @@ praisonai "Analyze this data" --metrics
1339
1341
  praisonai "Complex task" --metrics --planning
1340
1342
  ```
1341
1343
 
1344
+ ### Scheduler CLI:
1345
+ ```bash
1346
+ # Schedule an agent using agents.yaml
1347
+ praisonai schedule agents.yaml
1348
+
1349
+ # Override interval from CLI
1350
+ praisonai schedule agents.yaml --interval "*/30m"
1351
+
1352
+ # With verbose logging
1353
+ praisonai schedule agents.yaml --verbose
1354
+ ```
1355
+
1356
+ **agents.yaml with schedule:**
1357
+ ```yaml
1358
+ framework: praisonai
1359
+
1360
+ agents:
1361
+ - name: "AI News Monitor"
1362
+ instructions: "Search and summarize AI news"
1363
+ tools:
1364
+ - search_tool
1365
+
1366
+ task: "Search for latest AI news"
1367
+
1368
+ schedule:
1369
+ interval: "hourly" # hourly, daily, */30m, */6h
1370
+ max_retries: 3
1371
+ run_immediately: true
1372
+ ```
1373
+
1342
1374
  ### Image Processing CLI:
1343
1375
  ```bash
1344
1376
  # Process images with vision-based tasks
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PraisonAI
3
- Version: 2.3.63
3
+ Version: 2.3.65
4
4
  Summary: PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration.
5
5
  Author: Mervin Praison
6
6
  License: MIT
@@ -363,6 +363,8 @@ npm install praisonai
363
363
  | ↳ Interactive TUI | [Example](examples/python/cli/interactive_tui_example.py) | [📖](https://docs.praison.ai/docs/cli/interactive-tui) |
364
364
  | ↳ Git Integration | [Example](examples/python/cli/git_integration_example.py) | [📖](https://docs.praison.ai/docs/cli/git-integration) |
365
365
  | ↳ Sandbox Execution | [Example](examples/python/cli/sandbox_execution_example.py) | [📖](https://docs.praison.ai/docs/cli/sandbox-execution) |
366
+ | **⏰ 24/7 Scheduling** | | |
367
+ | ↳ Agent Scheduler | [Example](examples/python/scheduled_agents/news_checker_live.py) | [📖](https://docs.praison.ai/docs/cli/scheduler) |
366
368
 
367
369
  ---
368
370
 
@@ -1339,6 +1341,36 @@ praisonai "Analyze this data" --metrics
1339
1341
  praisonai "Complex task" --metrics --planning
1340
1342
  ```
1341
1343
 
1344
+ ### Scheduler CLI:
1345
+ ```bash
1346
+ # Schedule an agent using agents.yaml
1347
+ praisonai schedule agents.yaml
1348
+
1349
+ # Override interval from CLI
1350
+ praisonai schedule agents.yaml --interval "*/30m"
1351
+
1352
+ # With verbose logging
1353
+ praisonai schedule agents.yaml --verbose
1354
+ ```
1355
+
1356
+ **agents.yaml with schedule:**
1357
+ ```yaml
1358
+ framework: praisonai
1359
+
1360
+ agents:
1361
+ - name: "AI News Monitor"
1362
+ instructions: "Search and summarize AI news"
1363
+ tools:
1364
+ - search_tool
1365
+
1366
+ task: "Search for latest AI news"
1367
+
1368
+ schedule:
1369
+ interval: "hourly" # hourly, daily, */30m, */6h
1370
+ max_retries: 3
1371
+ run_immediately: true
1372
+ ```
1373
+
1342
1374
  ### Image Processing CLI:
1343
1375
  ```bash
1344
1376
  # Process images with vision-based tasks
@@ -8,6 +8,7 @@ PraisonAI.egg-info/requires.txt
8
8
  PraisonAI.egg-info/top_level.txt
9
9
  praisonai/__init__.py
10
10
  praisonai/__main__.py
11
+ praisonai/agent_scheduler.py
11
12
  praisonai/agents_generator.py
12
13
  praisonai/auto.py
13
14
  praisonai/chainlit_ui.py
@@ -23,7 +24,9 @@ praisonai/version.py
23
24
  praisonai/api/call.py
24
25
  praisonai/cli/__init__.py
25
26
  praisonai/cli/main.py
27
+ praisonai/cli/schedule_cli.py
26
28
  praisonai/cli/features/__init__.py
29
+ praisonai/cli/features/agent_scheduler.py
27
30
  praisonai/cli/features/at_mentions.py
28
31
  praisonai/cli/features/auto_memory.py
29
32
  praisonai/cli/features/autonomy_mode.py
@@ -77,6 +80,10 @@ praisonai/integrations/claude_code.py
77
80
  praisonai/integrations/codex_cli.py
78
81
  praisonai/integrations/cursor_cli.py
79
82
  praisonai/integrations/gemini_cli.py
83
+ praisonai/scheduler/__init__.py
84
+ praisonai/scheduler/agent_scheduler.py
85
+ praisonai/scheduler/base.py
86
+ praisonai/scheduler/yaml_loader.py
80
87
  praisonai/setup/__init__.py
81
88
  praisonai/setup/build.py
82
89
  praisonai/setup/post_install.py
@@ -266,6 +266,8 @@ npm install praisonai
266
266
  | ↳ Interactive TUI | [Example](examples/python/cli/interactive_tui_example.py) | [📖](https://docs.praison.ai/docs/cli/interactive-tui) |
267
267
  | ↳ Git Integration | [Example](examples/python/cli/git_integration_example.py) | [📖](https://docs.praison.ai/docs/cli/git-integration) |
268
268
  | ↳ Sandbox Execution | [Example](examples/python/cli/sandbox_execution_example.py) | [📖](https://docs.praison.ai/docs/cli/sandbox-execution) |
269
+ | **⏰ 24/7 Scheduling** | | |
270
+ | ↳ Agent Scheduler | [Example](examples/python/scheduled_agents/news_checker_live.py) | [📖](https://docs.praison.ai/docs/cli/scheduler) |
269
271
 
270
272
  ---
271
273
 
@@ -1242,6 +1244,36 @@ praisonai "Analyze this data" --metrics
1242
1244
  praisonai "Complex task" --metrics --planning
1243
1245
  ```
1244
1246
 
1247
+ ### Scheduler CLI:
1248
+ ```bash
1249
+ # Schedule an agent using agents.yaml
1250
+ praisonai schedule agents.yaml
1251
+
1252
+ # Override interval from CLI
1253
+ praisonai schedule agents.yaml --interval "*/30m"
1254
+
1255
+ # With verbose logging
1256
+ praisonai schedule agents.yaml --verbose
1257
+ ```
1258
+
1259
+ **agents.yaml with schedule:**
1260
+ ```yaml
1261
+ framework: praisonai
1262
+
1263
+ agents:
1264
+ - name: "AI News Monitor"
1265
+ instructions: "Search and summarize AI news"
1266
+ tools:
1267
+ - search_tool
1268
+
1269
+ task: "Search for latest AI news"
1270
+
1271
+ schedule:
1272
+ interval: "hourly" # hourly, daily, */30m, */6h
1273
+ max_retries: 3
1274
+ run_immediately: true
1275
+ ```
1276
+
1245
1277
  ### Image Processing CLI:
1246
1278
  ```bash
1247
1279
  # Process images with vision-based tasks
@@ -0,0 +1,337 @@
1
+ """
2
+ Agent Scheduler for PraisonAI - Run agents periodically 24/7.
3
+
4
+ This module provides scheduling capabilities for running PraisonAI agents
5
+ at regular intervals, enabling 24/7 autonomous agent operations.
6
+
7
+ Example:
8
+ # Run news checker every hour
9
+ from praisonai.agent_scheduler import AgentScheduler
10
+ from praisonai_agents import Agent
11
+
12
+ agent = Agent(
13
+ name="NewsChecker",
14
+ instructions="Check latest AI news and summarize",
15
+ tools=[search_tool]
16
+ )
17
+
18
+ scheduler = AgentScheduler(agent, task="Check latest AI news")
19
+ scheduler.start(schedule_expr="hourly")
20
+ """
21
+
22
+ import threading
23
+ import time
24
+ import logging
25
+ from datetime import datetime
26
+ from typing import Optional, Dict, Any, Callable
27
+ from abc import ABC, abstractmethod
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class ScheduleParser:
33
+ """Parse schedule expressions into intervals."""
34
+
35
+ @staticmethod
36
+ def parse(schedule_expr: str) -> int:
37
+ """
38
+ Parse schedule expression and return interval in seconds.
39
+
40
+ Supported formats:
41
+ - "daily" -> 86400 seconds
42
+ - "hourly" -> 3600 seconds
43
+ - "*/30m" -> 1800 seconds (every 30 minutes)
44
+ - "*/1h" -> 3600 seconds (every 1 hour)
45
+ - "60" -> 60 seconds (plain number)
46
+
47
+ Args:
48
+ schedule_expr: Schedule expression string
49
+
50
+ Returns:
51
+ Interval in seconds
52
+
53
+ Raises:
54
+ ValueError: If schedule format is not supported
55
+ """
56
+ schedule_expr = schedule_expr.strip().lower()
57
+
58
+ if schedule_expr == "daily":
59
+ return 86400
60
+ elif schedule_expr == "hourly":
61
+ return 3600
62
+ elif schedule_expr.isdigit():
63
+ return int(schedule_expr)
64
+ elif schedule_expr.startswith("*/"):
65
+ interval_part = schedule_expr[2:]
66
+ if interval_part.endswith("m"):
67
+ minutes = int(interval_part[:-1])
68
+ return minutes * 60
69
+ elif interval_part.endswith("h"):
70
+ hours = int(interval_part[:-1])
71
+ return hours * 3600
72
+ elif interval_part.endswith("s"):
73
+ return int(interval_part[:-1])
74
+ else:
75
+ return int(interval_part)
76
+ else:
77
+ raise ValueError(f"Unsupported schedule format: {schedule_expr}")
78
+
79
+
80
+ class AgentExecutorInterface(ABC):
81
+ """Abstract interface for agent execution."""
82
+
83
+ @abstractmethod
84
+ def execute(self, task: str) -> Any:
85
+ """Execute the agent with given task."""
86
+ pass
87
+
88
+
89
+ class PraisonAgentExecutor(AgentExecutorInterface):
90
+ """Executor for PraisonAI agents."""
91
+
92
+ def __init__(self, agent):
93
+ """
94
+ Initialize executor with a PraisonAI agent.
95
+
96
+ Args:
97
+ agent: PraisonAI Agent instance
98
+ """
99
+ self.agent = agent
100
+
101
+ def execute(self, task: str) -> Any:
102
+ """
103
+ Execute the agent with given task.
104
+
105
+ Args:
106
+ task: Task description for the agent
107
+
108
+ Returns:
109
+ Agent execution result
110
+ """
111
+ try:
112
+ result = self.agent.start(task)
113
+ return result
114
+ except Exception as e:
115
+ logger.error(f"Agent execution failed: {e}")
116
+ raise
117
+
118
+
119
+ class AgentScheduler:
120
+ """
121
+ Scheduler for running PraisonAI agents periodically.
122
+
123
+ Features:
124
+ - Interval-based scheduling (hourly, daily, custom)
125
+ - Thread-safe operation
126
+ - Automatic retry on failure
127
+ - Execution logging and monitoring
128
+ - Graceful shutdown
129
+
130
+ Example:
131
+ scheduler = AgentScheduler(agent, task="Check news")
132
+ scheduler.start(schedule_expr="hourly", max_retries=3)
133
+ # Agent runs every hour automatically
134
+ scheduler.stop() # Stop when needed
135
+ """
136
+
137
+ def __init__(
138
+ self,
139
+ agent,
140
+ task: str,
141
+ config: Optional[Dict[str, Any]] = None,
142
+ on_success: Optional[Callable] = None,
143
+ on_failure: Optional[Callable] = None
144
+ ):
145
+ """
146
+ Initialize agent scheduler.
147
+
148
+ Args:
149
+ agent: PraisonAI Agent instance
150
+ task: Task description to execute
151
+ config: Optional configuration dict
152
+ on_success: Callback function on successful execution
153
+ on_failure: Callback function on failed execution
154
+ """
155
+ self.agent = agent
156
+ self.task = task
157
+ self.config = config or {}
158
+ self.on_success = on_success
159
+ self.on_failure = on_failure
160
+
161
+ self.is_running = False
162
+ self._stop_event = threading.Event()
163
+ self._thread = None
164
+ self._executor = PraisonAgentExecutor(agent)
165
+ self._execution_count = 0
166
+ self._success_count = 0
167
+ self._failure_count = 0
168
+
169
+ def start(
170
+ self,
171
+ schedule_expr: str,
172
+ max_retries: int = 3,
173
+ run_immediately: bool = False
174
+ ) -> bool:
175
+ """
176
+ Start scheduled agent execution.
177
+
178
+ Args:
179
+ schedule_expr: Schedule expression (e.g., "hourly", "*/1h", "3600")
180
+ max_retries: Maximum retry attempts on failure
181
+ run_immediately: If True, run agent immediately before starting schedule
182
+
183
+ Returns:
184
+ True if scheduler started successfully
185
+ """
186
+ if self.is_running:
187
+ logger.warning("Scheduler is already running")
188
+ return False
189
+
190
+ try:
191
+ interval = ScheduleParser.parse(schedule_expr)
192
+ self.is_running = True
193
+ self._stop_event.clear()
194
+
195
+ logger.info(f"Starting agent scheduler: {self.agent.name if hasattr(self.agent, 'name') else 'Agent'}")
196
+ logger.info(f"Task: {self.task}")
197
+ logger.info(f"Schedule: {schedule_expr} ({interval}s interval)")
198
+ logger.info(f"Max retries: {max_retries}")
199
+
200
+ # Run immediately if requested
201
+ if run_immediately:
202
+ logger.info("Running agent immediately before starting schedule...")
203
+ self._execute_with_retry(max_retries)
204
+
205
+ self._thread = threading.Thread(
206
+ target=self._run_schedule,
207
+ args=(interval, max_retries),
208
+ daemon=True
209
+ )
210
+ self._thread.start()
211
+
212
+ logger.info("Agent scheduler started successfully")
213
+ return True
214
+
215
+ except Exception as e:
216
+ logger.error(f"Failed to start scheduler: {e}")
217
+ self.is_running = False
218
+ return False
219
+
220
+ def stop(self) -> bool:
221
+ """
222
+ Stop the scheduler gracefully.
223
+
224
+ Returns:
225
+ True if stopped successfully
226
+ """
227
+ if not self.is_running:
228
+ logger.info("Scheduler is not running")
229
+ return True
230
+
231
+ logger.info("Stopping agent scheduler...")
232
+ self._stop_event.set()
233
+
234
+ if self._thread and self._thread.is_alive():
235
+ self._thread.join(timeout=10)
236
+
237
+ self.is_running = False
238
+ logger.info("Agent scheduler stopped")
239
+ logger.info(f"Execution stats - Total: {self._execution_count}, Success: {self._success_count}, Failed: {self._failure_count}")
240
+ return True
241
+
242
+ def get_stats(self) -> Dict[str, Any]:
243
+ """
244
+ Get execution statistics.
245
+
246
+ Returns:
247
+ Dictionary with execution stats
248
+ """
249
+ return {
250
+ "is_running": self.is_running,
251
+ "total_executions": self._execution_count,
252
+ "successful_executions": self._success_count,
253
+ "failed_executions": self._failure_count,
254
+ "success_rate": (self._success_count / self._execution_count * 100) if self._execution_count > 0 else 0
255
+ }
256
+
257
+ def _run_schedule(self, interval: int, max_retries: int):
258
+ """Internal method to run scheduled agent executions."""
259
+ while not self._stop_event.is_set():
260
+ logger.info(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] Starting scheduled agent execution")
261
+
262
+ self._execute_with_retry(max_retries)
263
+
264
+ # Wait for next scheduled time
265
+ logger.info(f"Next execution in {interval} seconds ({interval/3600:.1f} hours)")
266
+ self._stop_event.wait(interval)
267
+
268
+ def _execute_with_retry(self, max_retries: int):
269
+ """Execute agent with retry logic."""
270
+ self._execution_count += 1
271
+ success = False
272
+
273
+ for attempt in range(max_retries):
274
+ try:
275
+ logger.info(f"Attempt {attempt + 1}/{max_retries}")
276
+ result = self._executor.execute(self.task)
277
+
278
+ logger.info(f"Agent execution successful on attempt {attempt + 1}")
279
+ logger.info(f"Result: {result}")
280
+
281
+ self._success_count += 1
282
+ success = True
283
+
284
+ if self.on_success:
285
+ self.on_success(result)
286
+
287
+ break
288
+
289
+ except Exception as e:
290
+ logger.error(f"Agent execution failed on attempt {attempt + 1}: {e}")
291
+
292
+ if attempt < max_retries - 1:
293
+ wait_time = 30 * (attempt + 1) # Exponential backoff
294
+ logger.info(f"Waiting {wait_time}s before retry...")
295
+ time.sleep(wait_time)
296
+
297
+ if not success:
298
+ self._failure_count += 1
299
+ logger.error(f"Agent execution failed after {max_retries} attempts")
300
+
301
+ if self.on_failure:
302
+ self.on_failure(f"Failed after {max_retries} attempts")
303
+
304
+ def execute_once(self) -> Any:
305
+ """
306
+ Execute agent immediately (one-time execution).
307
+
308
+ Returns:
309
+ Agent execution result
310
+ """
311
+ logger.info("Executing agent once")
312
+ try:
313
+ result = self._executor.execute(self.task)
314
+ logger.info(f"One-time execution successful: {result}")
315
+ return result
316
+ except Exception as e:
317
+ logger.error(f"One-time execution failed: {e}")
318
+ raise
319
+
320
+
321
+ def create_agent_scheduler(
322
+ agent,
323
+ task: str,
324
+ config: Optional[Dict[str, Any]] = None
325
+ ) -> AgentScheduler:
326
+ """
327
+ Factory function to create agent scheduler.
328
+
329
+ Args:
330
+ agent: PraisonAI Agent instance
331
+ task: Task description
332
+ config: Optional configuration
333
+
334
+ Returns:
335
+ Configured AgentScheduler instance
336
+ """
337
+ return AgentScheduler(agent, task, config)
@@ -0,0 +1,182 @@
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_schedule_command(args) -> int:
17
+ """
18
+ Handle the schedule command for running agents periodically.
19
+
20
+ Args:
21
+ args: Parsed command-line arguments
22
+
23
+ Returns:
24
+ Exit code (0 for success, 1 for error)
25
+ """
26
+ try:
27
+ from praisonai.scheduler import AgentScheduler
28
+ except ImportError:
29
+ print("Error: praisonai.scheduler module not found")
30
+ print("Please ensure PraisonAI is properly installed")
31
+ return 1
32
+
33
+ # Get YAML file path (default to agents.yaml)
34
+ yaml_path = getattr(args, 'schedule_yaml', None) or 'agents.yaml'
35
+
36
+ # Get overrides from CLI
37
+ interval_override = getattr(args, 'schedule_interval', None)
38
+ max_retries_override = getattr(args, 'schedule_max_retries', None)
39
+ verbose = getattr(args, 'verbose', False)
40
+
41
+ # Set up logging
42
+ if verbose:
43
+ logging.basicConfig(
44
+ level=logging.INFO,
45
+ format='[%(asctime)s] %(levelname)s - %(message)s',
46
+ datefmt='%Y-%m-%d %H:%M:%S'
47
+ )
48
+
49
+ try:
50
+ # Create scheduler from YAML
51
+ print(f"🤖 Loading agent configuration from: {yaml_path}")
52
+ scheduler = AgentScheduler.from_yaml(
53
+ yaml_path=yaml_path,
54
+ interval_override=interval_override,
55
+ max_retries_override=max_retries_override
56
+ )
57
+
58
+ # Display configuration
59
+ agent_name = getattr(scheduler.agent, 'name', 'Agent')
60
+ print(f"\n{'='*60}")
61
+ print(f"🤖 Starting 24/7 {agent_name}")
62
+ print(f"{'='*60}")
63
+ print(f"Task: {scheduler.task[:80]}{'...' if len(scheduler.task) > 80 else ''}")
64
+
65
+ # Get schedule info
66
+ schedule_config = scheduler._yaml_schedule_config
67
+ interval = interval_override or schedule_config.get('interval', 'hourly')
68
+ max_retries = max_retries_override or schedule_config.get('max_retries', 3)
69
+
70
+ print(f"Schedule: {interval}")
71
+ print(f"Max Retries: {max_retries}")
72
+ print(f"{'='*60}\n")
73
+
74
+ # Start scheduler
75
+ print("⏰ Starting scheduler... (Press Ctrl+C to stop)\n")
76
+ scheduler.start_from_yaml_config()
77
+
78
+ # Keep running until interrupted
79
+ try:
80
+ while scheduler.is_running:
81
+ import time
82
+ time.sleep(1)
83
+ except KeyboardInterrupt:
84
+ print("\n\n🛑 Stopping scheduler...")
85
+ scheduler.stop()
86
+
87
+ # Display final stats
88
+ stats = scheduler.get_stats()
89
+ print("\n📊 Final Statistics:")
90
+ print(f" Total Executions: {stats['total_executions']}")
91
+ print(f" Successful: {stats['successful_executions']}")
92
+ print(f" Failed: {stats['failed_executions']}")
93
+ print(f" Success Rate: {stats['success_rate']:.1f}%")
94
+ print("\n✅ Agent stopped successfully\n")
95
+
96
+ return 0
97
+
98
+ except FileNotFoundError as e:
99
+ print(f"❌ Error: {e}")
100
+ print(f"\nMake sure {yaml_path} exists in the current directory.")
101
+ print("\nExample agents.yaml structure:")
102
+ print("""
103
+ framework: praisonai
104
+
105
+ agents:
106
+ - name: "AI News Monitor"
107
+ role: "News Analyst"
108
+ instructions: "Search and summarize AI news"
109
+ tools:
110
+ - search_tool
111
+
112
+ task: "Search for latest AI news"
113
+
114
+ schedule:
115
+ interval: "hourly"
116
+ max_retries: 3
117
+ run_immediately: true
118
+ """)
119
+ return 1
120
+
121
+ except ValueError as e:
122
+ print(f"❌ Configuration Error: {e}")
123
+ return 1
124
+
125
+ except Exception as e:
126
+ print(f"❌ Error: {e}")
127
+ if verbose:
128
+ import traceback
129
+ traceback.print_exc()
130
+ return 1
131
+
132
+ @staticmethod
133
+ def add_schedule_arguments(subparsers):
134
+ """
135
+ Add schedule subcommand and arguments to parser.
136
+
137
+ Args:
138
+ subparsers: Subparsers object from argparse
139
+ """
140
+ schedule_parser = subparsers.add_parser(
141
+ 'schedule',
142
+ help='Schedule an agent to run continuously at regular intervals'
143
+ )
144
+
145
+ schedule_parser.add_argument(
146
+ 'schedule_yaml',
147
+ nargs='?',
148
+ default='agents.yaml',
149
+ help='Path to agents.yaml file (default: agents.yaml)'
150
+ )
151
+
152
+ schedule_parser.add_argument(
153
+ '--interval',
154
+ dest='schedule_interval',
155
+ type=str,
156
+ help='Override schedule interval (e.g., "hourly", "*/30m", "daily")'
157
+ )
158
+
159
+ schedule_parser.add_argument(
160
+ '--max-retries',
161
+ dest='schedule_max_retries',
162
+ type=int,
163
+ help='Override maximum retry attempts (default: from YAML or 3)'
164
+ )
165
+
166
+ schedule_parser.add_argument(
167
+ '--verbose', '-v',
168
+ action='store_true',
169
+ help='Enable verbose logging'
170
+ )
171
+
172
+ schedule_parser.set_defaults(func=AgentSchedulerHandler.handle_schedule_command)
173
+
174
+
175
+ def setup_scheduler_cli(subparsers):
176
+ """
177
+ Setup scheduler CLI commands.
178
+
179
+ Args:
180
+ subparsers: Subparsers object from argparse
181
+ """
182
+ AgentSchedulerHandler.add_schedule_arguments(subparsers)