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,191 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'legate'
6
+
7
+ # Set up the session service
8
+ Legate.config.session_service = Legate::SessionService::InMemory.new
9
+
10
+ # Register a simple echo tool
11
+ class EchoTool < Legate::Tool
12
+ def self.tool_metadata
13
+ {
14
+ name: :echo,
15
+ description: 'Echo a message back to the user',
16
+ parameters: {
17
+ message: {
18
+ type: 'string',
19
+ description: 'The message to echo back',
20
+ required: true
21
+ }
22
+ }
23
+ }
24
+ end
25
+
26
+ def call(params)
27
+ message = params[:message] || 'No message provided'
28
+ { result: message }
29
+ end
30
+ end
31
+
32
+ # Register the echo tool
33
+ Legate::GlobalToolManager.register_tool(EchoTool)
34
+
35
+ # Define a Calculator Agent
36
+ calculator_agent = Legate::AgentDefinition.new.define do |a|
37
+ a.name :calculator_agent
38
+ a.description 'An agent specialized in mathematical calculations'
39
+ a.instruction 'You are a mathematical assistant.'
40
+ a.use_tool :echo
41
+ end
42
+
43
+ # Define a Research Agent
44
+ researcher_agent = Legate::AgentDefinition.new.define do |a|
45
+ a.name :researcher_agent
46
+ a.description 'An agent specialized in answering research questions'
47
+ a.instruction 'You are a research assistant.'
48
+ a.use_tool :echo
49
+ end
50
+
51
+ # Define the Coordinator Agent
52
+ coordinator_agent = Legate::AgentDefinition.new.define do |a|
53
+ a.name :coordinator_agent
54
+ a.description 'An agent that coordinates tasks by delegating to specialized agents'
55
+ a.instruction 'You are a coordinator agent.'
56
+ a.use_tool :echo
57
+ a.can_delegate_to :calculator_agent, :researcher_agent
58
+ end
59
+
60
+ # Register all agent definitions
61
+ Legate::GlobalDefinitionRegistry.register(calculator_agent)
62
+ Legate::GlobalDefinitionRegistry.register(researcher_agent)
63
+ Legate::GlobalDefinitionRegistry.register(coordinator_agent)
64
+
65
+ # Create a single session for the interaction
66
+ session_service = Legate.config.session_service
67
+ session = session_service.create_session(app_name: coordinator_agent.name, user_id: 'delegation_example_user')
68
+ session_id = session.id
69
+
70
+ # Create the agent instances
71
+ coordinator = Legate::Agent.new(definition: coordinator_agent)
72
+ calculator = Legate::Agent.new(definition: calculator_agent)
73
+ researcher = Legate::Agent.new(definition: researcher_agent)
74
+
75
+ # Custom delegation implementation
76
+ class CustomCoordinator < Legate::Agent
77
+ def run_task(session_id:, user_input:, session_service:, **_options)
78
+ # First check for math expressions with a more precise regex
79
+ if is_math_question?(user_input)
80
+ # Mathematics question - route to calculator
81
+ puts 'Delegating to calculator agent...'
82
+ calculator = find_agent(:calculator_agent)
83
+ result = calculator.run_task(session_id: session_id, user_input: user_input, session_service: session_service)
84
+ Legate::Event.new(role: :agent, content: { result: "Calculator says: #{result.content[:result]}" })
85
+ elsif is_research_question?(user_input)
86
+ # Knowledge question - route to researcher
87
+ puts 'Delegating to researcher agent...'
88
+ researcher = find_agent(:researcher_agent)
89
+ result = researcher.run_task(session_id: session_id, user_input: user_input, session_service: session_service)
90
+ Legate::Event.new(role: :agent, content: { result: "Researcher says: #{result.content[:result]}" })
91
+ else
92
+ # Default handling - echo
93
+ Legate::Event.new(role: :agent, content: { result: "I'm not sure how to handle: #{user_input}" })
94
+ end
95
+ end
96
+
97
+ private
98
+
99
+ def is_math_question?(input)
100
+ # Check for explicit math operators between numbers
101
+ return true if input =~ %r{\d\s*[+\-*/]\s*\d}
102
+
103
+ # Check for explicit math keywords
104
+ math_keywords = %w[calculate math sum add subtract multiply divide]
105
+ math_keywords.any? { |keyword| input.downcase.include?(keyword) }
106
+ end
107
+
108
+ def is_research_question?(input)
109
+ # Check for question words
110
+ question_patterns = [
111
+ /what\s+is/i, /where\s+is/i, /who\s+is/i, /why\s+is/i, /how\s+is/i,
112
+ /tell\s+me\s+about/i, /capital/i, /country/i,
113
+ /what\s+are/i, /where\s+are/i, /who\s+are/i
114
+ ]
115
+
116
+ question_patterns.any? { |pattern| input =~ pattern }
117
+ end
118
+ end
119
+
120
+ # Create custom calculator with direct response
121
+ class CustomCalculator < Legate::Agent
122
+ def run_task(session_id:, user_input:, session_service:, **_options)
123
+ # Simple calculation handling
124
+
125
+ # Strip non-math characters and evaluate
126
+ stripped_input = user_input.gsub(%r{[^0-9+\-*/().]}, '')
127
+ result = eval(stripped_input)
128
+ Legate::Event.new(role: :agent, content: { result: "The result is: #{result}" })
129
+ rescue StandardError
130
+ # If we can't evaluate, just echo back
131
+ Legate::Event.new(role: :agent, content: { result: "I couldn't calculate: #{user_input}" })
132
+ end
133
+ end
134
+
135
+ # Create custom researcher with direct response
136
+ class CustomResearcher < Legate::Agent
137
+ def run_task(session_id:, user_input:, session_service:, **_options)
138
+ # Extract the topic from questions like "what is X?"
139
+ topic = user_input.gsub(/what is|where is|who is|tell me about/i, '').strip
140
+
141
+ # For France questions specifically
142
+ return Legate::Event.new(role: :agent, content: { result: 'The capital of France is Paris.' }) if topic.downcase.include?('france') && user_input.downcase.include?('capital')
143
+
144
+ # Default research response
145
+ Legate::Event.new(role: :agent, content: { result: "Research information about: #{topic}" })
146
+ end
147
+ end
148
+
149
+ # Create the custom agents instead of standard ones
150
+ custom_coordinator = CustomCoordinator.new(definition: coordinator_agent)
151
+ custom_calculator = CustomCalculator.new(definition: calculator_agent)
152
+ custom_researcher = CustomResearcher.new(definition: researcher_agent)
153
+
154
+ # Establish parent-child relationships
155
+ custom_calculator.instance_variable_set(:@parent_agent, custom_coordinator)
156
+ custom_researcher.instance_variable_set(:@parent_agent, custom_coordinator)
157
+ custom_coordinator.instance_variable_set(:@sub_agents, [custom_calculator, custom_researcher])
158
+
159
+ puts '=' * 80
160
+ puts 'Multi-Agent Delegation Example'
161
+ puts '=' * 80
162
+ puts 'Available commands:'
163
+ puts "- Type math questions like: 'What is 125 * 45?' to use the Calculator agent"
164
+ puts "- Type research questions like: 'What is the capital of France?' to use the Researcher agent"
165
+ puts "- Type 'exit' to quit"
166
+ puts '=' * 80
167
+
168
+ # Main interaction loop
169
+ loop do
170
+ print "\nYour request > "
171
+ user_input = gets.chomp
172
+
173
+ break if user_input.downcase == 'exit'
174
+
175
+ puts "\nProcessing request..."
176
+
177
+ # Execute the custom coordinator agent with the user's input
178
+ result_event = custom_coordinator.run_task(
179
+ session_id: session_id,
180
+ user_input: user_input,
181
+ session_service: session_service
182
+ )
183
+
184
+ # Display the result
185
+ puts "\nResult:"
186
+ puts '-' * 50
187
+ puts result_event.content[:result]
188
+ puts '-' * 50
189
+ end
190
+
191
+ puts "\nThank you for using the Multi-Agent Delegation Example!"
@@ -0,0 +1,28 @@
1
+ # examples/advanced/mas/loop_workflow.rb
2
+ require_relative '../../../lib/legate'
3
+
4
+ # 1. Define Worker
5
+ Legate::Agent.define do |agent|
6
+ agent.name :improver
7
+ agent.instruction 'Improve the text quality.'
8
+ agent.use_tool :echo
9
+ # Logic to check quality would be here, setting :quality_met state
10
+ end
11
+
12
+ # 2. Define Loop Workflow
13
+ Legate::Agent.define do |agent|
14
+ agent.name :quality_control_loop
15
+ agent.agent_type :loop
16
+ agent.description 'Improve text until quality standard is met.'
17
+ agent.instruction 'Refine content.'
18
+
19
+ agent.loop_sub_agents :improver
20
+
21
+ # Stop when 'quality_met' is true in session state
22
+ agent.loop_condition :quality_met, true
23
+
24
+ # Safety valve: max 5 loops
25
+ agent.loop_max_iterations 5
26
+ end
27
+
28
+ puts 'Defined Loop Workflow: :quality_control_loop'
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legate
4
+ # A mock planner that returns pre-defined plans based on the input
5
+ class MockPlanner
6
+ attr_reader :agent, :logger, :model_name
7
+
8
+ def initialize(agent:, model_name: nil, **options)
9
+ @agent = agent
10
+ @logger = options[:logger] || Legate.logger
11
+ @model_name = model_name || 'mock-model'
12
+ end
13
+
14
+ def plan(user_input)
15
+ input_lower = user_input.downcase
16
+
17
+ if input_lower.match?(%r{[\d\s+\-*/()]+}) || input_lower.match?(/calculate|math|sum|multiply|divide|subtract|add/)
18
+ # Math question - delegate to calculator
19
+ return create_delegation_plan(:calculator_agent, user_input) if @agent.definition.delegation_targets&.include?(:calculator_agent)
20
+
21
+ create_calculator_plan(user_input)
22
+
23
+ elsif input_lower.match?(/who|what|where|when|why|how|explain|describe|tell me|history|science|geography|capital|country/)
24
+ # Knowledge question - delegate to researcher
25
+ return create_delegation_plan(:researcher_agent, user_input) if @agent.definition.delegation_targets&.include?(:researcher_agent)
26
+
27
+ create_echo_plan("I know the answer is: Test response about #{user_input}")
28
+
29
+ else
30
+ # Simple response
31
+ create_echo_plan("I'll help with: #{user_input}")
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def create_delegation_plan(target_agent, task)
38
+ {
39
+ thought_process: "This task should be delegated to the #{target_agent}",
40
+ steps: [
41
+ {
42
+ tool: :"agent_transfer_to_#{target_agent}",
43
+ params: { task: task }
44
+ }
45
+ ]
46
+ }
47
+ end
48
+
49
+ def create_calculator_plan(input)
50
+ # Extract numbers and operations
51
+ expression = input.gsub(%r{[^\d\s+\-*/().]+}, '')
52
+ expression = '2+2' if expression.empty?
53
+
54
+ {
55
+ thought_process: 'This appears to be a calculation request',
56
+ steps: [
57
+ {
58
+ tool: :calculate,
59
+ params: { expression: expression.strip }
60
+ }
61
+ ]
62
+ }
63
+ end
64
+
65
+ def create_echo_plan(message)
66
+ {
67
+ thought_process: 'Responding directly',
68
+ steps: [
69
+ {
70
+ tool: :echo,
71
+ params: { message: message }
72
+ }
73
+ ]
74
+ }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'legate'
6
+ require 'legate/planner'
7
+
8
+ # Set up the session service
9
+ Legate.config.session_service = Legate::SessionService::InMemory.new
10
+
11
+ # We'll create a simple MockPlanner that will be used to simulate LLM planning
12
+ # This planner will explicitly recognize delegation targets and route appropriately
13
+ class MockPlanner
14
+ attr_reader :agent, :logger
15
+
16
+ def initialize(agent:, **_options)
17
+ @agent = agent
18
+ @logger = Legate.logger
19
+ end
20
+
21
+ def plan(user_input)
22
+ if !@agent.definition.respond_to?(:delegation_targets) || @agent.definition.delegation_targets.empty?
23
+ # No delegation targets, just echo
24
+ return {
25
+ thought_process: "This agent doesn't have delegation targets, so I'll just echo the input.",
26
+ steps: [{ tool: :echo, params: { message: "Direct response: #{user_input}" } }]
27
+ }
28
+ end
29
+
30
+ # Check if we should delegate to calculator
31
+ if @agent.definition.delegation_targets.include?(:calculator_agent) &&
32
+ (user_input =~ %r{\d\s*[+\-*/]\s*\d} || user_input.downcase.include?('calculate') || user_input.downcase.include?('math'))
33
+
34
+ {
35
+ thought_process: 'This appears to be a math question, delegating to calculator_agent',
36
+ steps: [
37
+ {
38
+ tool: :agent_transfer_to_calculator_agent,
39
+ params: { task: user_input }
40
+ }
41
+ ]
42
+ }
43
+ # Check if we should delegate to researcher
44
+ elsif @agent.definition.delegation_targets.include?(:researcher_agent) &&
45
+ (user_input =~ /what\s+is|where\s+is|when\s+is|who\s+is|how\s+is|why\s+is|capital|country/)
46
+
47
+ {
48
+ thought_process: 'This appears to be a research question, delegating to researcher_agent',
49
+ steps: [
50
+ {
51
+ tool: :agent_transfer_to_researcher_agent,
52
+ params: { task: user_input }
53
+ }
54
+ ]
55
+ }
56
+ # Default to echoing
57
+ else
58
+ {
59
+ thought_process: "I don't see a need to delegate this, so I'll just echo it back.",
60
+ steps: [{ tool: :echo, params: { message: "Direct response: #{user_input}" } }]
61
+ }
62
+ end
63
+ end
64
+ end
65
+
66
+ # Register the echo tool
67
+ class EchoTool < Legate::Tool
68
+ def self.tool_metadata
69
+ {
70
+ name: :echo,
71
+ description: 'Echo a message back to the user',
72
+ parameters: {
73
+ message: {
74
+ type: 'string',
75
+ description: 'The message to echo back',
76
+ required: true
77
+ }
78
+ }
79
+ }
80
+ end
81
+
82
+ def call(params)
83
+ message = params[:message] || 'No message provided'
84
+ { result: message }
85
+ end
86
+ end
87
+
88
+ Legate::GlobalToolManager.register_tool(EchoTool)
89
+
90
+ # Define calculator tool
91
+ class CalculatorTool < Legate::Tool
92
+ def self.tool_metadata
93
+ {
94
+ name: :calculator,
95
+ description: 'Performs mathematical calculations',
96
+ parameters: {
97
+ expression: {
98
+ type: 'string',
99
+ description: 'Mathematical expression to evaluate',
100
+ required: true
101
+ }
102
+ }
103
+ }
104
+ end
105
+
106
+ def call(params)
107
+ result = eval(params[:expression].gsub(%r{[^0-9+\-*/().]}, ''))
108
+ { result: result }
109
+ rescue StandardError => e
110
+ { error: "Calculation error: #{e.message}" }
111
+ end
112
+ end
113
+
114
+ Legate::GlobalToolManager.register_tool(CalculatorTool)
115
+
116
+ # Define the agent definitions
117
+ calculator_agent = Legate::AgentDefinition.new.define do |a|
118
+ a.name :calculator_agent
119
+ a.description 'An agent specialized in mathematical calculations'
120
+ a.instruction 'You are a mathematical assistant. Solve math problems using the calculator tool.'
121
+ a.use_tool :calculator
122
+ a.use_tool :echo
123
+ end
124
+
125
+ researcher_agent = Legate::AgentDefinition.new.define do |a|
126
+ a.name :researcher_agent
127
+ a.description 'An agent specialized in answering research questions'
128
+ a.instruction 'You are a research assistant. Answer questions based on your knowledge.'
129
+ a.use_tool :echo
130
+ end
131
+
132
+ coordinator_agent = Legate::AgentDefinition.new.define do |a|
133
+ a.name :coordinator_agent
134
+ a.description 'An agent that coordinates tasks by delegating to specialized agents'
135
+ a.instruction <<~INSTRUCTION
136
+ You are a coordinator agent that delegates tasks to specialized agents.
137
+
138
+ You have access to these specialized agents:
139
+ 1. calculator_agent - For mathematical calculations
140
+ 2. researcher_agent - For answering general knowledge questions
141
+
142
+ Analyze each user request and delegate to the appropriate specialist agent.
143
+ INSTRUCTION
144
+ a.use_tool :echo
145
+ a.can_delegate_to :calculator_agent, :researcher_agent
146
+ end
147
+
148
+ # Register all agent definitions globally
149
+ Legate::GlobalDefinitionRegistry.register(calculator_agent)
150
+ Legate::GlobalDefinitionRegistry.register(researcher_agent)
151
+ Legate::GlobalDefinitionRegistry.register(coordinator_agent)
152
+
153
+ # Create a session for this interaction
154
+ session_service = Legate.config.session_service
155
+ session = session_service.create_session(
156
+ app_name: coordinator_agent.name,
157
+ user_id: 'delegation_example_user'
158
+ )
159
+ session_id = session.id
160
+
161
+ # Create and initialize the calculator agent
162
+ calculator = Legate::Agent.new(definition: calculator_agent)
163
+ calculator_planner = MockPlanner.new(agent: calculator)
164
+ def calculator.planner
165
+ @planner
166
+ end
167
+ calculator.instance_variable_set(:@planner, calculator_planner)
168
+
169
+ # Create and initialize the researcher agent
170
+ researcher = Legate::Agent.new(definition: researcher_agent)
171
+ researcher_planner = MockPlanner.new(agent: researcher)
172
+ def researcher.planner
173
+ @planner
174
+ end
175
+ researcher.instance_variable_set(:@planner, researcher_planner)
176
+
177
+ # Create and initialize the coordinator agent
178
+ coordinator = Legate::Agent.new(definition: coordinator_agent)
179
+ coordinator_planner = MockPlanner.new(agent: coordinator)
180
+ def coordinator.planner
181
+ @planner
182
+ end
183
+ coordinator.instance_variable_set(:@planner, coordinator_planner)
184
+
185
+ # Set up the parent-child relationships for agent hierarchy
186
+ calculator.instance_variable_set(:@parent_agent, coordinator)
187
+ researcher.instance_variable_set(:@parent_agent, coordinator)
188
+ coordinator.instance_variable_set(:@sub_agents, [calculator, researcher])
189
+
190
+ # Ensure all agents have their public_execute_step method for delegation
191
+ # This is done by the custom_agent_patch but let's make sure it's available
192
+
193
+ # Monkey patch the agent class to expose execute_step publicly (for testing only)
194
+ unless Legate::Agent.instance_methods.include?(:public_execute_step)
195
+ Legate::Agent.class_eval do
196
+ # Override execute_step to be public for testing
197
+ def public_execute_step(step, session, session_service)
198
+ # Handle special agent_transfer tool directly
199
+ if step[:tool].to_s.start_with?('agent_transfer_to_')
200
+ target_agent_name = step[:tool].to_s.sub('agent_transfer_to_', '').to_sym
201
+ task = step[:params][:task]
202
+
203
+ # Validate task parameter
204
+ unless task
205
+ return {
206
+ status: :error,
207
+ error_class: 'DelegationError',
208
+ error_message: "Missing 'task' parameter for delegation to '#{target_agent_name}'"
209
+ }
210
+ end
211
+
212
+ # Call transfer_to with the extracted target and task
213
+ return transfer_to(target_agent_name, task, session.id, session_service)
214
+ end
215
+
216
+ # For non-agent-transfer steps, use the standard execute_step
217
+ send(:execute_step, step, session, session_service)
218
+ end
219
+ end
220
+ end
221
+
222
+ # Start all agents
223
+ coordinator.start
224
+ calculator.start
225
+ researcher.start
226
+
227
+ puts '=' * 80
228
+ puts 'Multi-Agent Delegation Example (Using Proper Delegation)'
229
+ puts '=' * 80
230
+ puts 'Available commands:'
231
+ puts "- Type math questions like: 'What is 125 * 45?' to use the Calculator agent"
232
+ puts "- Type research questions like: 'What is the capital of France?' to use the Researcher agent"
233
+ puts "- Type 'exit' to quit"
234
+ puts '=' * 80
235
+
236
+ # Main interaction loop
237
+ loop do
238
+ print "\nYour request > "
239
+ user_input = gets.chomp
240
+
241
+ break if user_input.downcase == 'exit'
242
+
243
+ puts "\nProcessing request..."
244
+
245
+ # Execute the coordinator agent with the user's input
246
+ result_event = coordinator.run_task(
247
+ session_id: session_id,
248
+ user_input: user_input,
249
+ session_service: session_service
250
+ )
251
+
252
+ # Parse and display the result
253
+ content = result_event.content
254
+
255
+ puts "\nResult:"
256
+ puts '-' * 50
257
+
258
+ if content[:error]
259
+ puts "Error: #{content[:error]}"
260
+ elsif content[:error_message]
261
+ puts "Error: #{content[:error_message]}"
262
+ elsif content[:target_agent] # For delegation results
263
+ puts "Delegated to: #{content[:target_agent]}"
264
+ puts "Response: #{content[:result][:result]}"
265
+ else
266
+ puts "Response: #{content[:result]}"
267
+ end
268
+
269
+ puts '-' * 50
270
+ end
271
+
272
+ # Clean up
273
+ coordinator.stop
274
+ calculator.stop
275
+ researcher.stop
276
+ puts "\nThank you for using the Multi-Agent Delegation Example!"