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,140 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # If running from project root: bundle exec ruby examples/02_multi_tool_agent.rb
5
+ require_relative '../lib/legate'
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
+
11
+ puts '--- Multi-Tool Agent Example ---'
12
+
13
+ # 1. --- Agent Definitions ---
14
+
15
+ # Definition for the main multi-tool agent
16
+ multi_tool_agent_definition = Legate::AgentDefinition.new.define do |a|
17
+ a.name :multi_tool_agent
18
+ a.description 'An agent that can use multiple tools including echo, calculator, cat facts, the current time, and task delegation'
19
+ a.instruction 'You are a versatile assistant. Use the appropriate tool for the user\'s request. For calculations, use the calculator. For cat facts, use cat_facts. For the current date or time, use current_time. To delegate, use delegate_task.'
20
+ a.use_tool :echo
21
+ a.use_tool :calculator
22
+ a.use_tool :cat_facts # Provided by Legate::Tools::CatFacts
23
+ a.use_tool :current_time # Provided by Legate::Tools::CurrentTime
24
+ a.use_tool :delegate_task # Provided by Legate::Tools::AgentTool
25
+ end
26
+
27
+ # Definition for the calculator agent that will be used for delegation
28
+ calculator_agent_definition = Legate::AgentDefinition.new.define do |a|
29
+ a.name :calculator_agent
30
+ a.description 'A calculator agent that can perform basic arithmetic operations'
31
+ a.instruction 'You are a calculator. Perform the requested calculation.'
32
+ a.model_name 'gemini-3.5-flash' # Optional: specify model for this agent
33
+ a.use_tool :calculator
34
+ end
35
+
36
+ # --- Register Calculator Agent Definition for AgentTool --- >
37
+ # AgentTool will look up definitions in the GlobalDefinitionRegistry.
38
+ Legate::GlobalDefinitionRegistry.register(calculator_agent_definition)
39
+ puts "\nRegistered definition for '#{calculator_agent_definition.name}' in Legate::GlobalDefinitionRegistry."
40
+ # <----------------------------------------------------------
41
+
42
+ # Ensure all necessary tools are globally available/registered.
43
+ # Standard tools like Echo, Calculator, AgentTool (delegate_task) are usually auto-registered.
44
+ # For custom tools like CatFactsTool and RandomNumberTool, ensure they are loaded and registered
45
+ # e.g., via `require` and `Legate::GlobalToolManager.register_tool(CatFactsTool)` if not already.
46
+ # This example assumes they are available to the GlobalToolManager.
47
+
48
+ # 2. --- Agent Instantiation ---
49
+ agent = Legate::Agent.new(definition: multi_tool_agent_definition)
50
+
51
+ puts "\nAgent '#{agent.name}' created with tools:"
52
+ agent.tools.each { |tool| puts " - #{tool.name}" }
53
+
54
+ # 3. --- Start Agent ---
55
+ agent.start
56
+ puts "\nAgent started:"
57
+ puts " - #{agent.name}: Running: #{agent.running?}"
58
+
59
+ # 4. --- Session Setup ---
60
+ session_service = Legate::SessionService::InMemory.new
61
+ session = session_service.create_session(app_name: agent.name, user_id: 'example_user')
62
+ session_id = session.id
63
+ puts "\nCreated session: #{session_id}"
64
+
65
+ # 5. --- Task Execution Examples ---
66
+ tasks = [
67
+ 'Echo this message: Hello from multi-tool agent!',
68
+ 'Calculate 15 * 7',
69
+ 'Get me a cat fact',
70
+ 'What is the current date and time?',
71
+ 'Delegate this task to calculator_agent: what is 20 / 4'
72
+ ]
73
+
74
+ # Array to store results for summary table
75
+ task_results = []
76
+
77
+ tasks.each_with_index do |task, index|
78
+ task_num = index + 1
79
+ puts "\n" + '=' * 40
80
+ puts "--- Task #{task_num}: '#{task}' ---"
81
+ puts '=' * 40
82
+
83
+ outcome_message = '[Execution Error]'
84
+ begin
85
+ result_event = agent.run_task(
86
+ session_id: session_id,
87
+ user_input: task,
88
+ session_service: session_service
89
+ )
90
+
91
+ # --- Extract final outcome for summary --- #
92
+ if result_event.is_a?(Legate::Event) && result_event.content.is_a?(Hash)
93
+ content = result_event.content
94
+ if content[:status] == :success
95
+ final_result = content[:result]
96
+ # Handle nested result from delegation
97
+ outcome_message = if final_result.is_a?(Hash) && final_result.key?(:status) && final_result[:status] == :success
98
+ "Success: #{final_result[:result]}"
99
+ else
100
+ "Success: #{final_result}"
101
+ end
102
+ elsif content[:status] == :error
103
+ outcome_message = "Error: #{content[:error_message]}"
104
+ else # Pending or other statuses
105
+ outcome_message = "Status: #{content[:status]}"
106
+ outcome_message += " (#{content[:message]})" if content[:message]
107
+ end
108
+ else
109
+ outcome_message = "[Unexpected Result Format]: #{result_event.inspect}"
110
+ end
111
+ rescue StandardError => e
112
+ outcome_message = "[Execution Error]: #{e.message}"
113
+ puts "\nError executing Task #{task_num}: #{e.class} - #{e.message}"
114
+ puts e.backtrace.first(5).join("\n")
115
+ end
116
+ # Store the result
117
+ task_results << { task: task, outcome: outcome_message }
118
+ puts '=' * 40 # End separator for this task's logs
119
+ end
120
+
121
+ # 6. --- Print Summary Table --- #
122
+ puts "\n" + '#' * 50
123
+ puts '### Task Execution Summary ###'
124
+ puts '#' * 50
125
+ # Determine column width (simple approach)
126
+ max_task_len = task_results.map { |r| r[:task].length }.max || 40
127
+ puts "
128
+ | #{'Task'.ljust(max_task_len)} | Outcome |"
129
+ puts "|#{'-' * (max_task_len + 2)}|---------------------------------|"
130
+ task_results.each do |r|
131
+ puts "| #{r[:task].ljust(max_task_len)} | #{r[:outcome]} "
132
+ end
133
+ puts ''
134
+
135
+ # 7. --- Stop Agent --- #
136
+ agent.stop
137
+ puts "\nAgents stopped:"
138
+ puts " - #{agent.name}: Running: #{agent.running?}"
139
+
140
+ puts "\n--- Example Complete ---"
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Creating a Custom Tool
5
+ #
6
+ # This example shows how to build a custom tool from scratch using the
7
+ # Legate::Tool DSL: define parameters, implement perform_execution,
8
+ # and wire it into an agent.
9
+ #
10
+ # Run with: bundle exec ruby examples/03_custom_tool.rb
11
+
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
+ puts '--- Custom Tool Example ---'
19
+
20
+ # 1. Define a custom tool by inheriting from Legate::Tool
21
+ class GreetingTool < Legate::Tool
22
+ tool_description 'Generates a personalized greeting message.'
23
+
24
+ parameter :name, type: :string, required: true,
25
+ description: 'The name of the person to greet'
26
+
27
+ parameter :style, type: :string, required: false,
28
+ description: 'Greeting style: formal, casual, or pirate (default: casual)'
29
+
30
+ private
31
+
32
+ def perform_execution(params, _context)
33
+ name = params[:name]
34
+ style = (params[:style] || 'casual').downcase
35
+
36
+ greeting = case style
37
+ when 'formal'
38
+ "Good day, #{name}. It is a pleasure to make your acquaintance."
39
+ when 'pirate'
40
+ "Ahoy, #{name}! Welcome aboard, ye scurvy dog!"
41
+ else
42
+ "Hey #{name}, nice to meet you!"
43
+ end
44
+
45
+ { status: :success, result: greeting }
46
+ rescue StandardError => e
47
+ { status: :error, error_message: "Greeting failed: #{e.message}" }
48
+ end
49
+ end
50
+
51
+ # 2. Register the tool globally so agents can find it by name
52
+ Legate::GlobalToolManager.register_tool(GreetingTool)
53
+ puts "Registered tool: #{GreetingTool.tool_name}"
54
+
55
+ # 3. Use the tool directly (without an agent)
56
+ puts "\n--- Direct Tool Usage ---"
57
+ tool = GreetingTool.new
58
+ context = Legate::ToolContext.new(
59
+ session_id: 'demo-session',
60
+ user_id: 'demo-user',
61
+ app_name: 'custom_tool_example'
62
+ )
63
+
64
+ %w[casual formal pirate].each do |style|
65
+ result = tool.execute({ name: 'Alice', style: style }, context)
66
+ puts " #{style}: #{result[:result]}"
67
+ end
68
+
69
+ # 4. Wire the tool into an agent
70
+ puts "\n--- Agent with Custom Tool ---"
71
+ agent_definition = Legate::AgentDefinition.new.define do |a|
72
+ a.name :greeter_agent
73
+ a.description 'An agent that greets people in different styles'
74
+ a.instruction 'You are a greeting specialist. Use the greeting tool to greet people. Choose the style that best fits the request.'
75
+ a.use_tool :greeting_tool
76
+ end
77
+
78
+ agent = Legate::Agent.new(definition: agent_definition)
79
+ agent.start
80
+
81
+ session_service = Legate::SessionService::InMemory.new
82
+ session = session_service.create_session(app_name: agent.name, user_id: 'example_user')
83
+
84
+ result = agent.run_task(
85
+ session_id: session.id,
86
+ user_input: 'Greet Captain Blackbeard in pirate style',
87
+ session_service: session_service
88
+ )
89
+
90
+ puts "Agent result: #{result.content.inspect}"
91
+
92
+ agent.stop
93
+ puts "\n--- Example Complete ---"
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Agent Instructions (System Prompts)
5
+ #
6
+ # Demonstrates defining an agent with specific instructions (system prompt)
7
+ # that guide its behavior during planning.
8
+ #
9
+ # Run with: bundle exec ruby examples/04_agent_instructions.rb
10
+
11
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
12
+
13
+ require 'legate'
14
+
15
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
16
+ # The library never reads .env on its own; an application must opt in.
17
+ Legate.load_environment
18
+ require 'legate/tools/echo' # Ensure tool classes are loaded for GlobalToolManager
19
+ require 'legate/tools/calculator' # to find them by name.
20
+
21
+ # 1. Define the Agent with Instructions
22
+ # =====================================
23
+
24
+ # Create the AgentDefinition object first
25
+ concise_calculator_definition = Legate::AgentDefinition.new.define do |a|
26
+ a.name :concise_calculator # Use symbol for name
27
+ a.description 'A calculator agent that tries to be concise.'
28
+ # Provide instructions to the planner
29
+ a.instruction 'You are a calculator assistant. When asked to calculate, use the calculator tool. Respond only with the final numerical result, no extra words.'
30
+ # Add necessary tools by name
31
+ a.use_tool :calculator
32
+ a.use_tool :echo # Echo might be used by fallback or if planner chooses it
33
+ end
34
+
35
+ # Instantiate the Agent using the definition
36
+ agent = Legate::Agent.new(definition: concise_calculator_definition)
37
+
38
+ # 2. Setup Session Service
39
+ # ========================
40
+ session_service = Legate::SessionService::InMemory.new
41
+ session = session_service.create_session(app_name: agent.name, user_id: 'instruct_user')
42
+
43
+ # 3. Start Agent and Run Task
44
+ # ==========================
45
+ begin
46
+ puts "Starting agent '#{agent.name}'..."
47
+ agent.start
48
+
49
+ task = 'What is 5 plus 12?'
50
+ puts "\nRunning task: '#{task}'"
51
+
52
+ # The planner will receive the instruction along with the task and tool list.
53
+ # The LLM should ideally follow the instruction to generate a plan
54
+ # that results in a concise response (though the final response formatting
55
+ # also depends on the tool's output and agent logic).
56
+ result_event = agent.run_task(
57
+ session_id: session.id,
58
+ user_input: task,
59
+ session_service: session_service
60
+ )
61
+
62
+ puts "\n--- Agent Response ---"
63
+ puts " Role: #{result_event.role}"
64
+ puts " Content: #{result_event.content.inspect}"
65
+
66
+ # Example of a task the instructions might prevent (if LLM obeys)
67
+ task_inappropriate = 'Tell me a long story about the number 7.'
68
+ puts "\nRunning task: '#{task_inappropriate}' (Agent instructed to only calculate)"
69
+ result_event_2 = agent.run_task(
70
+ session_id: session.id,
71
+ user_input: task_inappropriate,
72
+ session_service: session_service
73
+ )
74
+ puts "\n--- Agent Response (Second Task) ---"
75
+ puts " Role: #{result_event_2.role}"
76
+ puts " Content: #{result_event_2.content.inspect}"
77
+ # Expected: Agent might respond with planning failure because the task doesn't match its instructions.
78
+ ensure
79
+ puts "\nStopping agent '#{agent.name}'..."
80
+ agent.stop
81
+ puts 'Agent stopped.'
82
+ end
83
+
84
+ puts "\nExample finished."
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Session State Management
5
+ #
6
+ # This example demonstrates how Legate manages state:
7
+ # - Creating sessions with initial state
8
+ # - Reading and writing state via the session service
9
+ # - How tools produce state deltas that agents apply
10
+ # - Inspecting event history after task execution
11
+ #
12
+ # Run with: bundle exec ruby examples/05_state_and_sessions.rb
13
+
14
+ require_relative '../lib/legate'
15
+
16
+ # Load .env and map GEMINI_API_KEY -> GOOGLE_API_KEY (as the `legate` CLI does).
17
+ # The library never reads .env on its own; an application must opt in.
18
+ Legate.load_environment
19
+
20
+ puts '--- State & Sessions Example ---'
21
+
22
+ # 1. Create a session service and session with initial state
23
+ session_service = Legate::SessionService::InMemory.new
24
+ session = session_service.create_session(
25
+ app_name: 'state_example',
26
+ user_id: 'demo_user',
27
+ initial_state: { visits: 0, greeting: 'Welcome!' }
28
+ )
29
+ session_id = session.id
30
+
31
+ puts "Created session: #{session_id}"
32
+ puts 'Initial state:'
33
+ session.state.each_pair { |k, v| puts " #{k}: #{v.inspect}" }
34
+
35
+ # 2. Direct state manipulation via session service
36
+ puts "\n--- Session Service State Operations ---"
37
+ session_service.set_state(session_id: session_id, key: :visits, value: 1)
38
+ session_service.set_state(session_id: session_id, key: :preference, value: 'dark_mode')
39
+
40
+ visits = session_service.get_state(session_id: session_id, key: :visits)
41
+ pref = session_service.get_state(session_id: session_id, key: :preference)
42
+ puts " visits: #{visits}"
43
+ puts " preference: #{pref}"
44
+
45
+ # 3. How tools interact with state
46
+ puts "\n--- Tool State Pattern ---"
47
+ puts ' Tools use context.state_get/state_set to read/write state.'
48
+ puts " state_set accumulates a 'pending delta' that the agent applies"
49
+ puts ' to the session after the tool completes. This ensures state'
50
+ puts ' changes are tracked alongside events.'
51
+
52
+ # 4. Create an agent and run a task to see events and state in action
53
+ puts "\n--- Agent Task with Events ---"
54
+ agent_definition = Legate::AgentDefinition.new.define do |a|
55
+ a.name :state_demo_agent
56
+ a.description 'Demonstrates state in agent context'
57
+ a.instruction 'You are a helpful agent. Echo back what the user says.'
58
+ a.use_tool :echo
59
+ end
60
+
61
+ agent = Legate::Agent.new(definition: agent_definition)
62
+ agent.start
63
+
64
+ result = agent.run_task(
65
+ session_id: session_id,
66
+ user_input: 'Remember that I visited the examples page',
67
+ session_service: session_service
68
+ )
69
+
70
+ puts " Task result: #{result.content.inspect}"
71
+
72
+ # 5. Inspect event history
73
+ puts "\n--- Event History ---"
74
+ final_session = session_service.get_session(session_id: session_id)
75
+ puts " Total events: #{final_session.events.size}"
76
+ final_session.events.each_with_index do |event, i|
77
+ role = event.role
78
+ content_preview = case event.content
79
+ when String then event.content[0..60]
80
+ when Hash then event.content[:status] || event.content.keys.first
81
+ else event.content.class
82
+ end
83
+ puts " #{i + 1}. [#{role}] #{content_preview}"
84
+ end
85
+
86
+ # 6. Final state
87
+ puts "\n--- Final State ---"
88
+ final_session.state.each_pair { |k, v| puts " #{k}: #{v.inspect}" }
89
+
90
+ agent.stop
91
+ puts "\n--- Example Complete ---"
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # If running from project root: bundle exec ruby examples/06_callbacks.rb
5
+ require_relative '../lib/legate'
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
+
11
+ puts '--- Legate Callbacks Basic Example ---'
12
+
13
+ # Helper method to safely inspect objects
14
+ def inspect_object(obj)
15
+ return 'nil' if obj.nil?
16
+
17
+ if obj.is_a?(Hash) || obj.is_a?(Array)
18
+ obj.inspect
19
+ else
20
+ # For other objects, get their instance variables and methods
21
+ vars = obj.instance_variables.map { |v| "#{v}=#{obj.instance_variable_get(v).inspect}" }
22
+ methods = obj.public_methods(false).sort.map(&:to_s)
23
+ "Object of class #{obj.class}\nInstance vars: #{vars.join(', ')}\nMethods: #{methods.join(', ')}"
24
+ end
25
+ end
26
+
27
+ # 1. --- Define our callback functions ---
28
+
29
+ # Agent callbacks
30
+ before_agent_callback = lambda do |context|
31
+ puts "\n[AGENT CALLBACK] Before agent processes request"
32
+ puts " Context structure: #{inspect_object(context)}"
33
+ puts " Session ID: #{context.session_id}" if context.respond_to?(:session_id)
34
+ end
35
+
36
+ # The documentation mentioned only 1 parameter, but the error suggests 2 are passed
37
+ after_agent_callback = lambda do |context, *args|
38
+ puts "\n[AGENT CALLBACK] After agent completed request"
39
+ puts " Context structure: #{inspect_object(context)}"
40
+ puts " Additional args: #{args.inspect}" if args.any?
41
+ end
42
+
43
+ # Model callbacks - expecting 2 arguments
44
+ before_model_callback = lambda do |context, prompt|
45
+ puts "\n[MODEL CALLBACK] Before sending prompt to LLM"
46
+ puts " Context structure: #{inspect_object(context)}"
47
+
48
+ if prompt.is_a?(String)
49
+ preview = prompt.length > 100 ? "#{prompt[0, 100]}..." : prompt
50
+ puts " Prompt: #{preview}"
51
+ else
52
+ puts " Prompt: #{prompt.inspect}"
53
+ end
54
+
55
+ # Return the prompt unchanged
56
+ prompt
57
+ end
58
+
59
+ after_model_callback = lambda do |context, response|
60
+ puts "\n[MODEL CALLBACK] After receiving response from LLM"
61
+ puts " Context structure: #{inspect_object(context)}"
62
+
63
+ if response.is_a?(String)
64
+ preview = response.length > 100 ? "#{response[0, 100]}..." : response
65
+ puts " Response: #{preview}"
66
+ else
67
+ puts " Response: #{response.inspect}"
68
+ end
69
+
70
+ # Return the response unchanged
71
+ response
72
+ end
73
+
74
+ # Tool callbacks - expecting 3 arguments
75
+ before_tool_callback = lambda do |context, tool, params|
76
+ puts "\n[TOOL CALLBACK] Before executing tool"
77
+ puts " Context structure: #{inspect_object(context)}"
78
+ puts " Tool: #{tool.name}" if tool.respond_to?(:name)
79
+ puts " Parameters: #{params.inspect}"
80
+
81
+ # Return the original parameters, not the context or tool
82
+ # The expected parameters format is a Hash like {message: "Hello, world"}
83
+ if params.is_a?(Hash)
84
+ params
85
+ elsif params.is_a?(Legate::ToolContext)
86
+ # If we're passed a ToolContext object, try to extract parameters from session
87
+ puts ' WARNING: Received ToolContext instead of params hash'
88
+
89
+ # Get parameters from the session events if possible
90
+ if params.respond_to?(:session_id) && params.session_id && defined?($session_service)
91
+ begin
92
+ session = $session_service.get_session(session_id: params.session_id)
93
+ if session
94
+ events = session.events
95
+ # Find the most recent tool_request event for this tool
96
+ tool_name = tool.respond_to?(:name) ? tool.name.to_s : nil
97
+ if tool_name
98
+ # The tool field might be stored in different ways
99
+ tool_request = events.reverse.find do |e|
100
+ e.role == :tool_request &&
101
+ ((e.respond_to?(:tool) && e.tool == tool_name) ||
102
+ (e.respond_to?(:[]) && e[:tool] == tool_name))
103
+ end
104
+
105
+ if tool_request && tool_request.respond_to?(:content) && tool_request.content.is_a?(Hash)
106
+ # We found the parameters in the session events
107
+ return tool_request.content
108
+ end
109
+ end
110
+ end
111
+ rescue StandardError => e
112
+ puts " WARNING: Error getting session events: #{e.message}"
113
+ end
114
+ end
115
+
116
+ # Default fallback for echo tool
117
+ { message: 'Hello, world! This is a callback example.' }
118
+ else
119
+ # If all else fails, return empty params
120
+ puts " WARNING: Unexpected params type: #{params.class}"
121
+ {}
122
+ end
123
+ end
124
+
125
+ after_tool_callback = lambda do |context, tool, result|
126
+ puts "\n[TOOL CALLBACK] After executing tool"
127
+ puts " Context structure: #{inspect_object(context)}"
128
+ puts " Tool: #{tool.name}" if tool.respond_to?(:name)
129
+ puts " Result: #{result.inspect}"
130
+
131
+ # Return the result unchanged
132
+ result
133
+ end
134
+
135
+ # 2. --- Agent Definition with Callbacks ---
136
+ echo_agent_definition = Legate::AgentDefinition.new.define do |a|
137
+ a.name :echo_with_callbacks
138
+ a.description 'An echo agent that demonstrates callbacks'
139
+ a.instruction 'You are an echo agent. Your task is to repeat the user\'s input exactly.'
140
+
141
+ # Register the callbacks
142
+ a.before_agent_callback(&before_agent_callback)
143
+ a.after_agent_callback(&after_agent_callback)
144
+
145
+ a.before_model_callback(&before_model_callback)
146
+ a.after_model_callback(&after_model_callback)
147
+
148
+ a.before_tool_callback(&before_tool_callback)
149
+ a.after_tool_callback(&after_tool_callback)
150
+
151
+ a.use_tool :echo
152
+ end
153
+
154
+ # 3. --- Agent Instantiation ---
155
+ agent = Legate::Agent.new(definition: echo_agent_definition)
156
+ puts "\nAgent '#{agent.name}' created with callbacks registered"
157
+
158
+ # 4. --- Start Agent and Setup Session ---
159
+ agent.start
160
+ session_service = Legate::SessionService::InMemory.new
161
+ # Make session_service accessible to callbacks
162
+ $session_service = session_service
163
+ session = session_service.create_session(app_name: agent.name, user_id: 'callback_example_user')
164
+ session_id = session.id
165
+ puts "\nCreated session: #{session_id}"
166
+
167
+ # 5. --- Execute Task and Observe Callbacks ---
168
+ task = 'Hello, world! This is a callback example.'
169
+ puts "\nExecuting task: '#{task}'"
170
+
171
+ begin
172
+ result = agent.run_task(
173
+ session_id: session_id,
174
+ user_input: task,
175
+ session_service: session_service
176
+ )
177
+
178
+ puts "\nFinal result: #{result.content.inspect}"
179
+ rescue StandardError => e
180
+ puts "\nError executing task: #{e.class} - #{e.message}"
181
+ puts e.backtrace.first(5).join("\n")
182
+ end
183
+
184
+ # 6. --- Stop Agent ---
185
+ agent.stop
186
+ puts "\n--- Example Complete ---"