legate 0.1.0

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 (317) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +345 -0
  4. data/bin/legate +13 -0
  5. data/examples/00_quickstart.rb +51 -0
  6. data/examples/01_simple_agent.rb +105 -0
  7. data/examples/02_multi_tool_agent.rb +140 -0
  8. data/examples/03_custom_tool.rb +93 -0
  9. data/examples/04_agent_instructions.rb +84 -0
  10. data/examples/05_state_and_sessions.rb +91 -0
  11. data/examples/06_callbacks.rb +186 -0
  12. data/examples/07_async_jobs.rb +112 -0
  13. data/examples/08_loop_agent.rb +197 -0
  14. data/examples/09_sequential_workflow.rb +40 -0
  15. data/examples/10_parallel_workflow.rb +34 -0
  16. data/examples/11_agent_delegation.rb +24 -0
  17. data/examples/12_http_client_tool.rb +156 -0
  18. data/examples/13_authentication.rb +220 -0
  19. data/examples/14_mcp_client.rb +154 -0
  20. data/examples/15_mcp_server.rb +79 -0
  21. data/examples/16_webhooks.rb +91 -0
  22. data/examples/README_sequential_agents.md +164 -0
  23. data/examples/advanced/auth/cookie_auth_tool.rb +146 -0
  24. data/examples/advanced/auth/custom_auth_flows_example.rb +626 -0
  25. data/examples/advanced/auth/excon_middleware.rb +317 -0
  26. data/examples/advanced/auth/excon_middleware_auth.rb +399 -0
  27. data/examples/advanced/auth/fiber_auth_example.rb +281 -0
  28. data/examples/advanced/auth/fiber_oidc_example.rb +403 -0
  29. data/examples/advanced/auth/httpbin_bearer_tool.rb +159 -0
  30. data/examples/advanced/auth/oauth2_auth.rb +419 -0
  31. data/examples/advanced/auth/oidc_auth.rb +514 -0
  32. data/examples/advanced/auth/openweather_api.rb +251 -0
  33. data/examples/advanced/auth/openweather_tool.rb +153 -0
  34. data/examples/advanced/auth/query_param_middleware_test.rb +138 -0
  35. data/examples/advanced/auth/service_account.rb +135 -0
  36. data/examples/advanced/auth/test_with_httpbin.rb +202 -0
  37. data/examples/advanced/auth/token_lifecycle_example.rb +428 -0
  38. data/examples/advanced/callback_monitoring.rb +679 -0
  39. data/examples/advanced/mas/fixed_delegation_example.rb +191 -0
  40. data/examples/advanced/mas/loop_workflow.rb +28 -0
  41. data/examples/advanced/mas/mock_planner.rb +77 -0
  42. data/examples/advanced/mas/proper_delegation_example.rb +276 -0
  43. data/examples/advanced/mcp/legate_mcp_server_resource_example.rb +182 -0
  44. data/examples/advanced/mcp/mcp_resource_server_example.rb +309 -0
  45. data/examples/advanced/mcp/mcp_server_async.rb +76 -0
  46. data/examples/advanced/mcp/mcp_server_async_tools.rb +122 -0
  47. data/examples/advanced/mcp/mcp_server_legate_agent.rb +95 -0
  48. data/examples/advanced/mcp/mcp_server_rack.rb +89 -0
  49. data/examples/advanced/random_calculator.rb +104 -0
  50. data/examples/advanced/sleep_agent.rb +153 -0
  51. data/examples/advanced/webhooks/webhook_e2e_runner.rb +110 -0
  52. data/examples/advanced/webhooks/webhook_receiver_agent.rb +58 -0
  53. data/examples/advanced/workflows/task_refinement_loop_agent.rb +278 -0
  54. data/examples/advanced/workflows/travel_planner_auto_sequential.rb +444 -0
  55. data/examples/advanced/workflows/travel_planner_parallel.rb +656 -0
  56. data/examples/advanced/workflows/travel_planner_sequential.rb +512 -0
  57. data/examples/tools/oauth2_example.rb +136 -0
  58. data/examples/tools/sleepy_tool.rb +42 -0
  59. data/lib/legate/activity_log.rb +71 -0
  60. data/lib/legate/agent.rb +959 -0
  61. data/lib/legate/agent_code_generator.rb +185 -0
  62. data/lib/legate/agent_definition.rb +812 -0
  63. data/lib/legate/agentic/decision.rb +49 -0
  64. data/lib/legate/agentic/loop.rb +134 -0
  65. data/lib/legate/agentic.rb +5 -0
  66. data/lib/legate/agents/loop_agent.rb +248 -0
  67. data/lib/legate/agents/parallel_agent.rb +163 -0
  68. data/lib/legate/agents/sequential_agent.rb +190 -0
  69. data/lib/legate/agents.rb +14 -0
  70. data/lib/legate/auth/config.rb +148 -0
  71. data/lib/legate/auth/coordinator.rb +218 -0
  72. data/lib/legate/auth/coordinators/oauth2_coordinator.rb +99 -0
  73. data/lib/legate/auth/coordinators/oidc_coordinator.rb +68 -0
  74. data/lib/legate/auth/coordinators/service_account_coordinator.rb +122 -0
  75. data/lib/legate/auth/credential.rb +157 -0
  76. data/lib/legate/auth/encryption.rb +108 -0
  77. data/lib/legate/auth/error.rb +94 -0
  78. data/lib/legate/auth/exchanged_credential.rb +180 -0
  79. data/lib/legate/auth/excon_middleware.rb +285 -0
  80. data/lib/legate/auth/http_client_utils.rb +364 -0
  81. data/lib/legate/auth/manager.rb +531 -0
  82. data/lib/legate/auth/manager_store.rb +394 -0
  83. data/lib/legate/auth/middleware_factory.rb +290 -0
  84. data/lib/legate/auth/runner.rb +279 -0
  85. data/lib/legate/auth/scheme.rb +125 -0
  86. data/lib/legate/auth/schemes/api_key.rb +212 -0
  87. data/lib/legate/auth/schemes/google_service_account.rb +108 -0
  88. data/lib/legate/auth/schemes/http_bearer.rb +98 -0
  89. data/lib/legate/auth/schemes/oauth2.rb +396 -0
  90. data/lib/legate/auth/schemes/openid_connect.rb +346 -0
  91. data/lib/legate/auth/schemes/service_account.rb +388 -0
  92. data/lib/legate/auth/schemes.rb +40 -0
  93. data/lib/legate/auth/token_manager.rb +362 -0
  94. data/lib/legate/auth/token_store.rb +86 -0
  95. data/lib/legate/auth/tool_context_extension.rb +97 -0
  96. data/lib/legate/auth/tool_integration.rb +188 -0
  97. data/lib/legate/auth/url_guard.rb +81 -0
  98. data/lib/legate/auth.rb +453 -0
  99. data/lib/legate/callbacks/callback_context.rb +71 -0
  100. data/lib/legate/cli/agent_commands.rb +950 -0
  101. data/lib/legate/cli/auth_commands.rb +520 -0
  102. data/lib/legate/cli/base_command.rb +24 -0
  103. data/lib/legate/cli/deployment_commands.rb +934 -0
  104. data/lib/legate/cli/output_helper.rb +108 -0
  105. data/lib/legate/cli/session_commands.rb +138 -0
  106. data/lib/legate/cli/skaffold_commands.rb +223 -0
  107. data/lib/legate/cli/tool_commands.rb +261 -0
  108. data/lib/legate/cli/web_commands.rb +182 -0
  109. data/lib/legate/cli.rb +40 -0
  110. data/lib/legate/configuration/webhooks.rb +113 -0
  111. data/lib/legate/configuration.rb +39 -0
  112. data/lib/legate/definition_store.rb +23 -0
  113. data/lib/legate/errors.rb +118 -0
  114. data/lib/legate/event.rb +161 -0
  115. data/lib/legate/gemini_ai_beta_patch.rb +39 -0
  116. data/lib/legate/generators/agent_generator.rb +412 -0
  117. data/lib/legate/generators/code_validator.rb +48 -0
  118. data/lib/legate/generators/legate/install_generator.rb +35 -0
  119. data/lib/legate/generators/legate/templates/create_legate_tables.rb.tt +36 -0
  120. data/lib/legate/generators/legate/templates/initializer.rb +18 -0
  121. data/lib/legate/generators/runtime_tool_loader.rb +76 -0
  122. data/lib/legate/generators/tool_generator.rb +408 -0
  123. data/lib/legate/generators.rb +11 -0
  124. data/lib/legate/global_definition_registry.rb +506 -0
  125. data/lib/legate/global_tool_manager.rb +135 -0
  126. data/lib/legate/llm/adapter.rb +69 -0
  127. data/lib/legate/llm/gemini.rb +172 -0
  128. data/lib/legate/llm/ollama.rb +80 -0
  129. data/lib/legate/llm.rb +34 -0
  130. data/lib/legate/mcp/client.rb +320 -0
  131. data/lib/legate/mcp/connection/sse.rb +292 -0
  132. data/lib/legate/mcp/connection/stdio.rb +273 -0
  133. data/lib/legate/mcp/connection_manager.rb +103 -0
  134. data/lib/legate/mcp/server/legate_agent_adapter.rb +170 -0
  135. data/lib/legate/mcp/server/legate_direct_agent_adapter.rb +140 -0
  136. data/lib/legate/mcp/server/legate_tool_adapter.rb +119 -0
  137. data/lib/legate/mcp/tool_wrapper.rb +138 -0
  138. data/lib/legate/mcp/util/schema_converter.rb +134 -0
  139. data/lib/legate/mcp.rb +23 -0
  140. data/lib/legate/plan_executor.rb +375 -0
  141. data/lib/legate/planner.rb +839 -0
  142. data/lib/legate/rails/railtie.rb +43 -0
  143. data/lib/legate/rails.rb +9 -0
  144. data/lib/legate/redaction.rb +32 -0
  145. data/lib/legate/session.rb +299 -0
  146. data/lib/legate/session_service/active_record.rb +300 -0
  147. data/lib/legate/session_service/base.rb +68 -0
  148. data/lib/legate/session_service/event_broadcast.rb +74 -0
  149. data/lib/legate/session_service/in_memory.rb +188 -0
  150. data/lib/legate/tool/metadata_dsl.rb +122 -0
  151. data/lib/legate/tool.rb +276 -0
  152. data/lib/legate/tool_code_generator.rb +103 -0
  153. data/lib/legate/tool_context.rb +350 -0
  154. data/lib/legate/tool_loader.rb +39 -0
  155. data/lib/legate/tool_registry.rb +73 -0
  156. data/lib/legate/tool_result.rb +61 -0
  157. data/lib/legate/tools/agent_tool.rb +187 -0
  158. data/lib/legate/tools/base/http_client.rb +319 -0
  159. data/lib/legate/tools/base/safe_url.rb +56 -0
  160. data/lib/legate/tools/base_async_job_tool.rb +91 -0
  161. data/lib/legate/tools/calculator.rb +89 -0
  162. data/lib/legate/tools/cat_facts.rb +81 -0
  163. data/lib/legate/tools/check_job_status_tool.rb +48 -0
  164. data/lib/legate/tools/current_time_tool.rb +64 -0
  165. data/lib/legate/tools/echo.rb +43 -0
  166. data/lib/legate/tools/http_request_tool.rb +105 -0
  167. data/lib/legate/tools/random_number_tool.rb +64 -0
  168. data/lib/legate/tools/read_webpage_tool.rb +92 -0
  169. data/lib/legate/tools/sleepy_tool.rb +74 -0
  170. data/lib/legate/tools/webhook_tool.rb +146 -0
  171. data/lib/legate/version.rb +5 -0
  172. data/lib/legate/web/app.rb +984 -0
  173. data/lib/legate/web/public/css/main.css +4980 -0
  174. data/lib/legate/web/public/images/favicon-256.png +0 -0
  175. data/lib/legate/web/public/images/favicon-32.png +0 -0
  176. data/lib/legate/web/public/images/legate-logo-dark.png +0 -0
  177. data/lib/legate/web/public/images/legate-logo-light.png +0 -0
  178. data/lib/legate/web/public/js/legate.js +616 -0
  179. data/lib/legate/web/public/styles/main.scss +4402 -0
  180. data/lib/legate/web/routes/agent_authentication_routes.rb +530 -0
  181. data/lib/legate/web/routes/agent_definition_routes.rb +803 -0
  182. data/lib/legate/web/routes/agent_generator_routes.rb +80 -0
  183. data/lib/legate/web/routes/agent_interaction_routes.rb +734 -0
  184. data/lib/legate/web/routes/agent_runtime_routes.rb +323 -0
  185. data/lib/legate/web/routes/api_routes.rb +56 -0
  186. data/lib/legate/web/routes/authentication_routes.rb +1541 -0
  187. data/lib/legate/web/routes/core_routes.rb +111 -0
  188. data/lib/legate/web/routes/documentation_routes.rb +220 -0
  189. data/lib/legate/web/routes/tool_generator_routes.rb +81 -0
  190. data/lib/legate/web/routes/tools_ui_routes.rb +207 -0
  191. data/lib/legate/web/sass_compiler.rb +73 -0
  192. data/lib/legate/web/views/_active_session_info.slim +25 -0
  193. data/lib/legate/web/views/_activity_list.slim +55 -0
  194. data/lib/legate/web/views/_agent_card.slim +56 -0
  195. data/lib/legate/web/views/_agent_generator_modal.slim +382 -0
  196. data/lib/legate/web/views/_agent_status_controls.slim +71 -0
  197. data/lib/legate/web/views/_agent_tool_table.slim +74 -0
  198. data/lib/legate/web/views/_chat_message.slim +95 -0
  199. data/lib/legate/web/views/_display_agent_configuration.slim +26 -0
  200. data/lib/legate/web/views/_display_agent_description.slim +11 -0
  201. data/lib/legate/web/views/_display_agent_fallback.slim +15 -0
  202. data/lib/legate/web/views/_display_agent_hierarchy.slim +93 -0
  203. data/lib/legate/web/views/_display_agent_instruction.slim +17 -0
  204. data/lib/legate/web/views/_display_agent_mcp.slim +13 -0
  205. data/lib/legate/web/views/_display_agent_model.slim +17 -0
  206. data/lib/legate/web/views/_display_agent_name.slim +42 -0
  207. data/lib/legate/web/views/_display_agent_output_key.slim +26 -0
  208. data/lib/legate/web/views/_display_agent_type.slim +65 -0
  209. data/lib/legate/web/views/_edit_agent_configuration.slim +74 -0
  210. data/lib/legate/web/views/_edit_agent_description.slim +16 -0
  211. data/lib/legate/web/views/_edit_agent_fallback.slim +25 -0
  212. data/lib/legate/web/views/_edit_agent_hierarchy.slim +98 -0
  213. data/lib/legate/web/views/_edit_agent_instruction.slim +49 -0
  214. data/lib/legate/web/views/_edit_agent_mcp.slim +33 -0
  215. data/lib/legate/web/views/_edit_agent_model.slim +23 -0
  216. data/lib/legate/web/views/_edit_agent_output_key.slim +36 -0
  217. data/lib/legate/web/views/_edit_agent_tools.slim +40 -0
  218. data/lib/legate/web/views/_edit_agent_type.slim +67 -0
  219. data/lib/legate/web/views/_session_error.slim +4 -0
  220. data/lib/legate/web/views/_skeleton.slim +69 -0
  221. data/lib/legate/web/views/_tool_card.slim +9 -0
  222. data/lib/legate/web/views/_tool_generator_modal.slim +311 -0
  223. data/lib/legate/web/views/agent.slim +436 -0
  224. data/lib/legate/web/views/agent_auth.slim +562 -0
  225. data/lib/legate/web/views/agents.slim +369 -0
  226. data/lib/legate/web/views/auth.slim +112 -0
  227. data/lib/legate/web/views/auth_credential_detail.slim +327 -0
  228. data/lib/legate/web/views/auth_credentials.slim +261 -0
  229. data/lib/legate/web/views/auth_debug.slim +94 -0
  230. data/lib/legate/web/views/auth_mapping_detail.slim +151 -0
  231. data/lib/legate/web/views/auth_mapping_new.slim +123 -0
  232. data/lib/legate/web/views/auth_mappings.slim +120 -0
  233. data/lib/legate/web/views/auth_scheme_detail.slim +274 -0
  234. data/lib/legate/web/views/auth_schemes.slim +259 -0
  235. data/lib/legate/web/views/auth_test.slim +418 -0
  236. data/lib/legate/web/views/chat.slim +192 -0
  237. data/lib/legate/web/views/docs_index.slim +105 -0
  238. data/lib/legate/web/views/docs_show.slim +105 -0
  239. data/lib/legate/web/views/error_404.slim +5 -0
  240. data/lib/legate/web/views/index.slim +148 -0
  241. data/lib/legate/web/views/layout.slim +144 -0
  242. data/lib/legate/web/views/tool_detail.slim +87 -0
  243. data/lib/legate/web/views/tools.slim +50 -0
  244. data/lib/legate/web/webhook_listener.rb +367 -0
  245. data/lib/legate/web.rb +9 -0
  246. data/lib/legate.rb +220 -0
  247. data/public/docs/advanced/callbacks.md +828 -0
  248. data/public/docs/advanced/mcp_schema_conversion.md +59 -0
  249. data/public/docs/authentication/api_reference/config.md +210 -0
  250. data/public/docs/authentication/api_reference/credential.md +246 -0
  251. data/public/docs/authentication/api_reference/encryption.md +218 -0
  252. data/public/docs/authentication/api_reference/exchanged_credential.md +271 -0
  253. data/public/docs/authentication/api_reference/excon_middleware.md +175 -0
  254. data/public/docs/authentication/api_reference/index.md +30 -0
  255. data/public/docs/authentication/api_reference/scheme.md +250 -0
  256. data/public/docs/authentication/api_reference/schemes/api_key.md +175 -0
  257. data/public/docs/authentication/api_reference/schemes/google_service_account.md +221 -0
  258. data/public/docs/authentication/api_reference/schemes/http_bearer.md +169 -0
  259. data/public/docs/authentication/api_reference/schemes/oauth2.md +343 -0
  260. data/public/docs/authentication/api_reference/schemes/oidc.md +73 -0
  261. data/public/docs/authentication/api_reference/schemes/openid_connect.md +311 -0
  262. data/public/docs/authentication/api_reference/schemes/service_account.md +287 -0
  263. data/public/docs/authentication/api_reference/token_manager.md +221 -0
  264. data/public/docs/authentication/api_reference/token_store.md +146 -0
  265. data/public/docs/authentication/api_reference/tool_context_extension.md +166 -0
  266. data/public/docs/authentication/guides/api_key.md +190 -0
  267. data/public/docs/authentication/guides/bearer.md +172 -0
  268. data/public/docs/authentication/guides/configuration.md +255 -0
  269. data/public/docs/authentication/guides/custom_flow.md +523 -0
  270. data/public/docs/authentication/guides/index.md +24 -0
  271. data/public/docs/authentication/guides/migration.md +435 -0
  272. data/public/docs/authentication/guides/oauth2.md +252 -0
  273. data/public/docs/authentication/guides/oidc.md +241 -0
  274. data/public/docs/authentication/guides/overview.md +155 -0
  275. data/public/docs/authentication/guides/secure_storage.md +301 -0
  276. data/public/docs/authentication/guides/service_account.md +228 -0
  277. data/public/docs/authentication/guides/token_lifecycle.md +295 -0
  278. data/public/docs/authentication/guides/web_ui_integration.md +504 -0
  279. data/public/docs/authentication/index.md +58 -0
  280. data/public/docs/authentication/troubleshooting/credential_storage.md +550 -0
  281. data/public/docs/authentication/troubleshooting/environment_variables.md +540 -0
  282. data/public/docs/authentication/troubleshooting/index.md +11 -0
  283. data/public/docs/authentication/troubleshooting/oauth2_issues.md +220 -0
  284. data/public/docs/authentication/troubleshooting/oidc_issues.md +412 -0
  285. data/public/docs/authentication/troubleshooting/token_refresh.md +338 -0
  286. data/public/docs/cli/legate_cli_usage.md +363 -0
  287. data/public/docs/core_concepts/legate_agent_lifecycle.md +124 -0
  288. data/public/docs/core_concepts/legate_architecture_overview.md +110 -0
  289. data/public/docs/core_concepts/legate_configuration.md +116 -0
  290. data/public/docs/core_concepts/legate_definition_store.md +102 -0
  291. data/public/docs/core_concepts/legate_planner.md +94 -0
  292. data/public/docs/core_concepts/legate_session_service.md +104 -0
  293. data/public/docs/error_handling/legate_error_handling.md +122 -0
  294. data/public/docs/examples.md +199 -0
  295. data/public/docs/getting_started.md +111 -0
  296. data/public/docs/guides/agentic_agents.md +137 -0
  297. data/public/docs/guides/ai_code_generators.md +437 -0
  298. data/public/docs/guides/auto_loading.md +326 -0
  299. data/public/docs/guides/configuring_agent_webhooks.md +219 -0
  300. data/public/docs/guides/http_client_usage.md +264 -0
  301. data/public/docs/guides/llm_providers.md +137 -0
  302. data/public/docs/guides/mcp_client_integration.md +232 -0
  303. data/public/docs/guides/mcp_server_exposure.md +206 -0
  304. data/public/docs/guides/rails_integration.md +128 -0
  305. data/public/docs/guides/sending_outbound_webhooks.md +227 -0
  306. data/public/docs/guides/streaming.md +112 -0
  307. data/public/docs/guides/webhooks.md +288 -0
  308. data/public/docs/introduction.md +51 -0
  309. data/public/docs/multi_agent_systems/advanced_features.md +57 -0
  310. data/public/docs/multi_agent_systems/agent_delegation.md +190 -0
  311. data/public/docs/multi_agent_systems/agent_hierarchy.md +49 -0
  312. data/public/docs/multi_agent_systems/state_management.md +47 -0
  313. data/public/docs/multi_agent_systems/workflow_agents.md +72 -0
  314. data/public/docs/tools/legate_built_in_tools.md +332 -0
  315. data/public/docs/tools/legate_tools_and_registry.md +263 -0
  316. data/public/docs/web_ui/legate_web_ui.md +137 -0
  317. metadata +823 -0
