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,656 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example of a Parallel Travel Planning Process
5
+ # Run with: bundle exec ruby examples/advanced/workflows/travel_planner_parallel.rb
6
+ #
7
+ # This example demonstrates how to create a parallel agent pattern where multiple specialized
8
+ # agents are executed concurrently, with each agent handling a specific part of a travel planning task.
9
+ #
10
+ # Key components of this example:
11
+ # 1. Multiple specialized agents: destination_research, flight_search, accommodation_search,
12
+ # attractions_research, and weather_forecast
13
+ # 2. A parent parallel agent that orchestrates the concurrent execution of the specialized agents
14
+ # 3. Each agent has its own output_key to store its results in the shared session state
15
+ # 4. The ParallelAgent class automatically handles the execution and waits for all agents to complete
16
+ # 5. A final summary agent that compiles all the parallel results
17
+ #
18
+ # The architecture follows these principles:
19
+ # - Each agent is fully specialized for its task
20
+ # - Agents share the same session, allowing them to access each other's outputs
21
+ # - The parent-child relationship is explicitly established through the sub_agents parameter
22
+ # - The execution happens concurrently, significantly reducing the total time needed
23
+
24
+ require_relative '../../../lib/legate'
25
+ require_relative '../../../lib/legate/agents/parallel_agent' # Required for ParallelAgent
26
+ require_relative '../../../lib/legate/agents/sequential_agent' # Required for the final SequentialAgent
27
+
28
+ # Check if tty-spinner is installed
29
+ begin
30
+ require 'tty-spinner'
31
+ rescue LoadError
32
+ puts 'The tty-spinner gem is required for this example.'
33
+ puts 'Please install it with: gem install tty-spinner'
34
+ puts 'Or add it to your Gemfile and run: bundle install'
35
+ exit 1
36
+ end
37
+
38
+ # Clear any existing registrations to avoid conflicts
39
+ Legate::GlobalDefinitionRegistry.instance_variable_set(:@definitions, {})
40
+
41
+ puts '=== Travel Planner Parallel Process Example ==='
42
+ puts 'This example demonstrates multiple specialized agents running in parallel to plan a trip'
43
+ puts 'The ParallelAgent class automatically handles the concurrent execution of all agents.'
44
+
45
+ # Ensure the echo tool is registered - all agents will use this
46
+ Legate::GlobalToolManager.register_tool(Legate::Tools::Echo) unless Legate::GlobalToolManager.registered_tool_names.include?(:echo)
47
+
48
+ # Ensure delegate_task tool is registered for agent delegation
49
+ Legate::GlobalToolManager.register_tool(Legate::Tools::AgentTool) unless Legate::GlobalToolManager.registered_tool_names.include?(:delegate_task)
50
+
51
+ # ----- Define Specialized Parallel Agents -----
52
+
53
+ # 1. Destination Research Agent
54
+ destination_agent_def = Legate::AgentDefinition.new.define do |a|
55
+ a.name :destination_research
56
+ a.description 'Researches and suggests destination options based on user preferences'
57
+ a.instruction <<~INSTRUCTION
58
+ You are a destination research specialist. Your task is to analyze the user's preferences and suggest 2-3 suitable destinations.
59
+
60
+ First, think about what destinations would be appropriate based on the user's preferences. Then, use the 'echo' tool to output your response.
61
+
62
+ Your echo response should follow this EXACT format:
63
+
64
+ # DESTINATION RECOMMENDATIONS
65
+
66
+ Based on your preferences, here are the destinations I recommend:
67
+
68
+ ## [Destination 1 Name]
69
+ - **Location**: [Region/Country]
70
+ - **Weather**: [Weather description for the time period]
71
+ - **Highlights**: [3-4 key attractions or experiences]
72
+ - **Best For**: [What makes this destination perfect for the user]
73
+
74
+ ## [Destination 2 Name]
75
+ - **Location**: [Region/Country]
76
+ - **Weather**: [Weather description for the time period]
77
+ - **Highlights**: [3-4 key attractions or experiences]
78
+ - **Best For**: [What makes this destination perfect for the user]
79
+
80
+ ## [Destination 3 Name] (optional)
81
+ - **Location**: [Region/Country]
82
+ - **Weather**: [Weather description for the time period]
83
+ - **Highlights**: [3-4 key attractions or experiences]
84
+ - **Best For**: [What makes this destination perfect for the user]
85
+
86
+ # RECOMMENDATION SUMMARY
87
+ [Brief explanation of why these destinations match the user's preferences]
88
+ INSTRUCTION
89
+ a.model_name 'gemini-3.5-flash'
90
+ a.output_key :destination_results
91
+ a.use_tool :echo
92
+ end
93
+
94
+ # 2. Flight Search Agent
95
+ flight_agent_def = Legate::AgentDefinition.new.define do |a|
96
+ a.name :flight_search
97
+ a.description 'Searches for flight options based on user preferences'
98
+ a.instruction <<~INSTRUCTION
99
+ You are a flight search specialist. Your task is to provide flight options based on the user's travel preferences.
100
+
101
+ First, analyze the user's request to understand their travel needs. Consider their preferences for timing, budget level, and potential destinations.
102
+ Then, use the 'echo' tool to output your response with simulated flight options.
103
+
104
+ Your echo response should follow this EXACT format:
105
+
106
+ # FLIGHT OPTIONS
107
+
108
+ Based on your travel preferences, here are some flight options:
109
+
110
+ ## Option 1: [Origin] to [Destination]
111
+ - **Airline**: [Airline name]
112
+ - **Dates**: Depart [date] / Return [date]
113
+ - **Price**: $[amount] USD (round-trip)
114
+ - **Duration**: [hours] hrs each way
115
+ - **Stops**: [Direct/1 stop in X/etc.]
116
+ - **Notes**: [Any special notes about this option]
117
+
118
+ ## Option 2: [Origin] to [Destination]
119
+ - **Airline**: [Airline name]
120
+ - **Dates**: Depart [date] / Return [date]
121
+ - **Price**: $[amount] USD (round-trip)
122
+ - **Duration**: [hours] hrs each way
123
+ - **Stops**: [Direct/1 stop in X/etc.]
124
+ - **Notes**: [Any special notes about this option]
125
+
126
+ ## Option 3: [Origin] to [Destination] (optional)
127
+ - **Airline**: [Airline name]
128
+ - **Dates**: Depart [date] / Return [date]
129
+ - **Price**: $[amount] USD (round-trip)
130
+ - **Duration**: [hours] hrs each way
131
+ - **Stops**: [Direct/1 stop in X/etc.]
132
+ - **Notes**: [Any special notes about this option]
133
+
134
+ # FLIGHT SEARCH NOTES
135
+ - Prices are estimates and subject to change
136
+ - Consider booking within the next [timeframe] for best rates
137
+ - [Any other relevant booking tips]
138
+ INSTRUCTION
139
+ a.model_name 'gemini-3.5-flash'
140
+ a.output_key :flight_results
141
+ a.use_tool :echo
142
+ end
143
+
144
+ # 3. Accommodation Search Agent
145
+ accommodation_agent_def = Legate::AgentDefinition.new.define do |a|
146
+ a.name :accommodation_search
147
+ a.description 'Searches for accommodation options based on user preferences'
148
+ a.instruction <<~INSTRUCTION
149
+ You are an accommodation search specialist. Your task is to provide lodging options based on the user's travel preferences.
150
+
151
+ First, analyze the user's request to understand their accommodation needs. Consider their preferences for comfort level, budget, and potential destinations.
152
+ Then, use the 'echo' tool to output your response with simulated accommodation options.
153
+
154
+ Your echo response should follow this EXACT format:
155
+
156
+ # ACCOMMODATION OPTIONS
157
+
158
+ Based on your preferences, here are some accommodation recommendations:
159
+
160
+ ## Option 1: [Accommodation Name]
161
+ - **Type**: [Hotel/Resort/Airbnb/etc.]
162
+ - **Location**: [Area/Neighborhood in destination]
163
+ - **Price Range**: $[amount]-$[amount] USD per night
164
+ - **Rating**: [4.5/5 stars, etc.]
165
+ - **Amenities**: [List of key amenities]
166
+ - **Highlights**: [What makes this place special]
167
+
168
+ ## Option 2: [Accommodation Name]
169
+ - **Type**: [Hotel/Resort/Airbnb/etc.]
170
+ - **Location**: [Area/Neighborhood in destination]
171
+ - **Price Range**: $[amount]-$[amount] USD per night
172
+ - **Rating**: [4.5/5 stars, etc.]
173
+ - **Amenities**: [List of key amenities]
174
+ - **Highlights**: [What makes this place special]
175
+
176
+ ## Option 3: [Accommodation Name]
177
+ - **Type**: [Hotel/Resort/Airbnb/etc.]
178
+ - **Location**: [Area/Neighborhood in destination]
179
+ - **Price Range**: $[amount]-$[amount] USD per night
180
+ - **Rating**: [4.5/5 stars, etc.]
181
+ - **Amenities**: [List of key amenities]
182
+ - **Highlights**: [What makes this place special]
183
+
184
+ # BOOKING TIPS
185
+ - [Tip about when to book]
186
+ - [Tip about special deals or considerations]
187
+ - [Any other relevant accommodation advice]
188
+ INSTRUCTION
189
+ a.model_name 'gemini-3.5-flash'
190
+ a.output_key :accommodation_results
191
+ a.use_tool :echo
192
+ end
193
+
194
+ # 4. Local Attractions Agent
195
+ attractions_agent_def = Legate::AgentDefinition.new.define do |a|
196
+ a.name :attractions_research
197
+ a.description 'Researches local attractions and activities'
198
+ a.instruction <<~INSTRUCTION
199
+ You are a local attractions specialist. Your task is to research and suggest activities and attractions based on the user's travel preferences.
200
+
201
+ First, analyze the user's request to understand their interests and preferences. Consider the types of activities they might enjoy based on their stated preferences.
202
+ Then, use the 'echo' tool to output your response with suggested attractions and activities.
203
+
204
+ Your echo response should follow this EXACT format:
205
+
206
+ # LOCAL ATTRACTIONS & ACTIVITIES
207
+
208
+ Based on your interests, here are recommended attractions and activities:
209
+
210
+ ## Cultural Experiences
211
+ 1. **[Attraction Name]**: [Brief description] - [Approximate cost if applicable]
212
+ 2. **[Attraction Name]**: [Brief description] - [Approximate cost if applicable]
213
+ 3. **[Attraction Name]**: [Brief description] - [Approximate cost if applicable]
214
+
215
+ ## Nature & Outdoors
216
+ 1. **[Attraction/Activity Name]**: [Brief description] - [Approximate cost if applicable]
217
+ 2. **[Attraction/Activity Name]**: [Brief description] - [Approximate cost if applicable]
218
+ 3. **[Attraction/Activity Name]**: [Brief description] - [Approximate cost if applicable]
219
+
220
+ ## Food & Dining
221
+ 1. **[Restaurant/Food Experience]**: [Brief description] - [Price range]
222
+ 2. **[Restaurant/Food Experience]**: [Brief description] - [Price range]
223
+ 3. **[Restaurant/Food Experience]**: [Brief description] - [Price range]
224
+
225
+ ## Off the Beaten Path
226
+ 1. **[Unique Experience]**: [Brief description] - [Approximate cost if applicable]
227
+ 2. **[Unique Experience]**: [Brief description] - [Approximate cost if applicable]
228
+
229
+ # INSIDER TIPS
230
+ - [Local tip about best times to visit certain attractions]
231
+ - [Local tip about transportation or getting around]
232
+ - [Local tip about cultural norms or special considerations]
233
+ INSTRUCTION
234
+ a.model_name 'gemini-3.5-flash'
235
+ a.output_key :attractions_results
236
+ a.use_tool :echo
237
+ end
238
+
239
+ # 5. Weather Forecast Agent
240
+ weather_agent_def = Legate::AgentDefinition.new.define do |a|
241
+ a.name :weather_forecast
242
+ a.description 'Provides weather forecast information for potential destinations'
243
+ a.instruction <<~INSTRUCTION
244
+ You are a weather forecast specialist. Your task is to provide weather information for potential travel destinations based on the user's travel timeframe.
245
+
246
+ First, analyze the user's request to understand their travel timeframe and potential destinations. Consider typical weather patterns for early June in destinations that match their preferences.
247
+ Then, use the 'echo' tool to output your response with simulated weather forecasts.
248
+
249
+ Your echo response should follow this EXACT format:
250
+
251
+ # WEATHER FORECAST
252
+
253
+ Based on historical data, here's what you can expect for weather in early June:
254
+
255
+ ## Destination Region 1
256
+ - **Temperature Range**: [Low°F/°C] to [High°F/°C]
257
+ - **Precipitation**: [Likelihood and type of precipitation]
258
+ - **Humidity**: [Humidity level]
259
+ - **Conditions**: [General conditions - sunny, partly cloudy, etc.]
260
+ - **Recommendation**: [Weather-appropriate clothing/activities]
261
+
262
+ ## Destination Region 2
263
+ - **Temperature Range**: [Low°F/°C] to [High°F/°C]
264
+ - **Precipitation**: [Likelihood and type of precipitation]
265
+ - **Humidity**: [Humidity level]
266
+ - **Conditions**: [General conditions - sunny, partly cloudy, etc.]
267
+ - **Recommendation**: [Weather-appropriate clothing/activities]
268
+
269
+ ## Destination Region 3
270
+ - **Temperature Range**: [Low°F/°C] to [High°F/°C]
271
+ - **Precipitation**: [Likelihood and type of precipitation]
272
+ - **Humidity**: [Humidity level]
273
+ - **Conditions**: [General conditions - sunny, partly cloudy, etc.]
274
+ - **Recommendation**: [Weather-appropriate clothing/activities]
275
+
276
+ # CLIMATE NOTES
277
+ - [Note about weather patterns and what to expect]
278
+ - [Note about any seasonal weather considerations]
279
+ - [Any other relevant weather tips]
280
+ INSTRUCTION
281
+ a.model_name 'gemini-3.5-flash'
282
+ a.output_key :weather_results
283
+ a.use_tool :echo
284
+ end
285
+
286
+ # 6. Trip Summarizer Agent (This will run after the parallel agents)
287
+ summary_agent_def = Legate::AgentDefinition.new.define do |a|
288
+ a.name :trip_summarizer
289
+ a.description 'Creates a comprehensive trip summary from all parallel research results'
290
+ a.instruction <<~INSTRUCTION
291
+ You are a travel summary specialist. Create a concise, well-formatted trip summary that brings together all the information from the parallel research tasks.
292
+
293
+ IMPORTANT: You MUST use the actual results from the previous parallel agents stored in the session state. Look for:
294
+ - destination_results - Contains destination recommendations
295
+ - flight_results - Contains flight options
296
+ - accommodation_results - Contains accommodation options
297
+ - attractions_results - Contains local attractions and activities
298
+ - weather_results - Contains weather forecasts
299
+
300
+ DO NOT return a template. Instead, compile a specific travel plan using the ACTUAL data provided by the parallel agents.
301
+
302
+ First, analyze all of the parallel agent results to identify the most promising destination. Then, create a comprehensive#{' '}
303
+ travel plan for that destination including transportation, accommodation, activities, and budget.
304
+
305
+ Your echo response should follow this format:
306
+
307
+ # COMPREHENSIVE TRAVEL PLAN FOR [DESTINATION]
308
+
309
+ ## Selected Destination
310
+ [Provide a brief overview of the chosen destination and why it's ideal based on the user's preferences]
311
+
312
+ ## Travel Details
313
+ - **Dates**: [Specific dates in early June]
314
+ - **Transportation**: [Best flight option from the flight search results]
315
+ - **Accommodation**: [Best accommodation option from the results]
316
+ - **Weather**: [Expected weather conditions and packing suggestions]
317
+
318
+ ## Recommended Itinerary
319
+
320
+ **Day 1**
321
+ - Morning: [Specific activity from attractions list]
322
+ - Afternoon: [Specific activity from attractions list]
323
+ - Evening: [Specific dining or entertainment option]
324
+
325
+ **Day 2**
326
+ - Morning: [Specific activity from attractions list]
327
+ - Afternoon: [Specific activity from attractions list]
328
+ - Evening: [Specific dining or entertainment option]
329
+
330
+ **Day 3**
331
+ - Morning: [Specific activity from attractions list]
332
+ - Afternoon: [Specific activity from attractions list]
333
+ - Evening: [Specific dining or entertainment option]
334
+
335
+ ## Budget Overview
336
+ - **Transportation**: Approximately $[amount] USD
337
+ - **Accommodation**: Approximately $[amount] USD ([number] nights)
338
+ - **Activities**: Approximately $[amount] USD
339
+ - **Food & Other**: Approximately $[amount] USD
340
+ - **Total Estimated Cost**: $[amount] USD
341
+
342
+ ## Travel Tips
343
+ - [Specific tip related to the destination]
344
+ - [Specific tip about packing or preparation]
345
+ - [Specific advice based on weather or local customs]
346
+
347
+ Remember, use actual information from the parallel agents' results. Do not return a template with placeholders.
348
+ INSTRUCTION
349
+ a.model_name 'gemini-3.5-flash'
350
+ a.output_key :trip_summary
351
+ a.use_tool :echo
352
+ end
353
+
354
+ # 7. Parent Parallel Agent Definition
355
+ travel_planner_def = Legate::AgentDefinition.new.define do |a|
356
+ a.name :travel_planner
357
+ a.description 'Orchestrates the parallel travel planning process'
358
+ a.instruction <<~INSTRUCTION
359
+ You are a travel planning coordinator that will oversee the parallel process of planning a trip.
360
+
361
+ You will delegate specific tasks to specialized sub-agents that will run in parallel:
362
+ - Destination Research - Research and suggest suitable destinations
363
+ - Flight Search - Find appropriate flight options
364
+ - Accommodation Search - Find suitable places to stay
365
+ - Attractions Research - Identify interesting activities and sights
366
+ - Weather Forecast - Provide weather information for the travel period
367
+
368
+ Your job is to coordinate this process and ensure all agents have the information they need.
369
+ INSTRUCTION
370
+ a.model_name 'gemini-3.5-flash'
371
+ a.output_key :parallel_research_results
372
+ a.agent_type :parallel # Important! This tells Legate to use ParallelAgent
373
+ a.parallel_sub_agents %i[destination_research flight_search accommodation_search attractions_research weather_forecast]
374
+ end
375
+
376
+ # Register all agents globally
377
+ Legate::GlobalDefinitionRegistry.register(destination_agent_def)
378
+ Legate::GlobalDefinitionRegistry.register(flight_agent_def)
379
+ Legate::GlobalDefinitionRegistry.register(accommodation_agent_def)
380
+ Legate::GlobalDefinitionRegistry.register(attractions_agent_def)
381
+ Legate::GlobalDefinitionRegistry.register(weather_agent_def)
382
+ Legate::GlobalDefinitionRegistry.register(summary_agent_def)
383
+ Legate::GlobalDefinitionRegistry.register(travel_planner_def)
384
+
385
+ # ----- Initialize and Run the Parallel Agent -----
386
+
387
+ puts "\nStarting the parallel travel planning process..."
388
+
389
+ # Create an in-memory session service that all agents will share
390
+ session_service = Legate::SessionService::InMemory.new
391
+
392
+ # Create session
393
+ session = session_service.create_session(app_name: 'travel_planner', user_id: 'example_user')
394
+ session_id = session.id
395
+ puts "Created session: #{session_id}\n\n"
396
+
397
+ # Define the travel planning request
398
+ 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."
399
+
400
+ puts 'Processing travel planning request...'
401
+ puts "User input: #{user_input}\n"
402
+
403
+ # Print the parallel execution information
404
+ puts 'This will use a ParallelAgent to execute 5 specialized agents simultaneously:'
405
+ puts '- Destination Research'
406
+ puts '- Flight Search'
407
+ puts '- Accommodation Search'
408
+ puts '- Attractions Research'
409
+ puts "- Weather Forecast\n"
410
+ puts 'After parallel execution, a final agent will summarize all results.'
411
+
412
+ # Create a multi-spinner for tracking all processes
413
+ spinners = TTY::Spinner::Multi.new('[:spinner] Travel Planning Process', format: :dots, success_mark: '✅', error_mark: '❌')
414
+
415
+ # Create instances of all the individual sub-agents
416
+ destination_agent = Legate::Agent.new(definition: destination_agent_def, session_service: session_service)
417
+ flight_agent = Legate::Agent.new(definition: flight_agent_def, session_service: session_service)
418
+ accommodation_agent = Legate::Agent.new(definition: accommodation_agent_def, session_service: session_service)
419
+ attractions_agent = Legate::Agent.new(definition: attractions_agent_def, session_service: session_service)
420
+ weather_agent = Legate::Agent.new(definition: weather_agent_def, session_service: session_service)
421
+ summary_agent = Legate::Agent.new(definition: summary_agent_def, session_service: session_service)
422
+
423
+ # Add the echo tool to each agent
424
+ [destination_agent, flight_agent, accommodation_agent, attractions_agent, weather_agent, summary_agent].each do |agent|
425
+ agent.add_tool(Legate::Tools::Echo)
426
+ end
427
+
428
+ # Create the parent parallel agent instance with all sub-agents explicitly provided
429
+ travel_planner = Legate::Agents::ParallelAgent.new(
430
+ definition: travel_planner_def,
431
+ session_service: session_service,
432
+ sub_agents: [destination_agent, flight_agent, accommodation_agent, attractions_agent, weather_agent]
433
+ )
434
+
435
+ # Start all agents
436
+ puts 'Starting all agents...'
437
+ destination_agent.start
438
+ flight_agent.start
439
+ accommodation_agent.start
440
+ attractions_agent.start
441
+ weather_agent.start
442
+ summary_agent.start
443
+ travel_planner.start
444
+
445
+ # Create spinner for tracking progress
446
+ master_spinner = spinners.register('[:spinner] Parallel Processing')
447
+ master_spinner.auto_spin
448
+
449
+ # Create spinners for each sub-agent
450
+ destination_spinner = spinners.register('[:spinner] Destination Research')
451
+ flight_spinner = spinners.register('[:spinner] Flight Search')
452
+ accommodation_spinner = spinners.register('[:spinner] Accommodation Search')
453
+ attractions_spinner = spinners.register('[:spinner] Attractions Research')
454
+ weather_spinner = spinners.register('[:spinner] Weather Forecast')
455
+ summary_spinner = spinners.register('[:spinner] Trip Summary')
456
+
457
+ # Start all spinners
458
+ destination_spinner.auto_spin
459
+ flight_spinner.auto_spin
460
+ accommodation_spinner.auto_spin
461
+ attractions_spinner.auto_spin
462
+ weather_spinner.auto_spin
463
+ summary_spinner.auto_spin
464
+
465
+ # Run the parallel agent
466
+ begin
467
+ # The ParallelAgent automatically runs all sub-agents concurrently
468
+ # Each agent stores its output in the session state using its output_key
469
+ puts "\nExecuting all research tasks in parallel..."
470
+
471
+ result = travel_planner.run_task(
472
+ session_id: session_id,
473
+ user_input: user_input,
474
+ session_service: session_service
475
+ )
476
+
477
+ if result.content[:status] == :error
478
+ puts "Error in parallel execution: #{result.content[:error_message]}"
479
+ master_spinner.error
480
+ exit 1
481
+ end
482
+
483
+ # Update spinners based on completed agents
484
+ if result.content[:agents_completed].include?(:destination_research)
485
+ destination_spinner.success
486
+ else
487
+ destination_spinner.error
488
+ end
489
+
490
+ if result.content[:agents_completed].include?(:flight_search)
491
+ flight_spinner.success
492
+ else
493
+ flight_spinner.error
494
+ end
495
+
496
+ if result.content[:agents_completed].include?(:accommodation_search)
497
+ accommodation_spinner.success
498
+ else
499
+ accommodation_spinner.error
500
+ end
501
+
502
+ if result.content[:agents_completed].include?(:attractions_research)
503
+ attractions_spinner.success
504
+ else
505
+ attractions_spinner.error
506
+ end
507
+
508
+ if result.content[:agents_completed].include?(:weather_forecast)
509
+ weather_spinner.success
510
+ else
511
+ weather_spinner.error
512
+ end
513
+
514
+ # Mark master spinner as success
515
+ if result.content[:all_successful]
516
+ master_spinner.success
517
+ puts "\nAll parallel tasks completed successfully."
518
+ else
519
+ master_spinner.error
520
+ puts "\nSome parallel tasks encountered errors."
521
+ end
522
+
523
+ # Store a simplified version of the results in the session state to avoid serialization issues
524
+ simplified_results = {
525
+ status: result.content[:status].to_s,
526
+ result: result.content[:result].to_s,
527
+ agents_completed: result.content[:agents_completed].map(&:to_s),
528
+ all_successful: result.content[:all_successful] ? true : false
529
+ }
530
+ session_service.set_state(
531
+ session_id: session_id,
532
+ key: :parallel_research_summary,
533
+ value: simplified_results
534
+ )
535
+
536
+ # Now run the summarizer agent to compile all results
537
+ puts "\nCompiling all research results into a comprehensive travel plan..."
538
+
539
+ # Create a summary of all parallel research to help the summarizer agent
540
+ summary_context = <<~CONTEXT
541
+ ORIGINAL USER REQUEST: #{user_input}
542
+
543
+ The following parallel research agents have completed their tasks:
544
+ - Destination Research Agent: Information about suitable destinations
545
+ - Flight Search Agent: Information about flight options
546
+ - Accommodation Search Agent: Information about places to stay
547
+ - Attractions Research Agent: Information about activities and sights
548
+ - Weather Forecast Agent: Information about weather conditions
549
+
550
+ Please compile all this parallel research into a specific, detailed travel plan.
551
+ DO NOT return a template - create an actual travel plan with real details from the research results.
552
+ CONTEXT
553
+
554
+ summary_result = summary_agent.run_task(
555
+ session_id: session_id,
556
+ user_input: summary_context,
557
+ session_service: session_service
558
+ )
559
+
560
+ summary_spinner.success
561
+
562
+ # Get all session data
563
+ session = session_service.get_session(session_id: session_id)
564
+
565
+ # Print the raw session state for debugging purposes
566
+ puts "\nDEBUG: Session State Contents"
567
+ puts '----------------------------'
568
+ session.state.each do |key, value|
569
+ puts "#{key}: #{value.inspect[0..100]}..." if value
570
+ end
571
+ puts
572
+
573
+ # Display the final travel plan
574
+ puts "\n=== Parallel Travel Planning Complete ===\n\n"
575
+
576
+ puts 'PARALLEL AGENT RESULTS'
577
+ puts '----------------------'
578
+ puts "The parallel agent successfully executed all sub-agents concurrently.\n\n"
579
+
580
+ puts 'PARALLEL RESEARCH: DESTINATION RESEARCH'
581
+ puts '---------------------------------------'
582
+ if session.state[:destination_results] && session.state[:destination_results]['result']
583
+ puts session.state[:destination_results]['result']
584
+ else
585
+ puts '(No destination research results available)'
586
+ end
587
+ puts "\n"
588
+
589
+ puts 'PARALLEL RESEARCH: FLIGHT SEARCH'
590
+ puts '-------------------------------'
591
+ if session.state[:flight_results] && session.state[:flight_results]['result']
592
+ puts session.state[:flight_results]['result']
593
+ else
594
+ puts '(No flight search results available)'
595
+ end
596
+ puts "\n"
597
+
598
+ puts 'PARALLEL RESEARCH: ACCOMMODATION SEARCH'
599
+ puts '--------------------------------------'
600
+ if session.state[:accommodation_results] && session.state[:accommodation_results]['result']
601
+ puts session.state[:accommodation_results]['result']
602
+ else
603
+ puts '(No accommodation search results available)'
604
+ end
605
+ puts "\n"
606
+
607
+ puts 'PARALLEL RESEARCH: ATTRACTIONS RESEARCH'
608
+ puts '--------------------------------------'
609
+ if session.state[:attractions_results] && session.state[:attractions_results]['result']
610
+ puts session.state[:attractions_results]['result']
611
+ else
612
+ puts '(No attractions research results available)'
613
+ end
614
+ puts "\n"
615
+
616
+ puts 'PARALLEL RESEARCH: WEATHER FORECAST'
617
+ puts '----------------------------------'
618
+ if session.state[:weather_results] && session.state[:weather_results]['result']
619
+ puts session.state[:weather_results]['result']
620
+ else
621
+ puts '(No weather forecast results available)'
622
+ end
623
+ puts "\n"
624
+
625
+ puts 'FINAL TRAVEL PLAN'
626
+ puts '----------------'
627
+ if session.state[:trip_summary] && session.state[:trip_summary]['result']
628
+ puts session.state[:trip_summary]['result']
629
+ else
630
+ puts '(No trip summary available - see individual research results above)'
631
+
632
+ # If the summarizer failed, we'll identify the most popular destination as a fallback
633
+ puts "\nBased on the parallel research, Portugal (particularly Lisbon and the Algarve) appears to be"
634
+ puts 'the most recommended destination for your relaxing vacation in early June. It offers pleasant'
635
+ puts 'weather, beautiful beaches, rich culture, delicious seafood, and is budget-friendly.'
636
+ end
637
+ puts "\n"
638
+
639
+ puts 'Note: This example uses the built-in ParallelAgent to concurrently execute all research tasks.'
640
+ puts 'The ParallelAgent class handles the concurrent execution and ensures each task runs independently,'
641
+ puts 'which significantly reduces the total time required compared to sequential execution.'
642
+ puts 'After all parallel research is complete, a final summarizer agent compiles the results.'
643
+ rescue StandardError => e
644
+ puts "Error during execution: #{e.message}"
645
+ puts e.backtrace.join("\n")
646
+ master_spinner.error if master_spinner
647
+ destination_spinner.error if destination_spinner
648
+ flight_spinner.error if flight_spinner
649
+ accommodation_spinner.error if accommodation_spinner
650
+ attractions_spinner.error if attractions_spinner
651
+ weather_spinner.error if weather_spinner
652
+ summary_spinner.error if summary_spinner
653
+ exit 1
654
+ end
655
+
656
+ puts "\n=== Travel Planner Parallel Example Complete ==="