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,288 @@
|
|
|
1
|
+
# Using Inbound Webhooks in Legate
|
|
2
|
+
|
|
3
|
+
This document explains how to configure and use the inbound webhook feature in the Legate framework to trigger agent tasks from external systems.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
The primary goal of this feature is to allow external events (e.g., Git pushes, CI/CD notifications, CRM updates, IoT alerts) to initiate specific agent workflows asynchronously without requiring direct code integration or constantly running agent processes.
|
|
8
|
+
|
|
9
|
+
## Architecture Overview
|
|
10
|
+
|
|
11
|
+
The following diagram illustrates the flow of an inbound webhook request triggering an agent task:
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
graph LR
|
|
15
|
+
Ext[External System] -- HTTP Request --> Listen[Webhook Listener]
|
|
16
|
+
Listen -- Raw Request --> Router
|
|
17
|
+
Router -- Route Match --> Handler[Dynamic Agent Handler]
|
|
18
|
+
Handler -- Load Definition & Metadata --> Store[(Agent Definition Store)]
|
|
19
|
+
Handler -- Validation/Transform/Extract --> ThreadPool[Threaded Execution]
|
|
20
|
+
ThreadPool -- Concurrent::Promises.future --> AgentExec[Agent Execution]
|
|
21
|
+
AgentExec -- Load Definition --> Store
|
|
22
|
+
AgentExec -- Get Session --> SessionSvc[(Session Service)]
|
|
23
|
+
AgentExec -- Instantiate Agent --> AgentDef[Agent Definition]
|
|
24
|
+
AgentDef -- Uses --> SessionSvc
|
|
25
|
+
AgentExec -- Calls run_task --> AgentInst[Agent Instance]
|
|
26
|
+
AgentInst -- Logs Events --> SessionSvc
|
|
27
|
+
AgentInst -- Task Result --> AgentExec
|
|
28
|
+
AgentExec -- Logs Outcome --> Log[(Logs)]
|
|
29
|
+
|
|
30
|
+
subgraph "Webhook Listener Process"
|
|
31
|
+
Listen
|
|
32
|
+
Router
|
|
33
|
+
Handler
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
subgraph "In-Process Threaded Execution"
|
|
37
|
+
AgentExec
|
|
38
|
+
AgentInst
|
|
39
|
+
Log
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
style Store fill:#f9f,stroke:#333,stroke-width:2px
|
|
43
|
+
style SessionSvc fill:#f9f,stroke:#333,stroke-width:2px
|
|
44
|
+
style JobQueue fill:#ccf,stroke:#333,stroke-width:2px
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Key Components:**
|
|
48
|
+
|
|
49
|
+
* **Webhook Listener:** Receives HTTP requests, performs initial routing.
|
|
50
|
+
* **Dynamic Agent Handler:** Looks up agent definitions, validates, transforms, extracts session IDs based on agent metadata, and enqueues jobs.
|
|
51
|
+
* **Threaded Execution (`Concurrent::Promises.future`):** Runs webhook tasks asynchronously in background threads within the same process.
|
|
52
|
+
* **Agent Execution:** Loads definitions, instantiates agents, interacts with the Session Service, and executes `agent.run_task`.
|
|
53
|
+
* **Agent Definition Store:** Stores agent configurations, including webhook metadata.
|
|
54
|
+
* **Session Service:** Manages agent conversation state.
|
|
55
|
+
|
|
56
|
+
## Enabling the Webhook Listener
|
|
57
|
+
|
|
58
|
+
To receive webhooks, you first need to enable the built-in listener application within your Legate configuration (e.g., in `config/initializers/legate.rb` or your main setup file).
|
|
59
|
+
|
|
60
|
+
```ruby
|
|
61
|
+
# config/initializers/legate.rb or similar
|
|
62
|
+
require 'legate'
|
|
63
|
+
|
|
64
|
+
Legate.configure do |config|
|
|
65
|
+
# --- Enable the listener ---
|
|
66
|
+
config.webhooks.listener_enabled = true
|
|
67
|
+
|
|
68
|
+
# --- Optional Listener Settings ---
|
|
69
|
+
# Address to bind to (default: '127.0.0.1')
|
|
70
|
+
# Use '0.0.0.0' to listen on all interfaces (common for containers/production)
|
|
71
|
+
config.webhooks.listen_address = "0.0.0.0"
|
|
72
|
+
|
|
73
|
+
# Port to listen on (default: 9292)
|
|
74
|
+
config.webhooks.listen_port = 9293
|
|
75
|
+
|
|
76
|
+
# Base path for all webhook routes (default: '/webhooks')
|
|
77
|
+
config.webhooks.base_path = "/myhooks"
|
|
78
|
+
|
|
79
|
+
# ... other Legate configurations ...
|
|
80
|
+
end
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
When `listener_enabled` is `true`, the `legate web start` command will automatically mount the listener alongside the main Web UI (in development/test environments). For production, see the Deployment section.
|
|
84
|
+
|
|
85
|
+
## Triggering Agents via Webhook
|
|
86
|
+
|
|
87
|
+
The recommended way to trigger specific agents is using the dynamic agent route handler.
|
|
88
|
+
|
|
89
|
+
### Enabling the Dynamic Handler
|
|
90
|
+
|
|
91
|
+
Enable the dynamic route in your Legate configuration:
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
Legate.configure do |config|
|
|
95
|
+
config.webhooks.listener_enabled = true
|
|
96
|
+
# ... other listener settings ...
|
|
97
|
+
|
|
98
|
+
# --- Enable the dynamic route ---
|
|
99
|
+
config.webhooks.enable_dynamic_agent_handler = true
|
|
100
|
+
|
|
101
|
+
# --- Optional: Customize the route pattern ---
|
|
102
|
+
# The default pattern includes :agent_name parameter.
|
|
103
|
+
# config.webhooks.dynamic_agent_route_pattern = '/invoke/:agent_name' # Example override
|
|
104
|
+
|
|
105
|
+
# ... other Legate configurations ...
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
With the default settings (`base_path = '/webhooks'`, `dynamic_agent_route_pattern = '/agents/:agent_name/trigger'`), you can trigger an agent named `:my_cool_agent` by sending a `POST` request to:
|
|
110
|
+
|
|
111
|
+
`POST /webhooks/agents/my_cool_agent/trigger`
|
|
112
|
+
|
|
113
|
+
### Configuring an Agent to Receive Webhooks
|
|
114
|
+
|
|
115
|
+
For an agent to be triggerable via the dynamic route, you **must** configure specific metadata within its definition:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# app/agents/my_webhook_agent.rb (or loaded via DefinitionStore)
|
|
119
|
+
require 'legate'
|
|
120
|
+
|
|
121
|
+
Legate::Agent.define do |a|
|
|
122
|
+
a.name :my_webhook_agent
|
|
123
|
+
a.description "Processes incoming data from external service X."
|
|
124
|
+
a.instruction "Summarize the provided data."
|
|
125
|
+
# ... other agent settings (tools, model) ...
|
|
126
|
+
|
|
127
|
+
# --- Webhook Configuration Metadata ---
|
|
128
|
+
|
|
129
|
+
# 1. Enable Webhook Triggering (REQUIRED)
|
|
130
|
+
# MUST be true for the dynamic route to work for this agent.
|
|
131
|
+
a.webhook_enabled true
|
|
132
|
+
|
|
133
|
+
# 2. Define Payload Transformation (REQUIRED if enabled)
|
|
134
|
+
# A Proc that takes the parsed request body (Hash/Array) and returns
|
|
135
|
+
# the specific String or Hash expected by this agent's `run_task` `user_input`.
|
|
136
|
+
a.webhook_transformer ->(request_body) do
|
|
137
|
+
# Example: Extract data from a specific key
|
|
138
|
+
data = request_body['important_data']
|
|
139
|
+
unless data
|
|
140
|
+
# You can raise errors for invalid payloads
|
|
141
|
+
raise Legate::WebhookConfigurationError, "Missing 'important_data' in webhook payload."
|
|
142
|
+
end
|
|
143
|
+
"Summarize this: #{data}" # Return the user_input for run_task
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# 3. Define Session ID Extraction (REQUIRED if enabled)
|
|
147
|
+
# A Proc that takes the parsed request body and returns a String
|
|
148
|
+
# to be used as the session_id for this task.
|
|
149
|
+
# Allows grouping related events (e.g., multiple events for the same resource).
|
|
150
|
+
a.webhook_session_extractor ->(request_body) do
|
|
151
|
+
# Example: Use a resource ID from the payload
|
|
152
|
+
resource_id = request_body.dig('resource', 'id')
|
|
153
|
+
raise Legate::WebhookConfigurationError, "Missing resource ID in payload." unless resource_id
|
|
154
|
+
"external_resource_#{resource_id}" # Return the session_id string
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# 4. Configure Validation (Optional, but Recommended)
|
|
158
|
+
# Option A: Use a globally registered named validator (see below)
|
|
159
|
+
a.webhook_validator :hmac_sha256
|
|
160
|
+
# Option B: Provide a custom Proc directly
|
|
161
|
+
# a.webhook_validator ->(request, secret) { request.params['token'] == secret }
|
|
162
|
+
|
|
163
|
+
# 5. Provide a Secret for the Validator (Optional)
|
|
164
|
+
# Used by the validator logic (e.g., the HMAC key or the token).
|
|
165
|
+
a.webhook_secret ENV['MY_SERVICE_X_WEBHOOK_SECRET']
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Key Requirements:**
|
|
170
|
+
* `webhook_enabled` **must** be set to `true`.
|
|
171
|
+
* `webhook_transformer` Proc **must** be defined.
|
|
172
|
+
* `webhook_session_extractor` Proc **must** be defined.
|
|
173
|
+
|
|
174
|
+
If these are not met, the dynamic route handler will return an error (likely 404 or 500) when a request for that agent is received.
|
|
175
|
+
|
|
176
|
+
## Request Validation
|
|
177
|
+
|
|
178
|
+
It's highly recommended to validate incoming webhooks to ensure they originate from the expected source.
|
|
179
|
+
|
|
180
|
+
### Using Named Validators (HMAC Example)
|
|
181
|
+
|
|
182
|
+
You can define reusable validation logic globally in your Legate configuration and reference it by name in your agent definitions.
|
|
183
|
+
|
|
184
|
+
> **Note:** Legate already ships with a built-in `:hmac_sha256` validator (registered automatically), so you can reference `a.webhook_validator :hmac_sha256` without defining it yourself. The example below shows how an equivalent validator is implemented if you want to register your own.
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
# config/initializers/legate.rb or similar
|
|
188
|
+
require 'openssl'
|
|
189
|
+
|
|
190
|
+
Legate.configure do |config|
|
|
191
|
+
# ... listener config ...
|
|
192
|
+
config.webhooks.enable_dynamic_agent_handler = true
|
|
193
|
+
|
|
194
|
+
# --- Define a named HMAC SHA256 validator ---
|
|
195
|
+
config.webhooks.register_validator(:hmac_sha256) do |request, secret|
|
|
196
|
+
# Ensure a secret is configured for the agent/route
|
|
197
|
+
return false unless secret
|
|
198
|
+
# GitHub example: X-Hub-Signature-256: sha256=...
|
|
199
|
+
signature_header = request.env['HTTP_X_HUB_SIGNATURE_256']
|
|
200
|
+
return false unless signature_header&.start_with?('sha256=')
|
|
201
|
+
|
|
202
|
+
expected_signature = signature_header.delete_prefix('sha256=')
|
|
203
|
+
|
|
204
|
+
request.body.rewind # Read body for calculation
|
|
205
|
+
payload_body = request.body.read
|
|
206
|
+
request.body.rewind # Rewind for transformer
|
|
207
|
+
|
|
208
|
+
calculated_signature = OpenSSL::HMAC.hexdigest('sha256', secret, payload_body)
|
|
209
|
+
|
|
210
|
+
# Compare in constant time to prevent timing attacks. A length check is
|
|
211
|
+
# required first because OpenSSL.fixed_length_secure_compare raises on
|
|
212
|
+
# unequal-length inputs.
|
|
213
|
+
calculated_signature.bytesize == expected_signature.bytesize &&
|
|
214
|
+
OpenSSL.fixed_length_secure_compare(calculated_signature, expected_signature)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Optional: Define a global secret if many agents share one
|
|
218
|
+
# config.webhooks.global_secret = ENV['LEGATE_GLOBAL_WEBHOOK_SECRET']
|
|
219
|
+
|
|
220
|
+
# Optional: Set a default validator if an agent doesn't define one
|
|
221
|
+
# config.webhooks.global_validator = :hmac_sha256
|
|
222
|
+
end
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Then, in your agent definition, reference the validator and provide the secret:
|
|
226
|
+
|
|
227
|
+
```ruby
|
|
228
|
+
Legate::Agent.define do |a|
|
|
229
|
+
# ... name, description, transformer, extractor ...
|
|
230
|
+
a.webhook_enabled true
|
|
231
|
+
a.webhook_validator :hmac_sha256 # Use the named validator
|
|
232
|
+
a.webhook_secret ENV['SPECIFIC_AGENT_WEBHOOK_SECRET'] # Provide the secret
|
|
233
|
+
end
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Using Custom Validator Procs
|
|
237
|
+
|
|
238
|
+
For unique validation logic, provide a Proc directly in the agent definition:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
Legate::Agent.define do |a|
|
|
242
|
+
# ... name, description, transformer, extractor ...
|
|
243
|
+
a.webhook_enabled true
|
|
244
|
+
a.webhook_validator ->(request, secret) do
|
|
245
|
+
# Example: Check a query parameter against the secret
|
|
246
|
+
request.params['auth_token'] == secret
|
|
247
|
+
end
|
|
248
|
+
a.webhook_secret ENV['AGENT_AUTH_TOKEN']
|
|
249
|
+
end
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
The validator proc receives the Rack `request` object and the configured `secret` string (or `nil` if none is set).
|
|
253
|
+
|
|
254
|
+
## Workflow Overview
|
|
255
|
+
|
|
256
|
+
1. **External System** sends `POST /webhooks/agents/agent_name/trigger` with a JSON payload.
|
|
257
|
+
2. **WebhookListener** receives the request.
|
|
258
|
+
3. **Dynamic Handler** matches the route pattern (`/agents/:agent_name/trigger`).
|
|
259
|
+
4. Handler loads the `:agent_name` **Agent Definition**.
|
|
260
|
+
5. Checks `webhook_enabled` (must be `true`).
|
|
261
|
+
6. Performs **Validation** using `webhook_validator` and `webhook_secret`.
|
|
262
|
+
7. If valid, performs **Transformation** using `webhook_transformer` to get `user_input`.
|
|
263
|
+
8. Extracts **Session ID** using `webhook_session_extractor`.
|
|
264
|
+
9. Constructs a **Job Payload** (`agent_name`, `session_id`, `user_input`, session service config).
|
|
265
|
+
10. Dispatches the task via `Concurrent::Promises.future` for **threaded execution**.
|
|
266
|
+
11. Listener returns **`202 Accepted`** to the external system.
|
|
267
|
+
12. The background thread instantiates the **Session Service**.
|
|
268
|
+
13. The thread loads the **Agent Definition**.
|
|
269
|
+
14. The thread instantiates the **Agent** using the definition.
|
|
270
|
+
15. The thread calls `agent.run_task(session_id:, user_input:, session_service:)`.
|
|
271
|
+
16. `run_task` executes the agent logic (planning, tool use).
|
|
272
|
+
17. The thread logs the outcome.
|
|
273
|
+
|
|
274
|
+
## Security Considerations
|
|
275
|
+
|
|
276
|
+
* **HTTPS:** Always serve the webhook listener over HTTPS in production.
|
|
277
|
+
* **Secret Management:** Use environment variables or a secure secret management system for `webhook_secret` values. Never hardcode secrets.
|
|
278
|
+
* **Validation:** Strongly recommend validation (e.g., HMAC) for all webhooks triggering actions or handling sensitive data.
|
|
279
|
+
* **Input Sanitization:** Be mindful of how the `transformed_user_input` is used within your agent's instructions and tools to prevent potential injection issues.
|
|
280
|
+
* **Rate Limiting:** Consider adding rate limiting middleware (e.g., `rack-attack`) in front of the listener endpoint to prevent abuse.
|
|
281
|
+
|
|
282
|
+
## Deployment
|
|
283
|
+
|
|
284
|
+
* **Development:** Run `bundle exec legate web start`. If `listener_enabled` is true, the listener will be mounted automatically within the main web server process.
|
|
285
|
+
* **Production:**
|
|
286
|
+
* You can continue running the combined app as in development.
|
|
287
|
+
* Alternatively, for better isolation and scaling, you can create a separate `config.ru` and Procfile entry to run the `Legate::Web::WebhookListener` Rack app as a standalone process using a server like Puma or Unicorn.
|
|
288
|
+
* Webhook jobs run in-process via background threads using `Concurrent::Promises.future`, so no separate worker process is needed.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Welcome to the Legate Documentation
|
|
2
|
+
|
|
3
|
+
Legate — AI Agent Framework for Ruby — is a comprehensive framework designed to simplify the creation, management, and deployment of sophisticated AI agents. Whether you're building simple task-oriented bots or complex, multi-tool agents capable of intricate planning, the Legate provides the foundational components and a streamlined workflow to accelerate your development process.
|
|
4
|
+
|
|
5
|
+
This documentation will guide you through understanding the core principles of Legate, setting up your development environment, utilizing its built-in features, and extending its capabilities to suit your specific needs.
|
|
6
|
+
|
|
7
|
+
## What is Legate?
|
|
8
|
+
|
|
9
|
+
Legate is a Ruby-based toolkit that empowers developers to:
|
|
10
|
+
|
|
11
|
+
* **Define Agent Capabilities:** Easily specify an agent's instructions, the tools it can use, the AI model it leverages (e.g., from Google's Gemini family), and its operational parameters.
|
|
12
|
+
* **Manage Agent Lifecycle:** Control how agents are started, how they process tasks, and how they are stopped or updated.
|
|
13
|
+
* **Utilize a Rich Toolset:** Leverage a variety of built-in tools (like calculators, webhooks, and even tools that delegate tasks to other agents) or create your own custom tools.
|
|
14
|
+
* **Handle Complex Interactions:** Employ a planner that enables agents to break down complex requests into a sequence of tool calls.
|
|
15
|
+
* **Persist and Manage State:** Use session services and definition stores to manage conversational history and agent configurations.
|
|
16
|
+
* **Integrate with External Systems:** Expose agents via web UIs, connect them to messaging platforms, or trigger them with webhooks.
|
|
17
|
+
* **Deploy with Ease:** Generate deployment assets (like Dockerfiles and cloud configuration scripts) to run your Legate applications in various environments.
|
|
18
|
+
|
|
19
|
+
## Getting Started
|
|
20
|
+
|
|
21
|
+
To begin your journey with Legate, we recommend the following steps:
|
|
22
|
+
|
|
23
|
+
1. **Understand the Fundamentals:** Familiarize yourself with the [Core Concepts](./core_concepts/legate_architecture_overview) that underpin the Legate, such as the [Agent Lifecycle](./core_concepts/legate_agent_lifecycle), [Tools and Registry](./tools/legate_tools_and_registry), and [Session Management](./core_concepts/legate_session_service).
|
|
24
|
+
2. **Setup Your Environment:** Ensure you have Ruby and Bundler installed. Most Legate projects will start by adding `legate` to their `Gemfile`.
|
|
25
|
+
3. **Explore the Configuration:** Learn how to configure Legate globally and per agent by reviewing the [Legate Configuration](./core_concepts/legate_configuration) guide.
|
|
26
|
+
4. **Try the CLI:** Use the [Legate Command-Line Interface](./cli/legate_cli_usage) to manage agent definitions, run the web UI, and interact with other Legate components.
|
|
27
|
+
5. **Run an Example:** Browse the [Examples Guide](./examples) for 16 hands-on examples covering every major feature, from basic agents to MCP integration.
|
|
28
|
+
|
|
29
|
+
## Key Features
|
|
30
|
+
|
|
31
|
+
* **Modular Architecture:** Easily extendable and customizable.
|
|
32
|
+
* **Powerful Planner:** Enables multi-step reasoning and tool usage.
|
|
33
|
+
* **Rich Tool Ecosystem:** Comes with several built-in tools and a clear interface for adding new ones.
|
|
34
|
+
* **Agent Definition Store:** Persistently store and manage your agent configurations.
|
|
35
|
+
* **Session Management:** Track conversation history and agent state.
|
|
36
|
+
* **Web UI:** A built-in Sinatra application for managing agents and viewing sessions.
|
|
37
|
+
* **CLI for Management:** Robust command-line tools for development and administration.
|
|
38
|
+
* **Deployment Assistance:** Tools to generate Dockerfiles and deployment scripts.
|
|
39
|
+
|
|
40
|
+
## Navigating these Docs
|
|
41
|
+
|
|
42
|
+
This documentation is organized into several key areas:
|
|
43
|
+
|
|
44
|
+
* **[Core Concepts](./core_concepts/):** Deep dives into the fundamental building blocks and architecture of Legate.
|
|
45
|
+
* **[Guides](./guides/):** Practical step-by-step instructions for common tasks and integrations.
|
|
46
|
+
* **[CLI Reference](./cli/legate_cli_usage):** Detailed information on using the `legate` command-line tool.
|
|
47
|
+
* **[Web UI Overview](./web_ui/legate_web_ui):** Information about the built-in web interface.
|
|
48
|
+
* **[Built-in Tools Reference](./tools/legate_built_in_tools):** Documentation for the tools that come packaged with Legate.
|
|
49
|
+
* **[Error Handling](./error_handling/legate_error_handling):** Guidance on understanding and managing errors within Legate.
|
|
50
|
+
|
|
51
|
+
We hope this documentation helps you build amazing AI agents with Legate!
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Advanced Features
|
|
2
|
+
|
|
3
|
+
## Agent Delegation
|
|
4
|
+
|
|
5
|
+
Agents can dynamically delegate tasks to other specialized agents during execution. This allows an agent to "ask for help" or hand off a sub-task.
|
|
6
|
+
|
|
7
|
+
### Configuration
|
|
8
|
+
|
|
9
|
+
Use `can_delegate_to` to specify which agents are available for delegation.
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
Legate::Agent.define do |agent|
|
|
13
|
+
agent.name :manager
|
|
14
|
+
agent.instruction "Manage the project and delegate tasks."
|
|
15
|
+
|
|
16
|
+
# Allow delegation to these agents
|
|
17
|
+
agent.can_delegate_to :researcher, :coder
|
|
18
|
+
end
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### How it Works
|
|
22
|
+
|
|
23
|
+
1. **Planning**: The `Legate::Planner` sees the available delegation targets as "tools" (e.g., `agent_transfer_to_researcher`).
|
|
24
|
+
2. **Execution**: If the agent decides to delegate, it invokes the delegation tool.
|
|
25
|
+
3. **Transfer**: The `Legate::Agent` intercepts this call and executes the target agent with the specified task.
|
|
26
|
+
4. **Session Reuse**: `can_delegate_to` / `Agent#transfer_to` delegation **reuses the calling session** — the same `session_id` is passed to the target agent, so both agents share session state. It does *not* create a new isolated session.
|
|
27
|
+
|
|
28
|
+
### Delegation vs. `AgentTool`
|
|
29
|
+
|
|
30
|
+
A separate mechanism, the `AgentTool` (registered as the `:delegate_task` tool), runs another agent from within a tool call. Unlike `transfer_to`, `AgentTool` defaults to a **new isolated session**. It exposes a `use_calling_session` parameter (default `false`) that, when set to `true`, makes it reuse the caller's session and share state instead.
|
|
31
|
+
|
|
32
|
+
## Callbacks
|
|
33
|
+
|
|
34
|
+
You can hook into the agent's lifecycle to execute custom logic.
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
Legate::Agent.define do |agent|
|
|
38
|
+
# ...
|
|
39
|
+
|
|
40
|
+
agent.before_agent_callback do |context|
|
|
41
|
+
Legate.logger.info "Agent starting for session #{context.session_id}"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
agent.after_tool_callback do |tool, params, context, result|
|
|
45
|
+
# Modify result or log execution
|
|
46
|
+
Legate.logger.info "Tool #{tool.name} executed."
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Available callbacks:
|
|
52
|
+
- `before_agent_callback`
|
|
53
|
+
- `after_agent_callback`
|
|
54
|
+
- `before_model_callback`
|
|
55
|
+
- `after_model_callback`
|
|
56
|
+
- `before_tool_callback`
|
|
57
|
+
- `after_tool_callback`
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Agent Delegation in Legate
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Agent delegation is a powerful feature in Legate that allows one agent to dynamically transfer control to another specialized agent during execution. This enables complex workflows where a coordinator agent can make decisions about which specialized agent should handle a particular task.
|
|
6
|
+
|
|
7
|
+
Unlike the `AgentTool` approach, which creates a new isolated session for the target agent, delegation maintains the same session context. This ensures continuity and allows agents to share state through the session.
|
|
8
|
+
|
|
9
|
+
## Key Concepts
|
|
10
|
+
|
|
11
|
+
### Delegation Targets
|
|
12
|
+
|
|
13
|
+
For an agent to delegate to another agent, the target agents must be explicitly defined as "delegation targets" in the delegating agent's definition:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
coordinator_agent = Legate::Agent.define do |a|
|
|
17
|
+
a.name :coordinator_agent
|
|
18
|
+
a.description 'A coordinator agent that delegates tasks'
|
|
19
|
+
a.instruction 'You are a coordinator. Analyze tasks and delegate to specialists.'
|
|
20
|
+
|
|
21
|
+
# Define which agents this agent can delegate to
|
|
22
|
+
a.can_delegate_to :math_agent, :research_agent, :translation_agent
|
|
23
|
+
|
|
24
|
+
# ... other configuration ...
|
|
25
|
+
end
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Agent Hierarchy
|
|
29
|
+
|
|
30
|
+
Delegation works within the agent hierarchy. An agent can:
|
|
31
|
+
|
|
32
|
+
1. **Delegate to direct sub-agents**: When agents are organized in a parent-child relationship.
|
|
33
|
+
2. **Delegate to any agent in the hierarchy**: By searching up and down the agent tree.
|
|
34
|
+
3. **Delegate to registered agents**: Even agents not directly in the hierarchy can be delegated to if their definitions are registered globally.
|
|
35
|
+
|
|
36
|
+
### Session State Continuity
|
|
37
|
+
|
|
38
|
+
When delegation occurs:
|
|
39
|
+
|
|
40
|
+
- The same session ID is used across all agents
|
|
41
|
+
- The session state is shared, allowing agents to read and write state
|
|
42
|
+
- The `output_key` feature can be used by each agent to store its results in the session state
|
|
43
|
+
|
|
44
|
+
## Implementation Methods
|
|
45
|
+
|
|
46
|
+
### 1. LLM-Driven Delegation
|
|
47
|
+
|
|
48
|
+
The primary way delegation happens is through the LLM planner, which can generate plan steps with special "agent_transfer_to_X" tool names:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"thought_process": "This is a math question, should delegate to math agent",
|
|
53
|
+
"plan": [
|
|
54
|
+
{
|
|
55
|
+
"step": 1,
|
|
56
|
+
"type": "tool_use",
|
|
57
|
+
"tool_name": "agent_transfer_to_math_agent",
|
|
58
|
+
"tool_input": {
|
|
59
|
+
"task": "Calculate the square root of 144"
|
|
60
|
+
},
|
|
61
|
+
"reason": "This is a mathematical calculation"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
When the agent executes this plan, it recognizes the `agent_transfer_to_` prefix and performs delegation to the specified agent.
|
|
68
|
+
|
|
69
|
+
### 2. Direct Transfer Method
|
|
70
|
+
|
|
71
|
+
You can also programmatically delegate using the `transfer_to` method:
|
|
72
|
+
|
|
73
|
+
```ruby
|
|
74
|
+
result = agent.transfer_to(
|
|
75
|
+
:math_agent, # Target agent name
|
|
76
|
+
"Calculate 2 + 2", # Task to delegate
|
|
77
|
+
session_id, # Current session ID
|
|
78
|
+
session_service # Session service instance
|
|
79
|
+
)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This returns a standard result hash:
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
{
|
|
86
|
+
status: :success,
|
|
87
|
+
target_agent: "math_agent",
|
|
88
|
+
result: { status: :success, result: "4" }
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Code Examples
|
|
93
|
+
|
|
94
|
+
### Basic Delegation Setup
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
# Define a math specialist agent
|
|
98
|
+
math_agent = Legate::Agent.define do |a|
|
|
99
|
+
a.name :math_agent
|
|
100
|
+
a.description 'Specialized in calculations'
|
|
101
|
+
a.instruction 'You are a math expert. Solve calculations accurately.'
|
|
102
|
+
a.use_tool :calculate
|
|
103
|
+
a.output_key :calculation_result
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Define a coordinator that can delegate to the math agent
|
|
107
|
+
coordinator = Legate::Agent.define do |a|
|
|
108
|
+
a.name :coordinator
|
|
109
|
+
a.description 'Coordinates tasks between agents'
|
|
110
|
+
a.instruction 'Analyze the task and delegate to specialists.'
|
|
111
|
+
a.can_delegate_to :math_agent
|
|
112
|
+
a.use_tool :echo
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Create and link the agents
|
|
116
|
+
coordinator_instance = Legate::Agent.new(definition: coordinator)
|
|
117
|
+
math_instance = Legate::Agent.new(definition: math_agent)
|
|
118
|
+
|
|
119
|
+
# Establish parent-child relationship
|
|
120
|
+
math_instance.instance_variable_set(:@parent_agent, coordinator_instance)
|
|
121
|
+
coordinator_instance.instance_variable_set(:@sub_agents, [math_instance])
|
|
122
|
+
|
|
123
|
+
# Start the agents
|
|
124
|
+
coordinator_instance.start
|
|
125
|
+
math_instance.start
|
|
126
|
+
|
|
127
|
+
# Run a task that might be delegated
|
|
128
|
+
result = coordinator_instance.run_task(
|
|
129
|
+
session_id: session_id,
|
|
130
|
+
user_input: "What is 125 * 45?",
|
|
131
|
+
session_service: session_service
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Working with Session State
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
# The delegated agent can store its result in the session state
|
|
139
|
+
math_agent = Legate::Agent.define do |a|
|
|
140
|
+
a.name :math_agent
|
|
141
|
+
a.description 'Specialized in calculations'
|
|
142
|
+
a.instruction 'You are a math expert. Solve calculations accurately.'
|
|
143
|
+
a.use_tool :calculate
|
|
144
|
+
a.output_key :calculation_result # Will store results with this key
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Later, another agent can access this result
|
|
148
|
+
translator_agent = Legate::Agent.define do |a|
|
|
149
|
+
a.name :translator_agent
|
|
150
|
+
a.description 'Translates content'
|
|
151
|
+
a.instruction <<~INSTRUCTION
|
|
152
|
+
You are a translator. Translate text using the calculation results
|
|
153
|
+
from the math agent if available in the session state.
|
|
154
|
+
INSTRUCTION
|
|
155
|
+
a.use_tool :translate
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Comparison: Delegation vs. AgentTool
|
|
160
|
+
|
|
161
|
+
| Feature | Agent Delegation | AgentTool |
|
|
162
|
+
|---------|-----------------|-----------|
|
|
163
|
+
| **Session Context** | Maintains the same session | Creates a new session |
|
|
164
|
+
| **State Sharing** | Shared session state | No state sharing, isolated execution |
|
|
165
|
+
| **Agent Registration** | Uses agent hierarchy and registry | Uses agent definitions from store |
|
|
166
|
+
| **Use Case** | Complex workflows with state continuity | Isolated, independent agent operations |
|
|
167
|
+
| **Implementation** | Direct `transfer_to` or LLM-driven | Tool execution via `tool_registry` |
|
|
168
|
+
|
|
169
|
+
## Best Practices
|
|
170
|
+
|
|
171
|
+
1. **Be Specific with Instructions**: Provide clear guidelines in your coordinator agent's instructions about when to delegate and which specialized agent to use for different tasks.
|
|
172
|
+
|
|
173
|
+
2. **Use `output_key`**: Have specialized agents store their results using the `output_key` feature to make them available in the session state.
|
|
174
|
+
|
|
175
|
+
3. **Validate Delegation Targets**: Ensure all agents defined in `can_delegate_to` actually exist. The system will warn you about missing targets, but it's best to address these warnings.
|
|
176
|
+
|
|
177
|
+
4. **Avoid Circular Delegation**: The system prevents direct circular dependencies, but complex cascading delegation chains should be avoided.
|
|
178
|
+
|
|
179
|
+
5. **Consider Agent Hierarchy**: Organize your agents in a logical hierarchy that reflects your delegation patterns, with coordinator agents at the top and specialists as sub-agents.
|
|
180
|
+
|
|
181
|
+
## Implementation Details
|
|
182
|
+
|
|
183
|
+
Under the hood, delegation is implemented through:
|
|
184
|
+
|
|
185
|
+
1. The `agent_transfer_to_` special tool name pattern recognized by `execute_step`
|
|
186
|
+
2. The `transfer_to` method that handles the delegation logic
|
|
187
|
+
3. Agent hierarchy navigation through `root_agent` and `find_agent` methods
|
|
188
|
+
4. Session state persistence across agent boundaries
|
|
189
|
+
|
|
190
|
+
For detailed implementation examples, see `examples/11_agent_delegation.rb` (and `examples/advanced/mas/proper_delegation_example.rb`) in the Legate codebase.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Agent Hierarchy
|
|
2
|
+
|
|
3
|
+
The Legate supports a hierarchical agent structure, allowing agents to be composed of other agents (sub-agents). This enables the creation of complex systems where a parent agent coordinates the work of specialized child agents.
|
|
4
|
+
|
|
5
|
+
## Core Concepts
|
|
6
|
+
|
|
7
|
+
### Parent and Sub-Agents
|
|
8
|
+
|
|
9
|
+
- **Parent Agent**: An agent that contains and manages other agents.
|
|
10
|
+
- **Sub-Agent**: An agent that is managed by another agent. A sub-agent can also be a parent to other agents, creating a multi-level hierarchy.
|
|
11
|
+
|
|
12
|
+
### Definition
|
|
13
|
+
|
|
14
|
+
You can define sub-agents within an `Legate::AgentDefinition` using the `sub_agents_define` DSL method.
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
Legate::Agent.define do |agent|
|
|
18
|
+
agent.name :parent_agent
|
|
19
|
+
agent.description "A parent agent that manages sub-agents"
|
|
20
|
+
agent.instruction "You are a manager. Delegate tasks to your sub-agents."
|
|
21
|
+
|
|
22
|
+
# Define sub-agents by name
|
|
23
|
+
agent.sub_agents_define :researcher_agent, :writer_agent
|
|
24
|
+
end
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The sub-agents must be defined and registered in the `GlobalDefinitionRegistry` so they can be instantiated when the parent agent is initialized.
|
|
28
|
+
|
|
29
|
+
## Runtime Structure
|
|
30
|
+
|
|
31
|
+
When a parent agent is initialized, it attempts to instantiate its defined sub-agents.
|
|
32
|
+
|
|
33
|
+
- **`agent.sub_agents`**: Returns a collection of initialized sub-agent instances.
|
|
34
|
+
- **`agent.parent_agent`**: Returns the parent agent instance (if any).
|
|
35
|
+
- **`agent.root_agent`**: Returns the top-level agent in the hierarchy.
|
|
36
|
+
|
|
37
|
+
### Navigation
|
|
38
|
+
|
|
39
|
+
You can navigate the hierarchy at runtime:
|
|
40
|
+
|
|
41
|
+
- **`agent.find_sub_agent(name)`**: Finds a direct sub-agent by name.
|
|
42
|
+
- **`agent.find_agent(name)`**: Finds an agent anywhere in the hierarchy (depth-first search).
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
Hierarchies are fundamental to **Workflow Agents** (Sequential, Parallel, Loop) and **Delegation**.
|
|
47
|
+
|
|
48
|
+
- **Workflow Agents**: Use sub-agents as steps in a predefined process.
|
|
49
|
+
- **Delegation**: Uses sub-agents (or other agents in the hierarchy) as targets for dynamic task delegation.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# State Management
|
|
2
|
+
|
|
3
|
+
The Legate provides a robust state management system to share data between agents and persist execution context across sessions.
|
|
4
|
+
|
|
5
|
+
## Session State
|
|
6
|
+
|
|
7
|
+
Every agent execution happens within a `Session`. The session maintains:
|
|
8
|
+
- **Event History**: A log of all interactions (user messages, agent responses, tool calls).
|
|
9
|
+
- **State**: A key-value store for persisting data.
|
|
10
|
+
|
|
11
|
+
## Output Keys
|
|
12
|
+
|
|
13
|
+
Agents can automatically store their final result into the session state using the `output_key` definition.
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
Legate::Agent.define do |agent|
|
|
17
|
+
agent.name :researcher
|
|
18
|
+
# ...
|
|
19
|
+
agent.output_key :research_summary # Result will be saved to state key :research_summary
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
When this agent finishes execution, its result is saved to the session state under `:research_summary`.
|
|
24
|
+
|
|
25
|
+
## Accessing State
|
|
26
|
+
|
|
27
|
+
### In Other Agents
|
|
28
|
+
Subsequent agents in a workflow (e.g., in a `SequentialAgent`) can access this state. The Legate automatically injects relevant state information or allows agents to query it.
|
|
29
|
+
|
|
30
|
+
### In Tools
|
|
31
|
+
Tools can access the session state via the `Legate::ToolContext`.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
def perform_execution(params, context)
|
|
35
|
+
# Read from state
|
|
36
|
+
previous_result = context.state_get(:research_summary)
|
|
37
|
+
|
|
38
|
+
# Write to state (pending until tool completion)
|
|
39
|
+
context.state_set(:new_data, "value")
|
|
40
|
+
|
|
41
|
+
# ...
|
|
42
|
+
end
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Session Service
|
|
46
|
+
|
|
47
|
+
The `Legate::SessionService` (e.g., `InMemory`) handles the actual storage and retrieval of state within the current process.
|