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,326 @@
|
|
|
1
|
+
# Auto-Loading Custom Tools and Agents
|
|
2
|
+
|
|
3
|
+
This guide explains how Legate automatically discovers and loads your custom tools and agent definitions when starting the web server.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
When you run `legate web start`, Legate automatically scans conventional directories for Ruby files containing custom tools and agents. This means you can:
|
|
8
|
+
|
|
9
|
+
- Drop a tool file in `lib/tools/` and it's automatically available
|
|
10
|
+
- Drop an agent file in `lib/agents/` and it appears in the Web UI
|
|
11
|
+
- No manual configuration required
|
|
12
|
+
|
|
13
|
+
> The Web UI [AI builders](ai_code_generators) write to `tools/` and `agents/` for
|
|
14
|
+
> you (when installing a tool live, or via an agent's **Save to `agents/`** action),
|
|
15
|
+
> so those generated files are picked up here on the next start too.
|
|
16
|
+
|
|
17
|
+
## Directory Structure
|
|
18
|
+
|
|
19
|
+
Legate scans the following directories relative to where you run `legate web start`:
|
|
20
|
+
|
|
21
|
+
### For Tools
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
./lib/tools/ # Primary location
|
|
25
|
+
./agents/lib/tools/ # Alternative (nested project)
|
|
26
|
+
./tools/ # Simple projects
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### For Agents
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
./lib/agents/ # Primary location
|
|
33
|
+
./agents/lib/agents/ # Alternative (nested project)
|
|
34
|
+
./agents/ # Simple projects (excludes /tools/ subdirs)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Recommended Structure
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
your_project/
|
|
41
|
+
├── Gemfile
|
|
42
|
+
├── .env # Environment variables (GOOGLE_API_KEY, etc.)
|
|
43
|
+
├── lib/
|
|
44
|
+
│ ├── tools/
|
|
45
|
+
│ │ ├── weather_tool.rb
|
|
46
|
+
│ │ ├── email_validator_tool.rb
|
|
47
|
+
│ │ └── custom_api_tool.rb
|
|
48
|
+
│ └── agents/
|
|
49
|
+
│ ├── customer_support_agent.rb
|
|
50
|
+
│ ├── data_processor_agent.rb
|
|
51
|
+
│ └── notification_agent.rb
|
|
52
|
+
└── legate_init.rb # Optional: custom initialization
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Creating Auto-Loadable Tools
|
|
56
|
+
|
|
57
|
+
Your tool file should:
|
|
58
|
+
|
|
59
|
+
1. Define the tool class
|
|
60
|
+
2. Register it with `GlobalToolManager`
|
|
61
|
+
|
|
62
|
+
**Example: `lib/tools/greeting_tool.rb`**
|
|
63
|
+
|
|
64
|
+
```ruby
|
|
65
|
+
# frozen_string_literal: true
|
|
66
|
+
|
|
67
|
+
require 'legate/tool'
|
|
68
|
+
|
|
69
|
+
class GreetingTool < Legate::Tool
|
|
70
|
+
tool_description 'Generates a personalized greeting'
|
|
71
|
+
|
|
72
|
+
parameter :name,
|
|
73
|
+
type: :string,
|
|
74
|
+
description: 'Name of the person to greet',
|
|
75
|
+
required: true
|
|
76
|
+
|
|
77
|
+
parameter :style,
|
|
78
|
+
type: :string,
|
|
79
|
+
description: 'Greeting style: formal, casual, or enthusiastic',
|
|
80
|
+
required: false
|
|
81
|
+
|
|
82
|
+
private
|
|
83
|
+
|
|
84
|
+
def perform_execution(params, context)
|
|
85
|
+
name = params[:name]
|
|
86
|
+
style = params[:style] || 'casual'
|
|
87
|
+
|
|
88
|
+
greeting = case style
|
|
89
|
+
when 'formal'
|
|
90
|
+
"Good day, #{name}. How may I assist you?"
|
|
91
|
+
when 'enthusiastic'
|
|
92
|
+
"Hey #{name}! Great to see you! 🎉"
|
|
93
|
+
else
|
|
94
|
+
"Hello, #{name}!"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
{ status: :success, result: greeting }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# This line makes the tool available to agents
|
|
102
|
+
Legate::GlobalToolManager.register_tool(GreetingTool)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Creating Auto-Loadable Agents
|
|
106
|
+
|
|
107
|
+
Your agent file should:
|
|
108
|
+
|
|
109
|
+
1. Define the agent using `AgentDefinition`
|
|
110
|
+
2. Register it with `GlobalDefinitionRegistry`
|
|
111
|
+
|
|
112
|
+
**Example: `lib/agents/greeter_agent.rb`**
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# frozen_string_literal: true
|
|
116
|
+
|
|
117
|
+
require 'legate'
|
|
118
|
+
|
|
119
|
+
definition = Legate::AgentDefinition.new.define do |a|
|
|
120
|
+
a.name :greeter
|
|
121
|
+
a.description 'A friendly agent that greets users'
|
|
122
|
+
|
|
123
|
+
a.instruction <<~INSTRUCTION
|
|
124
|
+
You are a friendly greeter agent.
|
|
125
|
+
|
|
126
|
+
When a user introduces themselves or asks for a greeting,
|
|
127
|
+
use the greeting_tool to generate an appropriate response.
|
|
128
|
+
|
|
129
|
+
Match the greeting style to the user's tone:
|
|
130
|
+
- Formal requests get formal greetings
|
|
131
|
+
- Casual messages get casual greetings
|
|
132
|
+
- Excited users get enthusiastic greetings
|
|
133
|
+
INSTRUCTION
|
|
134
|
+
|
|
135
|
+
a.use_tool :greeting_tool
|
|
136
|
+
a.model_name 'gemini-2.0-flash'
|
|
137
|
+
a.temperature 0.7
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# This line makes the agent appear in the Web UI
|
|
141
|
+
Legate::GlobalDefinitionRegistry.register(definition)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Load Order
|
|
145
|
+
|
|
146
|
+
Legate loads files in this order:
|
|
147
|
+
|
|
148
|
+
1. **Initializer** (if present) - `legate_init.rb`, `config/legate_init.rb`, or `agents/legate_init.rb`
|
|
149
|
+
2. **Tools** - All `.rb` files in tool directories
|
|
150
|
+
3. **Agents** - All `.rb` files in agent directories
|
|
151
|
+
4. **Register Definitions** - Agents are registered in the `GlobalDefinitionRegistry` for the Web UI
|
|
152
|
+
|
|
153
|
+
This order ensures tools are available before agents that reference them.
|
|
154
|
+
|
|
155
|
+
## Custom Initializer
|
|
156
|
+
|
|
157
|
+
For advanced setup, create a `legate_init.rb` file in your project root:
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
# legate_init.rb
|
|
161
|
+
|
|
162
|
+
# Set up environment
|
|
163
|
+
ENV['MY_CUSTOM_VAR'] ||= 'default_value'
|
|
164
|
+
|
|
165
|
+
# Configure Legate
|
|
166
|
+
Legate.configure do |config|
|
|
167
|
+
config.default_model_name = 'gemini-2.0-flash'
|
|
168
|
+
config.default_temperature = 0.5
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Pre-load specific dependencies
|
|
172
|
+
require 'some_custom_gem'
|
|
173
|
+
|
|
174
|
+
# Log that initialization completed
|
|
175
|
+
Legate.logger.info "Custom initialization complete"
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
The initializer runs before auto-loading, so you can set up anything your tools/agents need.
|
|
179
|
+
|
|
180
|
+
## Startup Logs
|
|
181
|
+
|
|
182
|
+
When auto-loading works correctly, you'll see log messages like:
|
|
183
|
+
|
|
184
|
+
```
|
|
185
|
+
INFO: Auto-loaded 3 custom tool file(s). Registered tools: [:echo, :calculator, ..., :greeting_tool, :weather_tool, :email_validator_tool]
|
|
186
|
+
INFO: Auto-loaded 2 custom agent file(s). Registered agents: [:greeter, :data_processor]
|
|
187
|
+
INFO: Synced agent 'greeter' to definition store for Web UI
|
|
188
|
+
INFO: Synced agent 'data_processor' to definition store for Web UI
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Disabling Auto-Loading
|
|
192
|
+
|
|
193
|
+
If you need to disable auto-loading (e.g., for testing):
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
bundle exec legate web start --no-autoload
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Excluding Files
|
|
200
|
+
|
|
201
|
+
The auto-loader automatically skips:
|
|
202
|
+
|
|
203
|
+
- Test files (`*_spec.rb`, `*_test.rb`)
|
|
204
|
+
- Files in `/tools/` subdirectories when loading agents (to avoid double-loading)
|
|
205
|
+
|
|
206
|
+
## Common Issues
|
|
207
|
+
|
|
208
|
+
### Tool Not Appearing
|
|
209
|
+
|
|
210
|
+
1. **Check the file location** - Must be in one of the scanned directories
|
|
211
|
+
2. **Check registration** - File must call `Legate::GlobalToolManager.register_tool(YourTool)`
|
|
212
|
+
3. **Check for errors** - Look at startup logs for load failures
|
|
213
|
+
|
|
214
|
+
### Agent Not Appearing in Web UI
|
|
215
|
+
|
|
216
|
+
1. **Check registration** - File must call `Legate::GlobalDefinitionRegistry.register(definition)`
|
|
217
|
+
2. **Don't run the agent** - The file should only define and register, not execute
|
|
218
|
+
3. **Check registration** - Agents must be registered in `GlobalDefinitionRegistry`
|
|
219
|
+
|
|
220
|
+
### Load Errors
|
|
221
|
+
|
|
222
|
+
If a file fails to load, you'll see:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
WARN: Failed to load tool file /path/to/file.rb: SomeError - error message
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Check the file for syntax errors or missing dependencies.
|
|
229
|
+
|
|
230
|
+
### Wrong require_relative Paths
|
|
231
|
+
|
|
232
|
+
**Don't use `require_relative` for other auto-loaded files.** Since tools load before agents, your agent can reference tools by symbol name without requiring them:
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# DON'T do this:
|
|
236
|
+
require_relative '../tools/my_tool' # Wrong!
|
|
237
|
+
|
|
238
|
+
# DO this:
|
|
239
|
+
a.use_tool :my_tool # The tool is already loaded
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Running from Different Directories
|
|
243
|
+
|
|
244
|
+
The auto-loader scans relative to the current working directory. Always run `legate web start` from your project root:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
cd /path/to/your/project
|
|
248
|
+
bundle exec legate web start
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Example Project
|
|
252
|
+
|
|
253
|
+
Here's a complete example project structure:
|
|
254
|
+
|
|
255
|
+
```
|
|
256
|
+
my_legate_project/
|
|
257
|
+
├── Gemfile
|
|
258
|
+
│ # source 'https://rubygems.org'
|
|
259
|
+
│ # gem 'legate'
|
|
260
|
+
│
|
|
261
|
+
├── .env
|
|
262
|
+
│ # GOOGLE_API_KEY=your_api_key_here
|
|
263
|
+
│
|
|
264
|
+
├── lib/
|
|
265
|
+
│ ├── tools/
|
|
266
|
+
│ │ └── math_helper_tool.rb
|
|
267
|
+
│ └── agents/
|
|
268
|
+
│ └── math_tutor_agent.rb
|
|
269
|
+
│
|
|
270
|
+
└── README.md
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
**`lib/tools/math_helper_tool.rb`:**
|
|
274
|
+
```ruby
|
|
275
|
+
require 'legate/tool'
|
|
276
|
+
|
|
277
|
+
class MathHelperTool < Legate::Tool
|
|
278
|
+
tool_description 'Explains math concepts step by step'
|
|
279
|
+
|
|
280
|
+
parameter :problem, type: :string, required: true,
|
|
281
|
+
description: 'The math problem or concept to explain'
|
|
282
|
+
|
|
283
|
+
private
|
|
284
|
+
|
|
285
|
+
def perform_execution(params, context)
|
|
286
|
+
{ status: :success, result: "Let me explain: #{params[:problem]}" }
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
Legate::GlobalToolManager.register_tool(MathHelperTool)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**`lib/agents/math_tutor_agent.rb`:**
|
|
294
|
+
```ruby
|
|
295
|
+
require 'legate'
|
|
296
|
+
|
|
297
|
+
definition = Legate::AgentDefinition.new.define do |a|
|
|
298
|
+
a.name :math_tutor
|
|
299
|
+
a.description 'A patient math tutor that explains concepts'
|
|
300
|
+
a.instruction 'You are a patient math tutor. Use the math_helper_tool to explain concepts.'
|
|
301
|
+
a.use_tool :math_helper_tool
|
|
302
|
+
a.use_tool :calculator
|
|
303
|
+
a.model_name 'gemini-2.0-flash'
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
Legate::GlobalDefinitionRegistry.register(definition)
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**Start the server:**
|
|
310
|
+
```bash
|
|
311
|
+
cd my_legate_project
|
|
312
|
+
bundle install
|
|
313
|
+
bundle exec legate web start
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Your math tutor agent will automatically appear in the Web UI!
|
|
317
|
+
|
|
318
|
+
## Related Documentation
|
|
319
|
+
|
|
320
|
+
- [AI Code Generators](ai_code_generators) - Generate tools and agents with AI
|
|
321
|
+
- [Built-in Tools](../tools/legate_built_in_tools) - Available built-in tools
|
|
322
|
+
- [Agent Lifecycle](../core_concepts/legate_agent_lifecycle) - How agents work
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Configuring an Agent for Inbound Webhooks
|
|
2
|
+
|
|
3
|
+
This guide details how to configure a specific Legate Agent Definition to be triggered by external systems using the Legate's inbound webhook feature.
|
|
4
|
+
|
|
5
|
+
**Prerequisite:** Ensure the Legate Webhook Listener and the Dynamic Agent Handler are enabled in your global Legate configuration. See the main [`webhooks`](./webhooks) documentation for details on setting up `Legate.configure`:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# config/initializers/legate.rb (or similar)
|
|
9
|
+
Legate.configure do |config|
|
|
10
|
+
config.webhooks.listener_enabled = true
|
|
11
|
+
config.webhooks.enable_dynamic_agent_handler = true
|
|
12
|
+
# ... other listener settings (port, base_path) ...
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
With the dynamic handler enabled (defaulting to the route `/agents/:agent_name/trigger` under the configured `base_path`), you can configure individual agents to respond to requests matching this pattern.
|
|
17
|
+
|
|
18
|
+
## Agent Definition Webhook Metadata
|
|
19
|
+
|
|
20
|
+
To make an agent triggerable via the dynamic route, you must define specific metadata within its definition block (`Legate::Agent.define do |a| ... end`).
|
|
21
|
+
|
|
22
|
+
### 1. Enable Webhook Triggering (`webhook_enabled`)
|
|
23
|
+
|
|
24
|
+
This is the master switch for exposing the agent via the dynamic webhook route.
|
|
25
|
+
|
|
26
|
+
* **Required:** Yes, if you want the agent triggerable by webhook.
|
|
27
|
+
* **Type:** Boolean
|
|
28
|
+
* **Default:** `false`
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
Legate::Agent.define do |a|
|
|
32
|
+
a.name :my_webhook_enabled_agent
|
|
33
|
+
# ... other config ...
|
|
34
|
+
|
|
35
|
+
a.webhook_enabled true # MUST be true
|
|
36
|
+
end
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If `webhook_enabled` is not explicitly set to `true`, requests targeting this agent via the dynamic route will result in a `404 Not Found` response.
|
|
40
|
+
|
|
41
|
+
### 2. Define Payload Transformation (`webhook_transformer`)
|
|
42
|
+
|
|
43
|
+
This Proc defines how the incoming webhook request payload is converted into the `user_input` expected by your agent's `run_task` method.
|
|
44
|
+
|
|
45
|
+
* **Required:** Yes, if `webhook_enabled` is `true`.
|
|
46
|
+
* **Type:** `Proc` (or Lambda)
|
|
47
|
+
* **Signature:** `lambda { |request_body| }`
|
|
48
|
+
* `request_body`: The parsed request body (typically a Hash if the request Content-Type was `application/json`, otherwise the raw String body).
|
|
49
|
+
* **Return Value:** The `String` or `Hash` that will be passed as the `user_input` parameter to `agent.run_task` by the background worker.
|
|
50
|
+
* **Error Handling:** If the payload is invalid or missing expected data, the Proc should raise an `Legate::WebhookConfigurationError` with a descriptive message. This will cause the listener to return a `400 Bad Request`.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
Legate::Agent.define do |a|
|
|
54
|
+
# ... name, webhook_enabled true ...
|
|
55
|
+
|
|
56
|
+
a.webhook_transformer ->(request_body) do
|
|
57
|
+
# Example: Expecting JSON: { "event_type": "...", "data": { ... } }
|
|
58
|
+
event_type = request_body['event_type']
|
|
59
|
+
event_data = request_body['data']
|
|
60
|
+
|
|
61
|
+
unless event_type && event_data
|
|
62
|
+
raise Legate::WebhookConfigurationError, "Payload missing 'event_type' or 'data'."
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Construct the input for the agent task
|
|
66
|
+
"Process event '#{event_type}' with data: #{event_data.to_json}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 3. Define Session ID Extraction (`webhook_session_extractor`)
|
|
72
|
+
|
|
73
|
+
This Proc determines the `session_id` to use for the agent task triggered by the webhook. This allows grouping related events into the same agent conversation history.
|
|
74
|
+
|
|
75
|
+
* **Required:** Yes, if `webhook_enabled` is `true`.
|
|
76
|
+
* **Type:** `Proc` (or Lambda)
|
|
77
|
+
* **Signature:** `lambda { |request_body| }`
|
|
78
|
+
* `request_body`: The parsed request body (Hash or String).
|
|
79
|
+
* **Return Value:** A non-empty `String` representing the `session_id`.
|
|
80
|
+
* **Error Handling:** If a suitable ID cannot be extracted, raise `Legate::WebhookConfigurationError`. This results in a `400 Bad Request`.
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
Legate::Agent.define do |a|
|
|
84
|
+
# ... name, webhook_enabled true, transformer ...
|
|
85
|
+
|
|
86
|
+
a.webhook_session_extractor ->(request_body) do
|
|
87
|
+
# Example: Use a resource ID from the payload
|
|
88
|
+
resource_id = request_body.dig('resource', 'id')
|
|
89
|
+
unless resource_id.is_a?(String) && !resource_id.empty?
|
|
90
|
+
raise Legate::WebhookConfigurationError, "Missing or invalid 'resource.id' in payload."
|
|
91
|
+
end
|
|
92
|
+
"resource_session_#{resource_id}" # e.g., "resource_session_proj-abc-123"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4. Configure Validation (`webhook_validator`)
|
|
98
|
+
|
|
99
|
+
Specify how incoming requests should be validated for authenticity. This is highly recommended.
|
|
100
|
+
|
|
101
|
+
* **Required:** No (but strongly recommended).
|
|
102
|
+
* **Type:** `Symbol` (referencing a globally registered validator) or `Proc` (for custom logic).
|
|
103
|
+
* **Default:** `nil` (no validation applied unless a global validator is configured).
|
|
104
|
+
|
|
105
|
+
**Option A: Using a Named Global Validator**
|
|
106
|
+
|
|
107
|
+
First, register a validator globally (see `webhooks.md`):
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
# config/initializers/legate.rb
|
|
111
|
+
Legate.configure do |config|
|
|
112
|
+
# ...
|
|
113
|
+
config.webhooks.register_validator(:hmac_sha256) do |request, secret|
|
|
114
|
+
# ... HMAC validation logic using request and secret ...
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then, reference it in the agent definition:
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
Legate::Agent.define do |a|
|
|
123
|
+
# ... name, webhook_enabled true, transformer, extractor ...
|
|
124
|
+
|
|
125
|
+
a.webhook_validator :hmac_sha256 # Use the named validator
|
|
126
|
+
a.webhook_secret ENV['MY_AGENT_WEBHOOK_SECRET'] # Provide the secret for this agent
|
|
127
|
+
end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Option B: Providing a Custom Proc**
|
|
131
|
+
|
|
132
|
+
Define the validation logic directly within the agent definition.
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
Legate::Agent.define do |a|
|
|
136
|
+
# ... name, webhook_enabled true, transformer, extractor ...
|
|
137
|
+
|
|
138
|
+
a.webhook_validator ->(request, secret) do
|
|
139
|
+
# Example: Check for a specific token in headers or params
|
|
140
|
+
# 'secret' here is the value from a.webhook_secret below
|
|
141
|
+
request.params['auth_token'] == secret || request.env['HTTP_X_AUTH_TOKEN'] == secret
|
|
142
|
+
end
|
|
143
|
+
a.webhook_secret ENV['MY_AGENT_AUTH_TOKEN']
|
|
144
|
+
end
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
* **Validator Proc Signature:** `lambda { |request, secret| }`
|
|
148
|
+
* `request`: The full Rack request object.
|
|
149
|
+
* `secret`: The value defined by `a.webhook_secret` (or `nil`).
|
|
150
|
+
* **Return Value:** `true` if valid, `false` otherwise.
|
|
151
|
+
* **Error Handling:** If validation fails (returns `false`), the listener returns a `401 Unauthorized` response. If the validator Proc itself raises an error, a `500 Internal Server Error` is returned.
|
|
152
|
+
|
|
153
|
+
### 5. Provide Validation Secret (`webhook_secret`)
|
|
154
|
+
|
|
155
|
+
Provides the secret key or token needed by the configured `webhook_validator`.
|
|
156
|
+
|
|
157
|
+
* **Required:** Only if the chosen validator logic requires a secret.
|
|
158
|
+
* **Type:** `String`
|
|
159
|
+
* **Default:** `nil`
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
Legate::Agent.define do |a|
|
|
163
|
+
# ... name, webhook_enabled true, transformer, extractor, validator ...
|
|
164
|
+
|
|
165
|
+
# Use environment variables for secrets!
|
|
166
|
+
a.webhook_secret ENV['MY_AGENT_WEBHOOK_SECRET']
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Example: Fully Configured Agent
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
# app/agents/github_issue_agent.rb
|
|
174
|
+
require 'legate'
|
|
175
|
+
require 'json'
|
|
176
|
+
require 'openssl' # Needed if validator uses it
|
|
177
|
+
|
|
178
|
+
Legate::Agent.define do |a|
|
|
179
|
+
a.name :github_issue_agent
|
|
180
|
+
a.description "Processes updates for GitHub issues via webhooks."
|
|
181
|
+
a.instruction "Analyze the GitHub issue event and associated data."
|
|
182
|
+
# ... add tools, model ...
|
|
183
|
+
|
|
184
|
+
# -- Webhook Settings --
|
|
185
|
+
a.webhook_enabled true
|
|
186
|
+
a.webhook_validator :hmac_sha256 # Assumes :hmac_sha256 is registered globally
|
|
187
|
+
a.webhook_secret ENV['GITHUB_ISSUE_WEBHOOK_SECRET']
|
|
188
|
+
|
|
189
|
+
a.webhook_session_extractor ->(payload) do
|
|
190
|
+
issue_id = payload.dig('issue', 'id')
|
|
191
|
+
repo_id = payload.dig('repository', 'id')
|
|
192
|
+
unless issue_id && repo_id
|
|
193
|
+
raise Legate::WebhookConfigurationError, "Missing issue or repository ID in payload."
|
|
194
|
+
end
|
|
195
|
+
"github_issue_#{repo_id}_#{issue_id}"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
a.webhook_transformer ->(payload) do
|
|
199
|
+
action = payload['action']
|
|
200
|
+
issue_title = payload.dig('issue', 'title')
|
|
201
|
+
issue_body = payload.dig('issue', 'body')
|
|
202
|
+
sender = payload.dig('sender', 'login')
|
|
203
|
+
|
|
204
|
+
unless action && issue_title && sender
|
|
205
|
+
raise Legate::WebhookConfigurationError, "Missing required fields (action, issue.title, sender.login)."
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Return a hash as input for the agent task
|
|
209
|
+
{
|
|
210
|
+
event_type: "github_issue_#{action}",
|
|
211
|
+
title: issue_title,
|
|
212
|
+
body: issue_body,
|
|
213
|
+
triggered_by: sender
|
|
214
|
+
}
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
By defining this metadata, your agent can securely and reliably be triggered by external webhook events, leveraging the Legate's asynchronous processing capabilities.
|