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,512 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example of a Sequential Travel Planning Process
5
+ # Run with: bundle exec ruby examples/advanced/workflows/travel_planner_sequential.rb
6
+ #
7
+ # This example demonstrates how to create a sequential agent pattern where multiple specialized
8
+ # agents are executed in sequence, with each agent handling a specific part of a larger task.
9
+ #
10
+ # Key components of this example:
11
+ # 1. Multiple specialized agents: destination_research, itinerary_planner, budget_estimator, and trip_summarizer
12
+ # 2. A parent sequential agent that orchestrates the execution of the specialized agents
13
+ # 3. Each agent has its own output_key to store its results in the shared session state
14
+ # 4. The sequential agent architecture allows complex workflows to be broken down into manageable steps
15
+ #
16
+ # The architecture follows these principles:
17
+ # - Each agent is fully specialized for its task
18
+ # - Agents share the same session, allowing them to access each other's outputs
19
+ # - The parent-child relationship is explicitly established through instance variables
20
+ # - The execution order is defined using sequential_sub_agents in the parent agent definition
21
+
22
+ require_relative '../../../lib/legate'
23
+ require_relative '../../../lib/legate/agents/sequential_agent' # Required for SequentialAgent
24
+
25
+ # Check if tty-spinner is installed
26
+ begin
27
+ require 'tty-spinner'
28
+ rescue LoadError
29
+ puts 'The tty-spinner gem is required for this example.'
30
+ puts 'Please install it with: gem install tty-spinner'
31
+ puts 'Or add it to your Gemfile and run: bundle install'
32
+ exit 1
33
+ end
34
+
35
+ # Clear any existing registrations to avoid conflicts
36
+ Legate::GlobalDefinitionRegistry.instance_variable_set(:@definitions, {})
37
+
38
+ puts '=== Travel Planner Sequential Process Example ==='
39
+ puts 'This example demonstrates a sequence of specialized agents to plan a trip'
40
+
41
+ # Ensure the echo tool is registered - all agents will use this
42
+ Legate::GlobalToolManager.register_tool(Legate::Tools::Echo) unless Legate::GlobalToolManager.registered_tool_names.include?(:echo)
43
+
44
+ # Ensure delegate_task tool is registered for agent delegation
45
+ Legate::GlobalToolManager.register_tool(Legate::Tools::AgentTool) unless Legate::GlobalToolManager.registered_tool_names.include?(:delegate_task)
46
+
47
+ # ----- Define Specialized Agents -----
48
+
49
+ # 1. Destination Research Agent
50
+ destination_agent_def = Legate::AgentDefinition.new.define do |a|
51
+ a.name :destination_research
52
+ a.description 'Researches and suggests destination options based on user preferences'
53
+ a.instruction <<~INSTRUCTION
54
+ You are a destination research specialist. Your task is to analyze the user's preferences and suggest 2-3 suitable destinations.
55
+
56
+ First, think about what destinations would be appropriate based on the user's preferences. Then, use the 'echo' tool to output your response.
57
+
58
+ Your echo response should follow this EXACT format:
59
+
60
+ # DESTINATION RECOMMENDATIONS
61
+
62
+ Based on your preferences, here are the destinations I recommend:
63
+
64
+ ## [Destination 1 Name]
65
+ - **Location**: [Region/Country]
66
+ - **Weather**: [Weather description for the time period]
67
+ - **Highlights**: [3-4 key attractions or experiences]
68
+ - **Best For**: [What makes this destination perfect for the user]
69
+
70
+ ## [Destination 2 Name]
71
+ - **Location**: [Region/Country]
72
+ - **Weather**: [Weather description for the time period]
73
+ - **Highlights**: [3-4 key attractions or experiences]
74
+ - **Best For**: [What makes this destination perfect for the user]
75
+
76
+ ## [Destination 3 Name] (optional)
77
+ - **Location**: [Region/Country]
78
+ - **Weather**: [Weather description for the time period]
79
+ - **Highlights**: [3-4 key attractions or experiences]
80
+ - **Best For**: [What makes this destination perfect for the user]
81
+
82
+ # RECOMMENDATION SUMMARY
83
+ [Brief explanation of why these destinations match the user's preferences]
84
+ INSTRUCTION
85
+ a.model_name 'gemini-3.5-flash'
86
+ a.output_key :destination_results
87
+ a.use_tool :echo
88
+ end
89
+
90
+ # 2. Itinerary Planning Agent
91
+ itinerary_agent_def = Legate::AgentDefinition.new.define do |a|
92
+ a.name :itinerary_planner
93
+ a.description 'Creates a detailed itinerary for the selected destination'
94
+ a.instruction <<~INSTRUCTION
95
+ You are an itinerary planner. Based on the destination information provided, create a 3-day itinerary for the most suitable destination.
96
+
97
+ First, analyze the destination information provided. Then, use the 'echo' tool to output your response.
98
+
99
+ Your echo response should follow this EXACT format:
100
+
101
+ # 3-DAY ITINERARY FOR [DESTINATION]
102
+
103
+ ## Day 1
104
+
105
+ **Morning**
106
+ - [Activity]: [Brief description]
107
+ - [Activity]: [Brief description]
108
+
109
+ **Afternoon**
110
+ - [Activity]: [Brief description]
111
+ - [Activity]: [Brief description]
112
+
113
+ **Evening**
114
+ - [Activity]: [Brief description]
115
+ - [Activity]: [Brief description]
116
+
117
+ ## Day 2
118
+
119
+ **Morning**
120
+ - [Activity]: [Brief description]
121
+ - [Activity]: [Brief description]
122
+
123
+ **Afternoon**
124
+ - [Activity]: [Brief description]
125
+ - [Activity]: [Brief description]
126
+
127
+ **Evening**
128
+ - [Activity]: [Brief description]
129
+ - [Activity]: [Brief description]
130
+
131
+ ## Day 3
132
+
133
+ **Morning**
134
+ - [Activity]: [Brief description]
135
+ - [Activity]: [Brief description]
136
+
137
+ **Afternoon**
138
+ - [Activity]: [Brief description]
139
+ - [Activity]: [Brief description]
140
+
141
+ **Evening**
142
+ - [Activity]: [Brief description]
143
+ - [Activity]: [Brief description]
144
+
145
+ # TRANSPORTATION TIPS
146
+ [Brief notes on getting around the destination]
147
+ INSTRUCTION
148
+ a.model_name 'gemini-3.5-flash'
149
+ a.output_key :itinerary_results
150
+ a.use_tool :echo
151
+ end
152
+
153
+ # 3. Budget Estimation Agent
154
+ budget_agent_def = Legate::AgentDefinition.new.define do |a|
155
+ a.name :budget_estimator
156
+ a.description 'Provides cost estimates for the planned trip'
157
+ a.instruction <<~INSTRUCTION
158
+ You are a travel budget specialist. Based on the destination and activities in the itinerary, provide a detailed cost estimate.
159
+
160
+ First, analyze the destination and itinerary information provided. Then, use the 'echo' tool to output your response.
161
+
162
+ Your echo response should follow this EXACT format:
163
+
164
+ # BUDGET ESTIMATE FOR [DESTINATION]
165
+
166
+ ## Estimated Total: $[AMOUNT] USD
167
+
168
+ ## Cost Breakdown
169
+
170
+ **Flights**
171
+ - Estimated cost: $[AMOUNT] USD
172
+ - Notes: [Brief notes about flight options/assumptions]
173
+
174
+ **Accommodation (3 nights)**
175
+ - Estimated cost: $[AMOUNT] USD ($[AMOUNT]/night)
176
+ - Type: [Hotel/Airbnb/etc.]
177
+ - Notes: [Brief notes about accommodation options]
178
+
179
+ **Daily Activities**
180
+ - Estimated cost: $[AMOUNT] USD
181
+ - Includes: [List of paid activities from itinerary]
182
+
183
+ **Food & Dining**
184
+ - Estimated cost: $[AMOUNT] USD ($[AMOUNT]/day)
185
+ - Includes: [Assumptions about meals]
186
+
187
+ **Local Transportation**
188
+ - Estimated cost: $[AMOUNT] USD
189
+ - Type: [Public transit/rental car/taxis/etc.]
190
+
191
+ **Miscellaneous**
192
+ - Estimated cost: $[AMOUNT] USD
193
+ - Includes: [Souvenirs, tips, unexpected expenses, etc.]
194
+
195
+ # MONEY-SAVING TIPS
196
+ - [Tip 1]
197
+ - [Tip 2]
198
+ - [Tip 3]
199
+ INSTRUCTION
200
+ a.model_name 'gemini-3.5-flash'
201
+ a.output_key :budget_results
202
+ a.use_tool :echo
203
+ end
204
+
205
+ # 4. Trip Summary Agent
206
+ summary_agent_def = Legate::AgentDefinition.new.define do |a|
207
+ a.name :trip_summarizer
208
+ a.description 'Creates a comprehensive trip summary from all previous results'
209
+ a.instruction <<~INSTRUCTION
210
+ You are a travel summary specialist. Create a concise, well-formatted trip summary that brings together all the information from the destination research, itinerary, and budget.
211
+
212
+ You are the final agent in a sequence of specialized travel planning agents. Your job is to summarize the outputs of the previous agents and create a final trip summary. The user input includes all previous agent outputs.
213
+
214
+ First, analyze all the information provided from the previous steps. Then, use the 'echo' tool to output your response.
215
+
216
+ Your echo response should follow this EXACT format:
217
+
218
+ # COMPLETE TRAVEL PLAN
219
+
220
+ ## Destination Overview
221
+ [Summarize key points about the destination(s) discussed in previous steps]
222
+
223
+ ## Trip Highlights
224
+ - [Highlight 1]
225
+ - [Highlight 2]
226
+ - [Highlight 3]
227
+
228
+ ## Budget Considerations
229
+ - **Daily Budget**: $[AMOUNT] USD
230
+ - **Main Expenses**: [Brief note on biggest expenses]
231
+ - **Savings Opportunities**: [Key money-saving tip]
232
+
233
+ ## Final Recommendations
234
+ [2-3 sentences with final personalized recommendations]
235
+
236
+ ## Additional Information
237
+ This trip plan was created by a sequence of specialized agents:
238
+ 1. Destination Research - Analyzed preferences and suggested destinations
239
+ 2. Itinerary Planning - Created daily activities
240
+ 3. Budget Estimation - Estimated costs
241
+ 4. Trip Summary - Combined all information (this output)
242
+ INSTRUCTION
243
+ a.model_name 'gemini-3.5-flash'
244
+ a.output_key :trip_summary
245
+ a.use_tool :echo
246
+ end
247
+
248
+ # 5. NEW: Parent Sequential Agent Definition
249
+ travel_planner_def = Legate::AgentDefinition.new.define do |a|
250
+ a.name :travel_planner
251
+ a.description 'Orchestrates the complete travel planning process'
252
+ a.instruction 'This is a sequential agent that coordinates multiple specialized agents to plan a complete trip.'
253
+ a.model_name 'gemini-3.5-flash'
254
+ a.output_key :complete_travel_plan
255
+ a.agent_type :sequential # Important! This tells Legate to use SequentialAgent
256
+ a.sequential_sub_agents :destination_research, :itinerary_planner, :budget_estimator, :trip_summarizer
257
+ end
258
+
259
+ # Register all agents globally
260
+ Legate::GlobalDefinitionRegistry.register(destination_agent_def)
261
+ Legate::GlobalDefinitionRegistry.register(itinerary_agent_def)
262
+ Legate::GlobalDefinitionRegistry.register(budget_agent_def)
263
+ Legate::GlobalDefinitionRegistry.register(summary_agent_def)
264
+ Legate::GlobalDefinitionRegistry.register(travel_planner_def)
265
+
266
+ # ----- Initialize and Run the Sequential Agent -----
267
+
268
+ puts "\nStarting the specialized travel planning process..."
269
+
270
+ # Create an in-memory session service that all agents will share
271
+ session_service = Legate::SessionService::InMemory.new
272
+
273
+ # Create session
274
+ session = session_service.create_session(app_name: 'travel_planner', user_id: 'example_user')
275
+ session_id = session.id
276
+ puts "Created session: #{session_id}\n\n"
277
+
278
+ # Define the travel planning request
279
+ base_user_input = "I'd like to plan a relaxing vacation for early June. I enjoy nature, good food, and cultural experiences. My budget is moderate, and I prefer places with warm but not hot weather."
280
+
281
+ # Add specialized prompts for each agent to help them understand their roles better
282
+ # This would typically be handled by the agent instructions, but we'll make it explicit
283
+ # in the user input to help the demo function correctly
284
+ user_input = <<~INPUT
285
+ #{base_user_input}
286
+
287
+ IMPORTANT CONTEXT: This request will be processed by a sequence of specialized agents:
288
+ 1. DESTINATION RESEARCH: You will research and suggest 2-3 suitable destinations matching my preferences
289
+ 2. ITINERARY PLANNING: You will create a detailed day-by-day itinerary for the best option
290
+ 3. BUDGET ESTIMATION: You will estimate costs for the trip
291
+ 4. TRIP SUMMARY: You will combine all information into a final travel plan
292
+ 5. USE US DOLLARS FOR ALL MONEY ESTIMATES
293
+
294
+ Please follow your specific role in this sequence.
295
+ INPUT
296
+
297
+ puts 'Processing travel planning request...'
298
+ puts "User input: #{base_user_input}\n"
299
+
300
+ # Print the sequence information
301
+ puts 'This will use a SequentialAgent to execute 4 specialized agents:'
302
+ puts "1. Destination Research → 2. Itinerary Planning → 3. Budget Estimation → 4. Trip Summary\n"
303
+
304
+ # Create a multi-spinner for tracking all processes
305
+ spinners = TTY::Spinner::Multi.new('[:spinner] Travel Planning Process', format: :dots, success_mark: '✅', error_mark: '❌')
306
+
307
+ # First, ensure the echo tool is registered globally
308
+ puts 'Ensuring the Echo tool is globally registered...'
309
+ Legate::GlobalToolManager.register_tool(Legate::Tools::Echo) unless Legate::GlobalToolManager.registered_tool_names.include?(:echo)
310
+
311
+ # Create instances of all the individual sub-agents
312
+ destination_agent = Legate::Agent.new(definition: destination_agent_def, session_service: session_service)
313
+ itinerary_agent = Legate::Agent.new(definition: itinerary_agent_def, session_service: session_service)
314
+ budget_agent = Legate::Agent.new(definition: budget_agent_def, session_service: session_service)
315
+ summary_agent = Legate::Agent.new(definition: summary_agent_def, session_service: session_service)
316
+
317
+ # Explicitly add the echo tool to each agent
318
+ puts 'Adding Echo tool to each agent...'
319
+ [destination_agent, itinerary_agent, budget_agent, summary_agent].each do |agent|
320
+ agent.add_tool(Legate::Tools::Echo)
321
+ end
322
+
323
+ # Create the parent sequential agent instance with all sub-agents explicitly provided
324
+ travel_planner = Legate::Agents::SequentialAgent.new(
325
+ definition: travel_planner_def,
326
+ session_service: session_service,
327
+ sub_agents: [destination_agent, itinerary_agent, budget_agent, summary_agent]
328
+ )
329
+
330
+ # Define a custom wrapper class that enhances the input for each agent
331
+ # This helps demonstrate how a sequential agent can be customized
332
+ class TravelPlannerSequentialAgent
333
+ def initialize(sequential_agent)
334
+ @sequential_agent = sequential_agent
335
+ @sub_agents = sequential_agent.instance_variable_get(:@sub_agents) # Access private variable
336
+ end
337
+
338
+ def start
339
+ @sequential_agent.start
340
+ end
341
+
342
+ def run_task(session_id:, user_input:, session_service:, spinners:)
343
+ # First, store the original user input in the session state
344
+ session_service.set_state(session_id: session_id, key: :original_request, value: user_input)
345
+
346
+ # Record the user's request as an event
347
+ user_event = Legate::Event.new(role: :user, content: user_input)
348
+ session_service.append_event(session_id: session_id, event: user_event)
349
+
350
+ puts "\nExecuting each agent in sequence with specialized context..."
351
+
352
+ # 1. Destination Research - First Agent gets the original request with emphasis on destinations
353
+ destination_spinner = spinners.register('[:spinner] Destination Research')
354
+ destination_spinner.auto_spin
355
+
356
+ destination_input = "#{user_input}\n\nYou are the DESTINATION RESEARCH agent. Your task is to suggest 2-3 destinations that match the preferences."
357
+ destination_result = @sub_agents[0].run_task(
358
+ session_id: session_id,
359
+ user_input: destination_input,
360
+ session_service: session_service
361
+ )
362
+ destination_spinner.success
363
+
364
+ # 2. Itinerary Planning - Gets the destinations and creates an itinerary
365
+ itinerary_spinner = spinners.register('[:spinner] Itinerary Planning')
366
+ itinerary_spinner.auto_spin
367
+
368
+ destination_data = session_service.get_state(session_id: session_id, key: :destination_results)
369
+ itinerary_input = "#{user_input}\n\nYou are the ITINERARY PLANNING agent. Your task is to create a detailed 3-day itinerary.\n\nPrevious agent output:\n#{destination_data ? destination_data['result'] : 'No destination data available'}"
370
+ itinerary_result = @sub_agents[1].run_task(
371
+ session_id: session_id,
372
+ user_input: itinerary_input,
373
+ session_service: session_service
374
+ )
375
+ itinerary_spinner.success
376
+
377
+ # 3. Budget Estimation - Gets the itinerary and estimates costs
378
+ budget_spinner = spinners.register('[:spinner] Budget Estimation')
379
+ budget_spinner.auto_spin
380
+
381
+ itinerary_data = session_service.get_state(session_id: session_id, key: :itinerary_results)
382
+ budget_input = "#{user_input}\n\nYou are the BUDGET ESTIMATION agent. Your task is to provide a detailed cost breakdown.\n\nPrevious agent outputs:\n#{destination_data ? destination_data['result'] : 'No destination data available'}\n#{itinerary_data ? itinerary_data['result'] : 'No itinerary data available'}"
383
+ budget_result = @sub_agents[2].run_task(
384
+ session_id: session_id,
385
+ user_input: budget_input,
386
+ session_service: session_service
387
+ )
388
+ budget_spinner.success
389
+
390
+ # 4. Trip Summary - Gets all previous data and creates a final summary
391
+ summary_spinner = spinners.register('[:spinner] Trip Summary')
392
+ summary_spinner.auto_spin
393
+
394
+ budget_data = session_service.get_state(session_id: session_id, key: :budget_results)
395
+ summary_input = "#{user_input}\n\nYou are the TRIP SUMMARY agent. Your task is to create a comprehensive summary of all previous results.\n\nPrevious agent outputs:\n#{destination_data ? destination_data['result'] : 'No destination data available'}\n#{itinerary_data ? itinerary_data['result'] : 'No itinerary data available'}\n#{budget_data ? budget_data['result'] : 'No budget data available'}"
396
+ summary_result = @sub_agents[3].run_task(
397
+ session_id: session_id,
398
+ user_input: summary_input,
399
+ session_service: session_service
400
+ )
401
+ summary_spinner.success
402
+
403
+ # Return the final summary result
404
+ summary_result
405
+ end
406
+ end
407
+
408
+ # Create and use the enhanced travel planner
409
+ enhanced_travel_planner = TravelPlannerSequentialAgent.new(travel_planner)
410
+
411
+ # Start all agents
412
+ puts 'Starting all agents...'
413
+ destination_agent.start
414
+ itinerary_agent.start
415
+ budget_agent.start
416
+ summary_agent.start
417
+ enhanced_travel_planner.start
418
+
419
+ # Create master spinner
420
+ master_spinner = spinners.register('[:spinner] Total Progress')
421
+ master_spinner.auto_spin
422
+
423
+ # Run the sequential agent
424
+ begin
425
+ # The SequentialAgent automatically runs all sub-agents in sequence.
426
+ # Each agent stores its output in the session state using its output_key.
427
+ # Subsequent agents can access previous agents' outputs by retrieving values from
428
+ # the session state. This is handled automatically by the SequentialAgent class.
429
+ result = enhanced_travel_planner.run_task(
430
+ session_id: session_id,
431
+ user_input: user_input,
432
+ session_service: session_service,
433
+ spinners: spinners
434
+ )
435
+
436
+ if result.content[:status] == :error
437
+ puts "Error in sequential execution: #{result.content[:error_message]}"
438
+ master_spinner.error
439
+ exit 1
440
+ end
441
+
442
+ # Mark the master spinner as complete
443
+ master_spinner.success
444
+
445
+ # Get all session data directly
446
+ session = session_service.get_session(session_id: session_id)
447
+
448
+ # Print the raw session state for debugging purposes
449
+ puts "\nDEBUG: Session State Contents"
450
+ puts '----------------------------'
451
+ session.state.each do |key, value|
452
+ puts "#{key}: #{value.inspect[0..200]}..." if value
453
+ end
454
+ puts
455
+
456
+ # Display the final trip summary
457
+ puts "\n=== Travel Planning Complete ===\n\n"
458
+
459
+ puts 'SEQUENTIAL AGENT RESULTS'
460
+ puts '------------------------'
461
+ puts "The sequential agent successfully executed all sub-agents in sequence.\n\n"
462
+
463
+ puts 'STEP 1: DESTINATION RESEARCH'
464
+ puts '----------------------------'
465
+ if session.state[:destination_results] && session.state[:destination_results]['result']
466
+ puts session.state[:destination_results]['result']
467
+ else
468
+ puts '(No destination research results available)'
469
+ end
470
+ puts "\n"
471
+
472
+ puts 'STEP 2: ITINERARY PLANNING'
473
+ puts '-------------------------'
474
+ if session.state[:itinerary_results] && session.state[:itinerary_results]['result']
475
+ puts session.state[:itinerary_results]['result']
476
+ else
477
+ puts '(No itinerary planning results available)'
478
+ end
479
+ puts "\n"
480
+
481
+ puts 'STEP 3: BUDGET ESTIMATION'
482
+ puts '-------------------------'
483
+ if session.state[:budget_results] && session.state[:budget_results]['result']
484
+ puts session.state[:budget_results]['result']
485
+ else
486
+ puts '(No budget estimation results available)'
487
+ end
488
+ puts "\n"
489
+
490
+ puts 'STEP 4: FINAL TRAVEL SUMMARY'
491
+ puts '---------------------------'
492
+ if session.state[:trip_summary] && session.state[:trip_summary]['result']
493
+ puts session.state[:trip_summary]['result']
494
+ else
495
+ puts '(No trip summary available)'
496
+ end
497
+ puts "\n"
498
+
499
+ puts 'Note: This example uses a custom wrapper around SequentialAgent to make the sub-agent sequence'
500
+ puts 'more explicit and to clearly demonstrate how data can be passed between agents.'
501
+ puts 'In production, the Legate::Agents::SequentialAgent class would manage this automatically without'
502
+ puts 'requiring a custom implementation. This approach is just for demonstration purposes.'
503
+ puts 'The example shows that agents can either use the built-in sequential processing or'
504
+ puts 'implement custom orchestration logic as shown here.'
505
+ rescue StandardError => e
506
+ puts "Error during execution: #{e.message}"
507
+ puts e.backtrace.join("\n")
508
+ master_spinner.error if master_spinner
509
+ exit 1
510
+ end
511
+
512
+ puts "\n=== Travel Planner Example Complete ==="
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'legate'
5
+ require 'legate/auth/schemes/oauth2'
6
+ require 'sinatra/base'
7
+ require 'webrick'
8
+ require 'logger'
9
+ require 'json'
10
+
11
+ class GitHubUserTool < Legate::Tool::Base
12
+ name 'github_user'
13
+ description 'Get information about a GitHub user using OAuth2 authentication'
14
+ display_name 'GitHub User'
15
+
16
+ parameter :username, type: :string, description: 'GitHub username to get information about'
17
+
18
+ def call(username:)
19
+ raise Legate::Tool::ExecutionError, 'Authentication required. Please run the tool with authentication enabled.' unless secured_credential
20
+
21
+ # Get user information from GitHub API
22
+ response = Legate::Auth.apply_authentication(
23
+ {
24
+ method: :get,
25
+ url: "https://api.github.com/users/#{username}",
26
+ headers: {
27
+ 'Accept' => 'application/vnd.github.v3+json',
28
+ 'User-Agent' => 'Legate-Ruby-Example'
29
+ }
30
+ },
31
+ secured_credential
32
+ )
33
+
34
+ Legate::Tool::Response.success(JSON.parse(response.body))
35
+ rescue Excon::Error => e
36
+ Legate::Tool::Response.error("GitHub API error: #{e.message}")
37
+ end
38
+ end
39
+
40
+ # Create the OAuth callback server for the OAuth2 flow
41
+ class OAuthCallbackServer < Sinatra::Base
42
+ set :port, 3000
43
+
44
+ get '/oauth/callback' do
45
+ # Handle the OAuth callback
46
+ Legate::Auth.handle_oauth_callback(request.url)
47
+
48
+ <<~HTML
49
+ <html>
50
+ <body>
51
+ <h1>Authorization Successful</h1>
52
+ <p>You can close this window and return to the application.</p>
53
+ <script>window.close()</script>
54
+ </body>
55
+ </html>
56
+ HTML
57
+ end
58
+ end
59
+
60
+ # Main example code
61
+ def run_example
62
+ # Configure logging
63
+ Legate.configure do |config|
64
+ config.logger = Logger.new($stdout)
65
+ config.logger.level = Logger::INFO
66
+ end
67
+
68
+ # Start the callback server in a thread
69
+ Thread.new do
70
+ OAuthCallbackServer.run!
71
+ end
72
+
73
+ # Define the OAuth2 provider ID
74
+ provider_id = 'github'
75
+
76
+ # Create the OAuth2 scheme
77
+ scheme = Legate::Auth::Schemes::OAuth2.new(
78
+ authorization_url: 'https://github.com/login/oauth/authorize',
79
+ token_url: 'https://github.com/login/oauth/access_token',
80
+ scopes: ['user:email', 'read:user']
81
+ )
82
+
83
+ # Create the credential with client information
84
+ # In a real application, you would load these from environment variables or a secure store
85
+ credential = Legate::Auth::Credential.new(
86
+ auth_type: :oauth2,
87
+ client_id: 'YOUR_GITHUB_CLIENT_ID',
88
+ client_secret: 'YOUR_GITHUB_CLIENT_SECRET'
89
+ )
90
+
91
+ # Register the tool
92
+ Legate::Tool::Registry.register(GitHubUserTool)
93
+
94
+ # Create a session
95
+ session = Legate::SessionService::Memory.new
96
+
97
+ # Start the OAuth2 flow
98
+ auth_uri = Legate::Auth.start_oauth_flow(
99
+ provider_id,
100
+ scheme,
101
+ credential,
102
+ 'http://localhost:3000/oauth/callback'
103
+ )
104
+
105
+ puts 'Please open the following URL in your browser to authorize the application:'
106
+ puts auth_uri
107
+ puts 'Waiting for authorization...'
108
+
109
+ # Wait for the callback and exchange the code for tokens
110
+ exchanged_credential = Legate::Auth.complete_oauth_flow(provider_id)
111
+
112
+ puts 'Successfully authenticated with GitHub!'
113
+ puts "Access token: #{exchanged_credential.access_token[0..5]}... (expires in #{exchanged_credential.expires_in} seconds)"
114
+
115
+ # Create a runner with the secured credential
116
+ runner = Legate::Runner.new(session: session)
117
+
118
+ # Run the tool with authentication
119
+ result = runner.run_tool(
120
+ tool_name: 'github_user',
121
+ parameters: { username: 'octocat' },
122
+ auth_credential: exchanged_credential
123
+ )
124
+
125
+ puts 'User information:'
126
+ puts JSON.pretty_generate(result.data)
127
+
128
+ # Refresh the token if it's about to expire
129
+ return unless exchanged_credential.refreshable? && exchanged_credential.expired?(300)
130
+
131
+ puts 'Refreshing the access token...'
132
+ refreshed_credential = Legate::Auth.refresh_token(provider_id)
133
+ puts 'Token refreshed successfully!'
134
+ end
135
+
136
+ run_example if $PROGRAM_NAME == __FILE__
@@ -0,0 +1,42 @@
1
+ # File: examples/tools/sleepy_tool.rb
2
+ # frozen_string_literal: true
3
+
4
+ # Ensure the base tool is loadable
5
+ require_relative '../../lib/legate/tools/base_async_job_tool'
6
+
7
+ module Legate
8
+ module Tools
9
+ # An example Legate tool that starts a background job that sleeps.
10
+ class SleepyTool < BaseAsyncJobTool
11
+ # --- New DSL Metadata ---
12
+ # Name will be inferred as :sleepy_tool
13
+ self.explicit_tool_name = :start_sleepy_job # Keep original name
14
+
15
+ tool_description 'Starts a background job that sleeps for a specified duration and then returns a message.'
16
+
17
+ parameter :duration,
18
+ type: :integer,
19
+ description: 'How many seconds the job should sleep.',
20
+ required: true
21
+
22
+ parameter :message,
23
+ type: :string,
24
+ description: 'A message to include in the final result.',
25
+ required: true
26
+ # --- End New DSL Metadata ---
27
+
28
+ # Return the worker class to run in the background.
29
+ def worker_class
30
+ SleepyWorker
31
+ end
32
+
33
+ # Prepare the arguments for the worker's perform method.
34
+ # Arguments must be JSON-serializable.
35
+ def prepare_job_arguments(params, _context)
36
+ duration = params[:duration].to_i
37
+ message = params[:message].to_s
38
+ [duration, message] # Must match SleepyWorker#perform signature
39
+ end
40
+ end
41
+ end
42
+ end