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