@@ -0,0 +1,112 @@
1
+ # File: examples/07_async_jobs.rb
2
+ # frozen_string_literal: true
3
+
4
+ puts 'To see this work end-to-end:'
5
+ puts '1. Run this script: bundle exec ruby examples/07_async_jobs.rb'
6
+
7
+ # This example demonstrates how to use an Legate tool that starts
8
+ # an asynchronous background job and how to check its status.
9
+
10
+ # --- Setup ---
11
+ # Load Legate and necessary components
12
+ require_relative '../lib/legate'
13
+
14
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
15
+ # The library never reads .env on its own; an application must opt in.
16
+ Legate.load_environment
17
+
18
+ # Load the custom tool class. Its definition (name, params) will be used by the GlobalToolManager.
19
+ require_relative 'tools/sleepy_tool' # Provides :start_sleepy_job
20
+ # Legate::Tools::CheckJobStatusTool (providing :check_job_status) is loaded by Legate core.
21
+
22
+ puts '--- Async Job Example ---'
23
+
24
+ # --- Agent Definition ---
25
+ puts "\nSetting up agent definition..."
26
+ async_job_runner_definition = Legate::AgentDefinition.new.define do |a|
27
+ a.name :async_job_runner
28
+ a.description 'An agent that can start and check background jobs.'
29
+ a.instruction 'You manage asynchronous jobs. Use start_sleepy_job to initiate them and check_job_status to monitor.'
30
+ a.use_tool :start_sleepy_job # Provided by SleepyTool
31
+ a.use_tool :check_job_status # Provided by CheckJobStatusTool
32
+ end
33
+
34
+ # --- Agent Instantiation ---
35
+ agent = Legate::Agent.new(definition: async_job_runner_definition)
36
+
37
+ # The check_job_status tool is now added via tool_classes
38
+ puts "Agent Tools: #{agent.tools.map(&:name)}"
39
+
40
+ # --- Session Setup ---
41
+ # Use in-memory session for this example
42
+ session_service = Legate::SessionService::InMemory.new
43
+ session = session_service.create_session(app_name: agent.name, user_id: 'async_example_user')
44
+ puts "Created session: #{session.id}"
45
+
46
+ # --- Task Execution ---
47
+
48
+ # Start the agent runtime
49
+ agent.start
50
+
51
+ # 1. Start the sleepy job
52
+ task_input_start = "Start a sleepy job that waits 5 seconds with message 'Hello Async!'"
53
+ puts "\nRunning task: '#{task_input_start}'"
54
+
55
+ # Simulate planner choosing the sleepy_tool
56
+ # In a real scenario, the planner would generate this plan:
57
+ plan_start = [
58
+ { tool: :start_sleepy_job, params: { duration: 5, message: 'Hello Async!' } }
59
+ ]
60
+
61
+ # Execute the plan step manually for demonstration
62
+ # (Alternatively, use agent.run_task and provide prompt engineering for the LLM to generate the plan)
63
+ puts "Executing plan step: #{plan_start.first.inspect}"
64
+
65
+ # Need the session object for execute_step
66
+ current_session = session_service.get_session(session_id: session.id)
67
+
68
+ start_result_hash = agent.send(:execute_step, plan_start.first, current_session, session_service)
69
+
70
+ puts "\nResult from starting the job:"
71
+ puts JSON.pretty_generate(start_result_hash)
72
+
73
+ unless start_result_hash[:status] == :pending && start_result_hash[:job_id]
74
+ puts "\nError: Expected pending status with job_id! Aborting."
75
+ agent.stop
76
+ exit 1
77
+ end
78
+
79
+ job_id = start_result_hash[:job_id]
80
+ puts "\nJob enqueued with ID: #{job_id}"
81
+ puts '(The job runs in a background thread and will complete after the specified duration)'
82
+
83
+ # 2. Check the job status (immediately - likely still pending)
84
+ task_input_check = "Check status for job #{job_id}"
85
+ puts "\nRunning task: '#{task_input_check}'"
86
+
87
+ plan_check = [
88
+ { tool: :check_job_status, params: { job_id: job_id } }
89
+ ]
90
+
91
+ puts "Executing plan step: #{plan_check.first.inspect}"
92
+ check_result_hash_1 = agent.send(:execute_step, plan_check.first, current_session, session_service)
93
+
94
+ puts "\nResult from first status check:"
95
+ puts JSON.pretty_generate(check_result_hash_1)
96
+
97
+ # 3. Wait and check again
98
+ wait_time = 7 # Wait longer than the job's sleep duration
99
+ puts "\nWaiting #{wait_time} seconds for the job to likely complete..."
100
+ sleep wait_time
101
+
102
+ puts "\nRunning task: '#{task_input_check}' (again)"
103
+ puts "Executing plan step: #{plan_check.first.inspect}"
104
+ check_result_hash_2 = agent.send(:execute_step, plan_check.first, current_session, session_service)
105
+
106
+ puts "\nResult from second status check:"
107
+ puts JSON.pretty_generate(check_result_hash_2)
108
+
109
+ # Stop the agent runtime
110
+ agent.stop
111
+
112
+ puts "\n--- Example Finished ---"
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example demonstrating the Legate::Agents::LoopAgent which executes sub-agents in a loop
4
+ # until a termination condition is met or max iterations are reached.
5
+
6
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
7
+ require 'legate'
8
+
9
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
10
+ # The library never reads .env on its own; an application must opt in.
11
+ Legate.load_environment
12
+ require 'legate/agents/loop_agent'
13
+
14
+ # First, define sub-agents that will be used in the loop
15
+
16
+ # 1. Counter agent - increments a counter in session state
17
+ counter_agent = Legate::AgentDefinition.new.define do |a|
18
+ a.name :counter_agent
19
+ a.description 'Increments a counter in session state'
20
+ a.instruction 'You increment a counter and report the new count.'
21
+ a.use_tool :echo
22
+ a.output_key :counter_result
23
+ end
24
+
25
+ # Register the agent definition so it can be found by name
26
+ Legate::GlobalDefinitionRegistry.register(counter_agent)
27
+
28
+ # 2. Check condition agent - examines count and decides if we need to continue
29
+ condition_agent = Legate::AgentDefinition.new.define do |a|
30
+ a.name :condition_agent
31
+ a.description 'Checks if loop should continue based on count'
32
+ a.instruction 'Check the counter and set done to true if target is reached.'
33
+ a.use_tool :echo
34
+ a.output_key :done
35
+ end
36
+
37
+ # Register the agent definition
38
+ Legate::GlobalDefinitionRegistry.register(condition_agent)
39
+
40
+ # Now define the loop agent that uses the sub-agents
41
+ loop_agent = Legate::AgentDefinition.new.define do |a|
42
+ a.name :loop_demo_agent
43
+ a.description 'Demonstrates loop agent functionality'
44
+ a.instruction 'You run a loop that counts until a condition is met.'
45
+ a.agent_type :loop # This is important - specifies this is a loop agent
46
+
47
+ # Sub-agents to execute in each loop iteration (in sequence)
48
+ a.loop_sub_agents %i[counter_agent condition_agent]
49
+
50
+ # Maximum number of iterations (safety valve)
51
+ a.loop_max_iterations 5
52
+
53
+ # Loop termination condition
54
+ a.loop_condition(:done, true)
55
+
56
+ # Store final result
57
+ a.output_key :loop_result
58
+ end
59
+
60
+ # Register the loop agent definition
61
+ Legate::GlobalDefinitionRegistry.register(loop_agent)
62
+
63
+ puts 'Agent definitions registered. Creating session and agent instances...'
64
+
65
+ # Create a session service for state management
66
+ session_service = Legate::SessionService::InMemory.new
67
+ user_id = 'demo-user'
68
+ app_name = 'loop-example'
69
+
70
+ # Create a session
71
+ session = session_service.create_session(
72
+ app_name: app_name,
73
+ user_id: user_id,
74
+ initial_state: { count: 0 }
75
+ )
76
+ session_id = session.id
77
+
78
+ puts "Created session with ID: #{session_id}"
79
+
80
+ # Create agent instances
81
+
82
+ # 1. Counter Agent implementation
83
+ counter_agent_instance = Legate::Agent.new(definition: counter_agent)
84
+
85
+ # Override execute_plan to implement the counting logic
86
+ def counter_agent_instance.execute_plan(_plan, session, session_service, _invocation_id = nil)
87
+ # Get the session ID from the session object
88
+ session_id = session.id
89
+
90
+ # Get current count from session state
91
+ current_count = session_service.get_state(session_id: session_id, key: :count) || 0
92
+
93
+ # Increment count
94
+ new_count = current_count + 1
95
+
96
+ # Update session state with new count
97
+ session_service.set_state(session_id: session_id, key: :count, value: new_count)
98
+
99
+ # Create success result with count info
100
+ result_hash = {
101
+ status: :success,
102
+ result: "Counter incremented to #{new_count}"
103
+ }
104
+
105
+ # Return the details and the result in the format expected by the parent method
106
+ { details: [result_hash], last_result: result_hash }
107
+ end
108
+
109
+ # 2. Condition Agent implementation
110
+ condition_agent_instance = Legate::Agent.new(definition: condition_agent)
111
+
112
+ # Override execute_plan to implement the condition check
113
+ def condition_agent_instance.execute_plan(_plan, session, session_service, _invocation_id = nil)
114
+ # Get the session ID from the session object
115
+ session_id = session.id
116
+
117
+ # Get current count
118
+ current_count = session_service.get_state(session_id: session_id, key: :count) || 0
119
+
120
+ # Check if we've reached our target count (3 for this example)
121
+ target = 3
122
+ done = current_count >= target
123
+
124
+ # Set the done flag in session state to control loop termination
125
+ session_service.set_state(session_id: session_id, key: :done, value: done)
126
+
127
+ # Create success result
128
+ result_hash = {
129
+ status: :success,
130
+ result: if done
131
+ "Target count reached (#{current_count} >= #{target}). Loop should terminate."
132
+ else
133
+ "Target count not yet reached (#{current_count} < #{target}). Loop should continue."
134
+ end
135
+ }
136
+
137
+ # Return the details and the result in the format expected by the parent method
138
+ { details: [result_hash], last_result: result_hash }
139
+ end
140
+
141
+ # Create the loop agent that will coordinate the whole process
142
+ loop_agent_instance = Legate::Agents::LoopAgent.new(
143
+ definition: loop_agent,
144
+ sub_agents: [counter_agent_instance, condition_agent_instance]
145
+ )
146
+
147
+ # Start all agents
148
+ puts 'Starting agents...'
149
+ counter_agent_instance.start
150
+ condition_agent_instance.start
151
+ loop_agent_instance.start
152
+
153
+ puts 'Starting loop agent execution...'
154
+ puts 'Loop will continue until count reaches 3 or 5 max iterations...'
155
+ puts '------------------------------'
156
+
157
+ # Execute the loop agent
158
+ result = loop_agent_instance.run_task(
159
+ session_id: session_id,
160
+ user_input: 'Start counting loop',
161
+ session_service: session_service
162
+ )
163
+
164
+ puts 'Loop agent execution complete!'
165
+ puts '------------------------------'
166
+ puts "Final result: #{result.content[:status]}"
167
+ puts "Iterations completed: #{result.content[:iterations_completed] || 'N/A'}"
168
+ puts "Loop condition met? #{result.content[:loop_condition_met] || 'N/A'}"
169
+ puts
170
+
171
+ if result.content[:status] == :error
172
+ puts "Error: #{result.content[:error_message]}"
173
+ else
174
+ puts 'Iteration details:'
175
+
176
+ # Print details from each iteration if available
177
+ if result.content[:iterations]
178
+ result.content[:iterations].each_with_index do |iteration, i|
179
+ puts "Iteration #{i + 1}:"
180
+
181
+ # Print each sub-agent's results in this iteration
182
+ iteration[:results].each do |sub_result|
183
+ puts " - #{sub_result[:agent]}: #{sub_result[:result][:result]}"
184
+ end
185
+ puts
186
+ end
187
+ else
188
+ puts 'No iteration details available.'
189
+ end
190
+
191
+ # Check final state values
192
+ final_count = session_service.get_state(session_id: session_id, key: :count)
193
+ puts "Final count in session state: #{final_count}"
194
+ puts "Done flag in session state: #{session_service.get_state(session_id: session_id, key: :done)}"
195
+ end
196
+
197
+ puts '------------------------------'
@@ -0,0 +1,40 @@
1
+ # examples/09_sequential_workflow.rb
2
+ require_relative '../lib/legate'
3
+
4
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
5
+ # The library never reads .env on its own; an application must opt in.
6
+ Legate.load_environment
7
+
8
+ # 1. Define Worker Agents
9
+ Legate::Agent.define do |agent|
10
+ agent.name :data_gatherer
11
+ agent.instruction 'Gather data about the topic.'
12
+ agent.use_tool :echo # Simulation
13
+ agent.output_key :gathered_data
14
+ end
15
+
16
+ Legate::Agent.define do |agent|
17
+ agent.name :data_processor
18
+ agent.instruction 'Process the gathered data.'
19
+ agent.use_tool :echo # Simulation
20
+ agent.output_key :processed_data
21
+ end
22
+
23
+ Legate::Agent.define do |agent|
24
+ agent.name :report_writer
25
+ agent.instruction 'Write a report based on processed data.'
26
+ agent.use_tool :echo # Simulation
27
+ end
28
+
29
+ # 2. Define Sequential Workflow
30
+ Legate::Agent.define do |agent|
31
+ agent.name :report_pipeline
32
+ agent.agent_type :sequential
33
+ agent.description 'A pipeline to gather, process, and write reports.'
34
+ agent.instruction 'Execute the pipeline.'
35
+
36
+ # Order matters!
37
+ agent.sequential_sub_agents :data_gatherer, :data_processor, :report_writer
38
+ end
39
+
40
+ puts 'Defined Sequential Workflow: :report_pipeline'
@@ -0,0 +1,34 @@
1
+ # examples/10_parallel_workflow.rb
2
+ require_relative '../lib/legate'
3
+
4
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
5
+ # The library never reads .env on its own; an application must opt in.
6
+ Legate.load_environment
7
+
8
+ # 1. Define Specialized Analysts
9
+ Legate::Agent.define do |agent|
10
+ agent.name :market_analyst
11
+ agent.instruction 'Analyze market trends.'
12
+ agent.use_tool :echo
13
+ agent.output_key :market_analysis
14
+ end
15
+
16
+ Legate::Agent.define do |agent|
17
+ agent.name :tech_analyst
18
+ agent.instruction 'Analyze technology trends.'
19
+ agent.use_tool :echo
20
+ agent.output_key :tech_analysis
21
+ end
22
+
23
+ # 2. Define Parallel Workflow
24
+ Legate::Agent.define do |agent|
25
+ agent.name :comprehensive_analysis
26
+ agent.agent_type :parallel
27
+ agent.description 'Run analyses concurrently.'
28
+ agent.instruction 'Analyze the sector.'
29
+
30
+ # Will run at the same time
31
+ agent.parallel_sub_agents :market_analyst, :tech_analyst
32
+ end
33
+
34
+ puts 'Defined Parallel Workflow: :comprehensive_analysis'
@@ -0,0 +1,24 @@
1
+ # examples/11_agent_delegation.rb
2
+ require_relative '../lib/legate'
3
+
4
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
5
+ # The library never reads .env on its own; an application must opt in.
6
+ Legate.load_environment
7
+
8
+ # 1. Define a Specialist
9
+ Legate::Agent.define do |agent|
10
+ agent.name :math_expert
11
+ agent.instruction 'You are a math expert. Solve math problems.'
12
+ agent.use_tool :calculator
13
+ end
14
+
15
+ # 2. Define a Manager who delegates
16
+ Legate::Agent.define do |agent|
17
+ agent.name :project_manager
18
+ agent.instruction 'You manage the project. If you see a math problem, delegate it to the math expert.'
19
+
20
+ # Allow delegation
21
+ agent.can_delegate_to :math_expert
22
+ end
23
+
24
+ puts 'Defined Delegation System: :project_manager can delegate to :math_expert'
@@ -0,0 +1,156 @@
1
+ # File: examples/12_http_client_tool.rb
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'legate' # Load Legate framework
6
+
7
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
8
+ # The library never reads .env on its own; an application must opt in.
9
+ Legate.load_environment
10
+ require 'legate/tools/base/http_client' # Load the HttpClient module
11
+
12
+ # --- Define the Custom Tool ---
13
+ class JsonPlaceholderTool < Legate::Tool
14
+ include Legate::Tools::Base::HttpClient # Include the mixin
15
+
16
+ # --- Tool Metadata ---
17
+ tool_name # Infer name from class: json_placeholder_tool
18
+ tool_description 'Fetches or creates posts on JSONPlaceholder API.'
19
+
20
+ parameter :action, type: :string, required: true, description: 'Action to perform: "get" or "create".'
21
+ parameter :post_id, type: :integer, required: false, description: 'The ID of the post to fetch (required for "get").'
22
+ parameter :post_data, type: :hash, required: false,
23
+ description: 'Data for the new post (required for "create"). Example: { title: "foo", body: "bar", userId: 1 }'
24
+ # --- End Metadata ---
25
+
26
+ API_BASE_URL = 'https://jsonplaceholder.typicode.com/'
27
+
28
+ def initialize(**options)
29
+ super(**options)
30
+ # Setup the client for JSONPlaceholder
31
+ # Use default options (timeouts, persistent connection, etc.)
32
+ # Specify default Accept header
33
+ setup_http_client(
34
+ base_url: API_BASE_URL,
35
+ headers: { 'Accept' => 'application/json' }
36
+ # options: { read_timeout: 5 } # Example: Override default timeout
37
+ )
38
+ Legate.logger.info 'JsonPlaceholderTool initialized.'
39
+ end
40
+
41
+ private
42
+
43
+ # Main logic for the tool
44
+ def perform_execution(params, context)
45
+ action = params.fetch(:action).downcase
46
+
47
+ case action
48
+ when 'get'
49
+ fetch_post(params, context)
50
+ when 'create'
51
+ create_post(params, context)
52
+ else
53
+ raise Legate::ToolArgumentError, "Invalid action specified: '#{action}'. Use 'get' or 'create'."
54
+ end
55
+
56
+ # Rescue ToolErrors raised by HttpClient or argument validation
57
+ rescue Legate::ToolError => e
58
+ Legate.logger.error("JsonPlaceholderTool Error: #{e.class} - #{e.message}")
59
+ # You might want more specific handling based on e.g., e.is_a?(Legate::ToolHttpError)
60
+ { status: :error, error_message: "API operation failed: #{e.message}" }
61
+ end
62
+
63
+ # --- Helper Methods ---
64
+
65
+ def fetch_post(params, _context)
66
+ post_id = params[:post_id]
67
+ raise Legate::ToolArgumentError, "Missing required parameter: post_id for action 'get'" unless post_id
68
+
69
+ Legate.logger.info "Fetching post with ID: #{post_id}"
70
+ # Use the http_get helper from HttpClient
71
+ response = http_get("posts/#{post_id}") # Path relative to base_url
72
+
73
+ # Parse the response body
74
+ begin
75
+ data = JSON.parse(response.body)
76
+ { status: :success, result: data }
77
+ rescue JSON::ParserError => e
78
+ raise Legate::ToolError, "Failed to parse JSON response: #{e.message}"
79
+ end
80
+ end
81
+
82
+ def create_post(params, _context)
83
+ post_data = params[:post_data]
84
+ unless post_data.is_a?(Hash) && !post_data.empty?
85
+ raise Legate::ToolArgumentError,
86
+ "Missing required parameter: post_data for action 'create'"
87
+ end
88
+
89
+ Legate.logger.info "Creating post with data: #{post_data.inspect}"
90
+ # Use the http_post helper. Payload is a Hash, HttpClient handles JSON encoding
91
+ # and sets Content-Type: application/json automatically.
92
+ response = http_post('posts', body: post_data)
93
+
94
+ # Check response status (optional, HttpClient raises ToolHttpError for 4xx/5xx)
95
+ # Here we just return the parsed response body which includes the new ID
96
+ begin
97
+ created_post = JSON.parse(response.body)
98
+ { status: :success, result: created_post }
99
+ rescue JSON::ParserError => e
100
+ raise Legate::ToolError, "Failed to parse JSON response after creating post: #{e.message}"
101
+ end
102
+ end
103
+ end
104
+
105
+ # --- Example Usage ---
106
+
107
+ # Ensure Legate logger is setup (usually done in main application)
108
+ Legate.logger.level = Logger::INFO
109
+
110
+ # Create an instance of the tool
111
+ placeholder_tool = JsonPlaceholderTool.new
112
+
113
+ # Create a dummy context (provide required keywords)
114
+ dummy_context = Legate::ToolContext.new(
115
+ session_id: 'dummy-session-123',
116
+ user_id: 'example-user',
117
+ app_name: 'http_client_example'
118
+ )
119
+
120
+ # Example 1: Fetch post with ID 1
121
+ puts "\n--- Fetching Post 1 ---"
122
+ fetch_params = { action: 'get', post_id: 1 }
123
+ fetch_result = placeholder_tool.execute(fetch_params, context: dummy_context)
124
+ puts "Result: #{fetch_result.inspect}"
125
+
126
+ puts "\n--- Fetching Non-existent Post 999 ---"
127
+ fetch_params_bad = { action: 'get', post_id: 999 }
128
+ fetch_result_bad = placeholder_tool.execute(fetch_params_bad, context: dummy_context)
129
+ puts "Result: #{fetch_result_bad.inspect}" # Expects :error status due to 404
130
+
131
+ # Example 2: Create a new post
132
+ puts "\n--- Creating New Post ---"
133
+ create_params = {
134
+ action: 'create',
135
+ post_data: {
136
+ title: 'Legate Test Post',
137
+ body: 'This post was created by the Legate HttpClient example.',
138
+ userId: 5
139
+ }
140
+ }
141
+ create_result = placeholder_tool.execute(create_params, context: dummy_context)
142
+ puts "Result: #{create_result.inspect}" # Expects :success with new post data (including ID)
143
+
144
+ # Example 3: Invalid action
145
+ puts "\n--- Invalid Action (Expected Error) ---"
146
+ puts '(This demonstrates the tool raising ToolArgumentError for unsupported actions)'
147
+ invalid_params = { action: 'delete', post_id: 1 } # Action not supported by our tool
148
+ invalid_result = placeholder_tool.execute(invalid_params, context: dummy_context)
149
+ puts "Result: #{invalid_result.inspect}" # Expects :error status
150
+
151
+ # Example 4: Missing required param
152
+ puts "\n--- Missing Parameter (Expected Error) ---"
153
+ puts '(This demonstrates the tool raising ToolArgumentError for missing required params)'
154
+ missing_params = { action: 'get' } # Missing post_id
155
+ missing_result = placeholder_tool.execute(missing_params, context: dummy_context)
156
+ puts "Result: #{missing_result.inspect}" # Expects :error status