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,950 @@
|
|
|
1
|
+
# File: lib/legate/cli/agent_commands.rb
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'thor'
|
|
5
|
+
require_relative 'base_command'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'yaml'
|
|
8
|
+
require 'fileutils' # For creating directories
|
|
9
|
+
require 'cli/ui' # Correct require
|
|
10
|
+
require_relative 'output_helper' # OutputHelper is included below
|
|
11
|
+
require_relative '../tool_registry'
|
|
12
|
+
require_relative '../agent'
|
|
13
|
+
require_relative '../event'
|
|
14
|
+
require_relative '../session'
|
|
15
|
+
require_relative '../session_service/in_memory'
|
|
16
|
+
require_relative '../global_tool_manager'
|
|
17
|
+
require_relative '../../legate' # For Legate.config, Legate.logger
|
|
18
|
+
|
|
19
|
+
module Legate
|
|
20
|
+
module CLI
|
|
21
|
+
# CLI commands for agent definition management AND temporary execution
|
|
22
|
+
class AgentCommands < BaseCommand
|
|
23
|
+
include OutputHelper
|
|
24
|
+
|
|
25
|
+
# In-memory session service backing the `execute` command. Overridable in tests.
|
|
26
|
+
@@session_service_for_execute = Legate::SessionService::InMemory.new
|
|
27
|
+
|
|
28
|
+
no_commands do
|
|
29
|
+
# --- Existing format_cli_result (for 'execute' command) ---
|
|
30
|
+
def format_cli_result(result_data)
|
|
31
|
+
content_to_display = nil
|
|
32
|
+
is_error = false
|
|
33
|
+
is_pending = false
|
|
34
|
+
status_prefix = ''
|
|
35
|
+
|
|
36
|
+
if result_data.is_a?(Legate::Event)
|
|
37
|
+
if %i[agent tool_result].include?(result_data.role)
|
|
38
|
+
content_to_display = result_data.content
|
|
39
|
+
if content_to_display.is_a?(Hash) && content_to_display.key?(:status)
|
|
40
|
+
is_error = (content_to_display[:status] == :error)
|
|
41
|
+
is_pending = (content_to_display[:status] == :pending)
|
|
42
|
+
status_prefix = '(Nested Result) ' if result_data.role == :agent
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
elsif result_data.is_a?(Hash) && result_data.key?(:status)
|
|
46
|
+
content_to_display = result_data
|
|
47
|
+
is_error = (result_data[:status] == :error)
|
|
48
|
+
is_pending = (result_data[:status] == :pending)
|
|
49
|
+
else
|
|
50
|
+
content_to_display = result_data
|
|
51
|
+
is_error = false
|
|
52
|
+
is_pending = false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if content_to_display.is_a?(Array) && !is_error && !is_pending
|
|
56
|
+
say "#{status_prefix}Multi-Step Result:", :cyan
|
|
57
|
+
any_step_errors = false
|
|
58
|
+
any_step_pending = false
|
|
59
|
+
content_to_display.each_with_index do |step_hash, index|
|
|
60
|
+
if step_hash.is_a?(Hash) && step_hash.key?(:status)
|
|
61
|
+
case step_hash[:status]
|
|
62
|
+
when :success
|
|
63
|
+
say " Step #{index + 1} (Success):", :green
|
|
64
|
+
step_result = step_hash[:result]
|
|
65
|
+
if step_result.is_a?(Hash) && step_result.key?(:status)
|
|
66
|
+
say " Result (Nested): #{step_result.inspect}"
|
|
67
|
+
else
|
|
68
|
+
say " Result: #{step_result}"
|
|
69
|
+
end
|
|
70
|
+
when :pending
|
|
71
|
+
say " Step #{index + 1} (Pending):", :yellow
|
|
72
|
+
say " Job ID: #{step_hash[:job_id]}"
|
|
73
|
+
say " Message: #{step_hash[:message]}" if step_hash[:message]
|
|
74
|
+
any_step_pending = true
|
|
75
|
+
when :error
|
|
76
|
+
say " Step #{index + 1} (Error):", :red
|
|
77
|
+
say " Message: #{step_hash[:error_message]}"
|
|
78
|
+
any_step_errors = true
|
|
79
|
+
else
|
|
80
|
+
say " Step #{index + 1} (Unknown Status): #{step_hash.inspect}", :yellow
|
|
81
|
+
any_step_errors = true
|
|
82
|
+
end
|
|
83
|
+
else
|
|
84
|
+
say " Step #{index + 1} (Unknown Step Format): #{step_hash.inspect}", :yellow
|
|
85
|
+
any_step_errors = true
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
overall_msg = if any_step_errors then 'Completed with errors'
|
|
89
|
+
elsif any_step_pending then 'Completed with pending steps'
|
|
90
|
+
else
|
|
91
|
+
'Completed successfully'
|
|
92
|
+
end
|
|
93
|
+
overall_color = if any_step_errors then :red
|
|
94
|
+
elsif any_step_pending then :yellow
|
|
95
|
+
else
|
|
96
|
+
:green
|
|
97
|
+
end
|
|
98
|
+
say "Overall Plan Status: #{overall_msg}", overall_color
|
|
99
|
+
elsif content_to_display.is_a?(Hash) && content_to_display.key?(:status)
|
|
100
|
+
case content_to_display[:status]
|
|
101
|
+
when :success
|
|
102
|
+
say "#{status_prefix}Success:", :green
|
|
103
|
+
say " Result: #{content_to_display[:result]}"
|
|
104
|
+
when :pending
|
|
105
|
+
say "#{status_prefix}Pending:", :yellow
|
|
106
|
+
say " Job ID: #{content_to_display[:job_id]}"
|
|
107
|
+
say " Message: #{content_to_display[:message]}" if content_to_display[:message]
|
|
108
|
+
when :error
|
|
109
|
+
say "#{status_prefix}Error:", :red
|
|
110
|
+
say " Message: #{content_to_display[:error_message]}"
|
|
111
|
+
else
|
|
112
|
+
say "#{status_prefix}Unknown Status:", :yellow
|
|
113
|
+
say " Data: #{content_to_display.inspect}"
|
|
114
|
+
end
|
|
115
|
+
else
|
|
116
|
+
say "#{status_prefix}Success:", :green
|
|
117
|
+
say " Result: #{content_to_display}"
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
# --- End format_cli_result ---
|
|
121
|
+
|
|
122
|
+
# Recursively extract the innermost :result value for nested :success hashes
|
|
123
|
+
def deep_result_value(val)
|
|
124
|
+
# Always normalize keys to symbols
|
|
125
|
+
if val.is_a?(Hash)
|
|
126
|
+
val = val.transform_keys(&:to_sym)
|
|
127
|
+
# Prefer to recurse into :result if present
|
|
128
|
+
if val.key?(:result)
|
|
129
|
+
return deep_result_value(val[:result])
|
|
130
|
+
elsif val.key?(:plan_details) && val[:plan_details].is_a?(Array) && !val[:plan_details].empty?
|
|
131
|
+
return deep_result_value(val[:plan_details].last)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
val
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# --- formatting helper for CLI UI Chat ---
|
|
138
|
+
def _format_chat_turn_output_cli_ui(event_or_hash, role_override = nil, timestamp = nil)
|
|
139
|
+
event_obj = event_or_hash.is_a?(Legate::Event) ? event_or_hash : nil
|
|
140
|
+
data_to_format = event_obj ? event_obj.content : event_or_hash
|
|
141
|
+
current_role = role_override || (event_obj ? event_obj.role : :agent)
|
|
142
|
+
|
|
143
|
+
# Use provided timestamp or create one if not provided
|
|
144
|
+
formatted_time = timestamp || Time.now.strftime('%H:%M:%S')
|
|
145
|
+
|
|
146
|
+
# Normalize keys to symbols
|
|
147
|
+
data_to_format = data_to_format.transform_keys(&:to_sym) if data_to_format.is_a?(Hash)
|
|
148
|
+
|
|
149
|
+
if current_role == :user
|
|
150
|
+
# Add extra line break for spacing and show timestamp
|
|
151
|
+
::CLI::UI.puts "\n{{blue:You}} {{gray:(#{formatted_time})}}:"
|
|
152
|
+
::CLI::UI.puts " #{data_to_format}\n"
|
|
153
|
+
return
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
if data_to_format.is_a?(Hash) && data_to_format.key?(:status) &&
|
|
157
|
+
data_to_format[:status].to_s.downcase == 'success'
|
|
158
|
+
actual_result = deep_result_value(data_to_format)
|
|
159
|
+
# Add timestamp to the header for agent responses
|
|
160
|
+
::CLI::UI::Frame.open("Agent Response (#{formatted_time})", color: :green) do
|
|
161
|
+
::CLI::UI.puts actual_result.to_s
|
|
162
|
+
end
|
|
163
|
+
::CLI::UI.puts '' # Add extra line break after the response
|
|
164
|
+
return
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
unless data_to_format.is_a?(Hash) && data_to_format.key?(:status)
|
|
168
|
+
::CLI::UI::Frame.open("Agent Response (#{formatted_time})", color: :cyan) do
|
|
169
|
+
::CLI::UI.puts data_to_format.inspect
|
|
170
|
+
end
|
|
171
|
+
::CLI::UI.puts '' # Add extra line break after the response
|
|
172
|
+
return
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
case data_to_format[:status]
|
|
176
|
+
when :error
|
|
177
|
+
title_color = :red
|
|
178
|
+
title_prefix = 'Agent Error'
|
|
179
|
+
message_body_content = data_to_format[:error_message]
|
|
180
|
+
::CLI::UI::Frame.open("#{title_prefix} (#{formatted_time})", color: title_color) do
|
|
181
|
+
::CLI::UI.puts message_body_content
|
|
182
|
+
end
|
|
183
|
+
when :pending
|
|
184
|
+
title_color = :yellow
|
|
185
|
+
title_prefix = 'Agent Pending'
|
|
186
|
+
message_body_content = "Job ID [#{data_to_format[:job_id]}] - #{data_to_format[:message]}"
|
|
187
|
+
::CLI::UI::Frame.open("#{title_prefix} (#{formatted_time})", color: title_color) do
|
|
188
|
+
::CLI::UI.puts message_body_content
|
|
189
|
+
end
|
|
190
|
+
else
|
|
191
|
+
title_color = :magenta
|
|
192
|
+
title_prefix = "Agent (Status: #{data_to_format[:status]})"
|
|
193
|
+
message_body_content = data_to_format.inspect
|
|
194
|
+
::CLI::UI::Frame.open("#{title_prefix} (#{formatted_time})", color: title_color) do
|
|
195
|
+
::CLI::UI.puts message_body_content
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
::CLI::UI.puts '' # Add extra line break after any response
|
|
199
|
+
end
|
|
200
|
+
# --- END _format_chat_turn_output_cli_ui ---
|
|
201
|
+
end # end no_commands
|
|
202
|
+
|
|
203
|
+
# --- Definition Management Commands (Existing - no changes shown for brevity) ---
|
|
204
|
+
desc 'list', 'List all defined agents'
|
|
205
|
+
method_option :json, type: :boolean, default: false,
|
|
206
|
+
desc: 'Output result in JSON format'
|
|
207
|
+
def list
|
|
208
|
+
definitions = Legate::GlobalDefinitionRegistry.all
|
|
209
|
+
if json_mode?
|
|
210
|
+
agents = definitions.sort_by { |name, _| name.to_s }.map do |name, defn|
|
|
211
|
+
{
|
|
212
|
+
name: name.to_s,
|
|
213
|
+
description: defn.description || nil,
|
|
214
|
+
model: (defn.model_name || Legate::Agent::DEFAULT_MODEL).to_s,
|
|
215
|
+
tools: defn.tool_names.to_a.map(&:to_s)
|
|
216
|
+
}
|
|
217
|
+
end
|
|
218
|
+
puts JSON.generate({ agents: agents })
|
|
219
|
+
elsif definitions.empty?
|
|
220
|
+
say 'No agent definitions found.'
|
|
221
|
+
else
|
|
222
|
+
say 'Defined Agents:', :bold
|
|
223
|
+
definitions.sort_by { |name, _| name.to_s }.each do |name, defn|
|
|
224
|
+
description = defn.description || '[No description]'
|
|
225
|
+
tools = defn.tool_names.to_a
|
|
226
|
+
model = (defn.model_name || "#{Legate::Agent::DEFAULT_MODEL} (Default)").to_s
|
|
227
|
+
tools_str = tools.empty? ? 'None' : tools.join(', ')
|
|
228
|
+
say "- #{name}: #{description} (Model: #{model}, Tools: #{tools_str})"
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
desc 'save NAME', 'Create or update an agent definition'
|
|
234
|
+
method_option :description, type: :string, required: true, desc: 'Agent description'
|
|
235
|
+
method_option :tools, type: :string, aliases: '-t', desc: 'Comma-separated list of tool names (e.g., "echo,calculator")'
|
|
236
|
+
method_option :model, type: :string, desc: "LLM model name (default: #{Legate::Agent::DEFAULT_MODEL})"
|
|
237
|
+
method_option :instruction, type: :string, desc: "Core instructions for the agent's behavior (system prompt)."
|
|
238
|
+
method_option :webhook_enabled, type: :boolean, default: false, desc: 'Enable webhook triggering for this agent.'
|
|
239
|
+
method_option :webhook_secret, type: :string, desc: 'Secret key for webhook validation (if webhook_enabled).'
|
|
240
|
+
method_option :mcp_servers_json, type: :string, desc: 'JSON string of MCP server configurations array.'
|
|
241
|
+
|
|
242
|
+
def save(name)
|
|
243
|
+
name_sym = name.to_sym
|
|
244
|
+
description = options[:description]
|
|
245
|
+
model_to_save = options[:model] && !options[:model].empty? ? options[:model] : Legate::Agent::DEFAULT_MODEL
|
|
246
|
+
instruction_to_save = options[:instruction]
|
|
247
|
+
|
|
248
|
+
selected_tools = []
|
|
249
|
+
valid_tools = Legate::GlobalToolManager.registered_tool_names.map(&:to_s)
|
|
250
|
+
if options[:tools]
|
|
251
|
+
requested_tools = options[:tools].split(',').map(&:strip).reject(&:empty?)
|
|
252
|
+
requested_tools.each do |tool_name|
|
|
253
|
+
if valid_tools.include?(tool_name)
|
|
254
|
+
selected_tools << tool_name unless selected_tools.include?(tool_name)
|
|
255
|
+
else
|
|
256
|
+
say "Warning: Unknown globally registered tool '#{tool_name}', ignoring.", :yellow
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
definition = {
|
|
262
|
+
description: description,
|
|
263
|
+
tools: selected_tools,
|
|
264
|
+
model: model_to_save,
|
|
265
|
+
instruction: instruction_to_save,
|
|
266
|
+
fallback_mode: :error,
|
|
267
|
+
mcp_servers_json: options[:mcp_servers_json] || '[]',
|
|
268
|
+
webhook_enabled: options[:webhook_enabled],
|
|
269
|
+
webhook_secret: options[:webhook_secret]
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
# Build an AgentDefinition object and register it
|
|
273
|
+
agent_def = Legate::AgentDefinition.from_hash(definition.merge(name: name_sym))
|
|
274
|
+
unless agent_def
|
|
275
|
+
say 'Error creating agent definition. Aborting.', :red
|
|
276
|
+
exit(1)
|
|
277
|
+
end
|
|
278
|
+
Legate::GlobalDefinitionRegistry.register(agent_def)
|
|
279
|
+
tools_msg = selected_tools.empty? ? 'None' : selected_tools.join(', ')
|
|
280
|
+
say "Agent definition '#{name}' saved (Model: #{model_to_save}, Tools: #{tools_msg}, Instruction: #{instruction_to_save ? 'Set' : 'Not Set'}).", :green
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
desc 'delete NAME', "Delete an agent's definition"
|
|
284
|
+
def delete(name)
|
|
285
|
+
name_sym = name.to_sym
|
|
286
|
+
definition_exists = Legate::GlobalDefinitionRegistry.definition_exists?(name_sym)
|
|
287
|
+
|
|
288
|
+
unless definition_exists
|
|
289
|
+
say "Error: Agent definition '#{name}' not found.", :red
|
|
290
|
+
exit(1)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
if yes?("Are you sure you want to permanently delete agent definition '#{name}'? [y/N]", :yellow)
|
|
294
|
+
Legate::GlobalDefinitionRegistry.delete_definition(name_sym)
|
|
295
|
+
say "Agent definition '#{name}' deleted successfully.", :green
|
|
296
|
+
else
|
|
297
|
+
say 'Deletion cancelled.', :yellow
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
desc 'generate NAME', 'Generate a new agent definition file'
|
|
302
|
+
method_option :description, type: :string, default: 'A new Legate agent.', desc: 'Agent description'
|
|
303
|
+
method_option :instruction, type: :string, default: 'You are a helpful assistant.', desc: 'Agent instruction (system prompt)'
|
|
304
|
+
method_option :tools, type: :string, aliases: '-t', default: '', desc: 'Comma-separated list of tool names (e.g., "echo,calculator")'
|
|
305
|
+
method_option :model, type: :string, desc: 'LLM model name (uses framework default if blank)'
|
|
306
|
+
method_option :dir, type: :string, default: './agents', desc: 'Directory to save the agent definition file'
|
|
307
|
+
method_option :force, type: :boolean, default: false, desc: 'Overwrite existing file without prompting'
|
|
308
|
+
method_option :webhook_enabled, type: :boolean, default: false, desc: 'Include webhook configuration placeholders'
|
|
309
|
+
def generate(name)
|
|
310
|
+
agent_name_sym = name.to_sym
|
|
311
|
+
dir_path = File.expand_path(options[:dir])
|
|
312
|
+
file_path = File.join(dir_path, "#{name}_agent.rb")
|
|
313
|
+
if File.exist?(file_path) && !options[:force] && !yes?("Agent file '#{file_path}' already exists. Overwrite? [y/N]", :yellow)
|
|
314
|
+
say 'Generation cancelled.', :yellow
|
|
315
|
+
exit(0)
|
|
316
|
+
end
|
|
317
|
+
begin
|
|
318
|
+
FileUtils.mkdir_p(dir_path)
|
|
319
|
+
rescue SystemCallError => e
|
|
320
|
+
say "Error: Could not create directory '#{dir_path}': #{e.message}", :red
|
|
321
|
+
exit(1)
|
|
322
|
+
end
|
|
323
|
+
agent_name_str = name
|
|
324
|
+
description = options[:description]
|
|
325
|
+
instruction = options[:instruction]
|
|
326
|
+
tools_list = options[:tools].split(',').map(&:strip).reject(&:empty?).map(&:to_sym)
|
|
327
|
+
model_str = options[:model]
|
|
328
|
+
webhook_enabled = options[:webhook_enabled]
|
|
329
|
+
code = <<~RUBY
|
|
330
|
+
require 'legate'
|
|
331
|
+
|
|
332
|
+
Legate::Agent.define do |a|
|
|
333
|
+
a.name :#{agent_name_sym}
|
|
334
|
+
a.description "#{description}"
|
|
335
|
+
a.instruction "#{instruction}"
|
|
336
|
+
#{' '}
|
|
337
|
+
RUBY
|
|
338
|
+
if model_str && !model_str.empty?
|
|
339
|
+
code += " # Optional: Specify model (defaults to Legate.config.default_model_name)\n"
|
|
340
|
+
code += " a.model_name '#{model_str}'\n\n"
|
|
341
|
+
else
|
|
342
|
+
code += " # Model will use framework default: #{Legate.config.default_model_name}\n\n"
|
|
343
|
+
end
|
|
344
|
+
code += " # Define tools the agent can use\n"
|
|
345
|
+
if tools_list.empty?
|
|
346
|
+
code += " # a.use_tool :echo # Example\n"
|
|
347
|
+
else
|
|
348
|
+
tools_list.each { |tool| code += " a.use_tool :#{tool}\n" }
|
|
349
|
+
end
|
|
350
|
+
code += "\n"
|
|
351
|
+
if webhook_enabled
|
|
352
|
+
code += <<~WEBHOOK
|
|
353
|
+
# --- Webhook Configuration ---#{' '}
|
|
354
|
+
# This agent can be triggered by POST /webhooks/agents/#{agent_name_sym}/trigger
|
|
355
|
+
# (Assuming default listener base_path and dynamic_agent_route_pattern in Legate.configure)
|
|
356
|
+
|
|
357
|
+
a.webhook_enabled true
|
|
358
|
+
|
|
359
|
+
a.webhook_transformer ->(request_body) do#{' '}
|
|
360
|
+
raise NotImplementedError, "Please implement the webhook_transformer proc to convert request_body into agent user_input."
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
a.webhook_session_extractor ->(request_body) do
|
|
364
|
+
raise NotImplementedError, "Please implement the webhook_session_extractor proc to extract a session ID."
|
|
365
|
+
end
|
|
366
|
+
WEBHOOK
|
|
367
|
+
end
|
|
368
|
+
code += "end\n"
|
|
369
|
+
begin
|
|
370
|
+
File.write(file_path, code)
|
|
371
|
+
say "Agent definition file created at '#{file_path}'", :green
|
|
372
|
+
if webhook_enabled
|
|
373
|
+
say "\nWebhook configuration placeholders added. Please implement the required transformer and extractor procs.", :yellow
|
|
374
|
+
say 'Remember to configure validation and secrets for production use!', :yellow
|
|
375
|
+
end
|
|
376
|
+
rescue SystemCallError => e
|
|
377
|
+
say "Error: Could not write file '#{file_path}': #{e.message}", :red
|
|
378
|
+
exit(1)
|
|
379
|
+
end
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
desc 'ai_generate', 'Generate agent definition code using AI from a natural language description'
|
|
383
|
+
long_desc <<-LONGDESC
|
|
384
|
+
Uses AI (Gemini LLM) to generate a production-ready agent definition based on
|
|
385
|
+
your natural language description.
|
|
386
|
+
|
|
387
|
+
Input sources (in priority order):
|
|
388
|
+
1. --description / -d : Inline description
|
|
389
|
+
2. --prompt-file / -f : Read description from a file
|
|
390
|
+
3. stdin : Pipe description via stdin (auto-enables stdout output)
|
|
391
|
+
|
|
392
|
+
Output destinations:
|
|
393
|
+
- Default: Writes to ./<suggested_name>_agent.rb
|
|
394
|
+
- --output / -o : Custom output file path
|
|
395
|
+
- --stdout : Output to stdout instead of file
|
|
396
|
+
- When reading from stdin: Auto-outputs to stdout
|
|
397
|
+
|
|
398
|
+
Examples:
|
|
399
|
+
legate agent ai_generate -d "An agent that helps with customer support"
|
|
400
|
+
legate agent ai_generate -f prompt.txt -o ./agents/support_agent.rb
|
|
401
|
+
echo "A hello world agent" | legate agent ai_generate
|
|
402
|
+
echo "A calculator agent" | legate agent ai_generate > calc_agent.rb
|
|
403
|
+
|
|
404
|
+
Requires GOOGLE_API_KEY environment variable to be set.
|
|
405
|
+
LONGDESC
|
|
406
|
+
method_option :description, aliases: '-d', type: :string, desc: 'Description of the agent to generate'
|
|
407
|
+
method_option :prompt_file, aliases: '-f', type: :string, desc: 'Read description from a file'
|
|
408
|
+
method_option :output, aliases: '-o', type: :string, desc: 'Output file path (default: ./<suggested_name>_agent.rb)'
|
|
409
|
+
method_option :stdout, type: :boolean, default: false, desc: 'Output to stdout instead of file'
|
|
410
|
+
method_option :force, type: :boolean, default: false, desc: 'Overwrite existing file without prompting'
|
|
411
|
+
def ai_generate
|
|
412
|
+
require_relative '../generators'
|
|
413
|
+
|
|
414
|
+
description = nil
|
|
415
|
+
from_stdin = false
|
|
416
|
+
|
|
417
|
+
# Priority: --description > --prompt-file > stdin
|
|
418
|
+
if options[:description] && !options[:description].strip.empty?
|
|
419
|
+
description = options[:description].strip
|
|
420
|
+
elsif options[:prompt_file]
|
|
421
|
+
unless File.exist?(options[:prompt_file])
|
|
422
|
+
say "Error: Prompt file '#{options[:prompt_file]}' not found.", :red
|
|
423
|
+
exit(1)
|
|
424
|
+
end
|
|
425
|
+
description = File.read(options[:prompt_file]).strip
|
|
426
|
+
elsif !$stdin.tty?
|
|
427
|
+
# Reading from stdin (piped input)
|
|
428
|
+
description = $stdin.read.strip
|
|
429
|
+
from_stdin = true
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
if description.nil? || description.empty?
|
|
433
|
+
say 'Error: No description provided. Use --description, --prompt-file, or pipe via stdin.', :red
|
|
434
|
+
exit(1)
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# Determine output mode
|
|
438
|
+
output_to_stdout = options[:stdout] || from_stdin
|
|
439
|
+
|
|
440
|
+
say 'Generating agent code via AI...', :cyan unless output_to_stdout
|
|
441
|
+
|
|
442
|
+
begin
|
|
443
|
+
result = Legate::Generators::AgentGenerator.generate(description: description)
|
|
444
|
+
code = result[:code]
|
|
445
|
+
suggested_name = result[:suggested_name]
|
|
446
|
+
|
|
447
|
+
if output_to_stdout
|
|
448
|
+
puts code
|
|
449
|
+
else
|
|
450
|
+
# Write to file
|
|
451
|
+
file_path = options[:output] || "./#{suggested_name}_agent.rb"
|
|
452
|
+
|
|
453
|
+
if File.exist?(file_path) && !options[:force] && !yes?("File '#{file_path}' already exists. Overwrite? [y/N]", :yellow)
|
|
454
|
+
say 'Generation cancelled.', :yellow
|
|
455
|
+
exit(0)
|
|
456
|
+
end
|
|
457
|
+
|
|
458
|
+
File.write(file_path, code)
|
|
459
|
+
say "Agent definition generated and saved to '#{file_path}'", :green
|
|
460
|
+
say " Suggested name: #{suggested_name}", :cyan
|
|
461
|
+
end
|
|
462
|
+
rescue Legate::Generators::AgentGenerator::ApiKeyMissingError => e
|
|
463
|
+
say "Error: #{e.message}", :red
|
|
464
|
+
exit(1)
|
|
465
|
+
rescue Legate::Generators::AgentGenerator::ApiError => e
|
|
466
|
+
say "Error: #{e.message}", :red
|
|
467
|
+
exit(1)
|
|
468
|
+
rescue Legate::Generators::AgentGenerator::GenerationError => e
|
|
469
|
+
say "Error: #{e.message}", :red
|
|
470
|
+
exit(1)
|
|
471
|
+
rescue StandardError => e
|
|
472
|
+
say "Unexpected error: #{e.class} - #{e.message}", :red
|
|
473
|
+
exit(1)
|
|
474
|
+
end
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
desc 'start NAME', 'Verify agent definition loading and start (Ephemeral)'
|
|
478
|
+
long_desc <<-LONGDESC
|
|
479
|
+
Loads agent definition, instantiates agent, starts agent runtime state,
|
|
480
|
+
verifies all components loaded correctly, prints details & exits.
|
|
481
|
+
This is a diagnostic tool to verify agent definition loads properly.
|
|
482
|
+
Use 'execute' or 'chat' command to run an actual task with the agent.
|
|
483
|
+
LONGDESC
|
|
484
|
+
method_option :quiet, type: :boolean, default: false, aliases: '-q',
|
|
485
|
+
desc: 'Suppress status messages, only output result'
|
|
486
|
+
method_option :json, type: :boolean, default: false,
|
|
487
|
+
desc: 'Output result in JSON format (implies --quiet)'
|
|
488
|
+
def start(name)
|
|
489
|
+
# Suppress all logging in JSON mode for clean output
|
|
490
|
+
Legate.logger.level = Logger::FATAL if json_mode?
|
|
491
|
+
|
|
492
|
+
name_sym = name.to_sym
|
|
493
|
+
status_message("Loading agent '#{name}'...")
|
|
494
|
+
|
|
495
|
+
# First check the global registry
|
|
496
|
+
agent_definition_object = Legate::GlobalDefinitionRegistry.find(name_sym)
|
|
497
|
+
|
|
498
|
+
unless agent_definition_object
|
|
499
|
+
output_error("Agent definition '#{name}' not found.", metadata: { agent: name })
|
|
500
|
+
exit(1)
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
agent = nil
|
|
504
|
+
result_data = nil
|
|
505
|
+
begin
|
|
506
|
+
# Pass the definition object directly. Session service will use global default.
|
|
507
|
+
agent = Legate::Agent.new(definition: agent_definition_object)
|
|
508
|
+
|
|
509
|
+
# Tool loading is now handled by Legate::Agent#initialize via the definition.
|
|
510
|
+
loaded_tool_names = agent.tools.map(&:name)
|
|
511
|
+
defined_tool_names = agent_definition_object.tool_names.to_a
|
|
512
|
+
missing_tools = defined_tool_names - loaded_tool_names
|
|
513
|
+
|
|
514
|
+
status_message(" - Agent uses model: #{agent.model_name}", :cyan)
|
|
515
|
+
status_message(" - Agent instruction: #{agent.instruction.inspect}", :cyan)
|
|
516
|
+
status_message(" - Warning: Tools defined but not loaded/found: [#{missing_tools.join(', ')}]", :yellow) unless missing_tools.empty?
|
|
517
|
+
status_message(" - Loaded tools: [#{loaded_tool_names.join(', ')}]", :cyan)
|
|
518
|
+
|
|
519
|
+
status_message(' - Starting agent runtime...', :cyan)
|
|
520
|
+
agent.start
|
|
521
|
+
status_message('started.', :cyan)
|
|
522
|
+
status_message("\nAgent '#{name}' is ready.", :green)
|
|
523
|
+
|
|
524
|
+
result_data = {
|
|
525
|
+
status: 'ready',
|
|
526
|
+
agent: name,
|
|
527
|
+
model: agent.model_name,
|
|
528
|
+
tools: loaded_tool_names.map(&:to_s),
|
|
529
|
+
missing_tools: missing_tools.map(&:to_s)
|
|
530
|
+
}
|
|
531
|
+
rescue StandardError => e
|
|
532
|
+
output_error(e, metadata: { agent: name })
|
|
533
|
+
puts e.backtrace.first(5).join("\n") unless json_mode?
|
|
534
|
+
exit(1)
|
|
535
|
+
ensure
|
|
536
|
+
if agent&.running?
|
|
537
|
+
status_message(' - Stopping agent runtime...', :cyan)
|
|
538
|
+
agent.stop
|
|
539
|
+
status_message('stopped.', :cyan)
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
|
|
543
|
+
# Output final result in JSON mode
|
|
544
|
+
return unless json_mode? && result_data
|
|
545
|
+
|
|
546
|
+
puts JSON.generate(result_data)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
desc 'stop NAME', 'Stop a persistent agent'
|
|
550
|
+
long_desc <<-LONGDESC
|
|
551
|
+
Stops a persistent agent by updating its status in the definition registry.
|
|
552
|
+
|
|
553
|
+
This command marks the agent as 'stopped'. If the agent is running
|
|
554
|
+
in a web server process, it will be stopped the next time the server checks
|
|
555
|
+
the agent status, or you can restart the web server.
|
|
556
|
+
|
|
557
|
+
Note: This command updates the persistent_status in the registry. If no web server
|
|
558
|
+
is running the agent, this simply ensures the status is set to 'stopped'.
|
|
559
|
+
LONGDESC
|
|
560
|
+
method_option :force, type: :boolean, default: false, desc: 'Force stop without confirmation'
|
|
561
|
+
method_option :quiet, type: :boolean, default: false, aliases: '-q',
|
|
562
|
+
desc: 'Suppress status messages, only output result'
|
|
563
|
+
method_option :json, type: :boolean, default: false,
|
|
564
|
+
desc: 'Output result in JSON format (implies --quiet)'
|
|
565
|
+
def stop(name)
|
|
566
|
+
# Suppress all logging in JSON mode for clean output
|
|
567
|
+
Legate.logger.level = Logger::FATAL if json_mode?
|
|
568
|
+
|
|
569
|
+
name_sym = name.to_sym
|
|
570
|
+
status_message("Stopping agent '#{name}'...")
|
|
571
|
+
|
|
572
|
+
# Load definition from registry
|
|
573
|
+
definition = Legate::GlobalDefinitionRegistry.get_definition(name_sym)
|
|
574
|
+
|
|
575
|
+
unless definition
|
|
576
|
+
output_error("Agent definition '#{name}' not found.", metadata: { agent: name })
|
|
577
|
+
exit(1)
|
|
578
|
+
end
|
|
579
|
+
|
|
580
|
+
# Check current status
|
|
581
|
+
current_status = definition[:persistent_status] || 'stopped'
|
|
582
|
+
if current_status == 'stopped'
|
|
583
|
+
if json_mode?
|
|
584
|
+
puts JSON.generate({ status: 'already_stopped', agent: name })
|
|
585
|
+
else
|
|
586
|
+
say "Agent '#{name}' is already stopped.", :yellow
|
|
587
|
+
end
|
|
588
|
+
return
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
# Confirm if not forced (skip in quiet/json mode)
|
|
592
|
+
if !(options[:force] || quiet_mode?) && !yes?("Agent '#{name}' is currently marked as '#{current_status}'. Stop it? [y/N]", :yellow)
|
|
593
|
+
say 'Stop cancelled.', :yellow
|
|
594
|
+
return
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# Update the persistent_status to stopped
|
|
598
|
+
Legate::GlobalDefinitionRegistry.update_definition(name_sym, { persistent_status: 'stopped' })
|
|
599
|
+
|
|
600
|
+
if json_mode?
|
|
601
|
+
puts JSON.generate({ status: 'stopped', agent: name, previous_status: current_status })
|
|
602
|
+
else
|
|
603
|
+
say "Agent '#{name}' has been marked as stopped.", :green
|
|
604
|
+
say ' Note: If running in a web server, the agent will stop on next status check or server restart.', :cyan
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
desc 'status NAME', 'Check the status of a persistent agent'
|
|
609
|
+
long_desc <<-LONGDESC
|
|
610
|
+
Shows the current status of a persistent agent from the definition store.
|
|
611
|
+
|
|
612
|
+
Displays the agent's persistent_status (running, stopped, etc.) along with
|
|
613
|
+
basic agent information. This is useful for checking if an agent is currently
|
|
614
|
+
active or has been stopped.
|
|
615
|
+
LONGDESC
|
|
616
|
+
method_option :quiet, type: :boolean, default: false, aliases: '-q',
|
|
617
|
+
desc: 'Suppress status messages, only output result'
|
|
618
|
+
method_option :json, type: :boolean, default: false,
|
|
619
|
+
desc: 'Output result in JSON format (implies --quiet)'
|
|
620
|
+
def status(name)
|
|
621
|
+
# Suppress all logging in JSON mode for clean output
|
|
622
|
+
Legate.logger.level = Logger::FATAL if json_mode?
|
|
623
|
+
|
|
624
|
+
name_sym = name.to_sym
|
|
625
|
+
status_message("Checking status of agent '#{name}'...")
|
|
626
|
+
|
|
627
|
+
# Load definition from registry
|
|
628
|
+
definition = Legate::GlobalDefinitionRegistry.get_definition(name_sym)
|
|
629
|
+
|
|
630
|
+
unless definition
|
|
631
|
+
output_error("Agent definition '#{name}' not found.", metadata: { agent: name })
|
|
632
|
+
exit(1)
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
persistent_status = definition[:persistent_status] || 'stopped'
|
|
636
|
+
model = definition[:model] || Legate::Agent::DEFAULT_MODEL
|
|
637
|
+
description = definition[:description] || '[No description]'
|
|
638
|
+
tools = definition[:tools] || []
|
|
639
|
+
|
|
640
|
+
if json_mode?
|
|
641
|
+
puts JSON.generate({
|
|
642
|
+
agent: name,
|
|
643
|
+
status: persistent_status,
|
|
644
|
+
model: model,
|
|
645
|
+
description: description,
|
|
646
|
+
tools: tools
|
|
647
|
+
})
|
|
648
|
+
else
|
|
649
|
+
say "Agent: #{name}", :bold
|
|
650
|
+
case persistent_status
|
|
651
|
+
when 'running'
|
|
652
|
+
say " Status: #{persistent_status}", :green
|
|
653
|
+
when 'stopped'
|
|
654
|
+
say " Status: #{persistent_status}", :yellow
|
|
655
|
+
else
|
|
656
|
+
say " Status: #{persistent_status}", :cyan
|
|
657
|
+
end
|
|
658
|
+
say " Model: #{model}"
|
|
659
|
+
say " Description: #{description}"
|
|
660
|
+
say " Tools: #{tools.empty? ? 'None' : tools.join(', ')}"
|
|
661
|
+
end
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
desc 'export NAME', 'Export an agent definition to YAML or JSON'
|
|
665
|
+
method_option :format, type: :string, default: 'yaml', enum: %w[yaml json], desc: 'Output format (yaml or json)'
|
|
666
|
+
method_option :output, type: :string, aliases: '-o', desc: 'Output file path (default: stdout)'
|
|
667
|
+
def export(name)
|
|
668
|
+
name_sym = name.to_sym
|
|
669
|
+
|
|
670
|
+
# Load definition from registry
|
|
671
|
+
definition = Legate::GlobalDefinitionRegistry.get_definition(name_sym)
|
|
672
|
+
|
|
673
|
+
unless definition
|
|
674
|
+
say "Error: Agent definition '#{name}' not found.", :red
|
|
675
|
+
exit(1)
|
|
676
|
+
end
|
|
677
|
+
|
|
678
|
+
# Clean up internal fields before export
|
|
679
|
+
export_data = definition.dup
|
|
680
|
+
export_data.delete(:persistent_status) # implementation detail
|
|
681
|
+
# Ensure keys are strings for cleaner JSON/YAML
|
|
682
|
+
export_data = export_data.transform_keys(&:to_s)
|
|
683
|
+
|
|
684
|
+
# Format output
|
|
685
|
+
output_content = ''
|
|
686
|
+
case options[:format].downcase
|
|
687
|
+
when 'json'
|
|
688
|
+
output_content = JSON.pretty_generate(export_data)
|
|
689
|
+
when 'yaml'
|
|
690
|
+
output_content = export_data.to_yaml
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
# Write to file or stdout
|
|
694
|
+
if options[:output]
|
|
695
|
+
begin
|
|
696
|
+
File.write(options[:output], output_content)
|
|
697
|
+
say "Agent definition exported to #{options[:output]}", :green
|
|
698
|
+
rescue StandardError => e
|
|
699
|
+
say "Error writing to file: #{e.message}", :red
|
|
700
|
+
exit(1)
|
|
701
|
+
end
|
|
702
|
+
else
|
|
703
|
+
say output_content
|
|
704
|
+
end
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
desc 'execute NAME TASK', 'Execute a task using agent definition (ephemeral)'
|
|
708
|
+
long_desc <<-LONGDESC
|
|
709
|
+
Loads agent definition, instantiates agent, runs TASK within a session context,
|
|
710
|
+
prints the result, stops agent runtime & exits.
|
|
711
|
+
|
|
712
|
+
Use --session-id to continue an existing conversation,
|
|
713
|
+
otherwise starts a new session for this execution. The session ID used will be printed.
|
|
714
|
+
|
|
715
|
+
If a task results in a :pending status (e.g., for an async job),
|
|
716
|
+
the job_id will be printed. Use the check_job_status tool
|
|
717
|
+
in a subsequent call to get the final result.
|
|
718
|
+
LONGDESC
|
|
719
|
+
method_option :session_id, type: :string, desc: 'Optional ID of an existing session to use.'
|
|
720
|
+
method_option :user_id, type: :string, default: 'cli_user', desc: 'User ID for the session'
|
|
721
|
+
method_option :quiet, type: :boolean, default: false, aliases: '-q',
|
|
722
|
+
desc: 'Suppress status messages, only output result'
|
|
723
|
+
method_option :json, type: :boolean, default: false,
|
|
724
|
+
desc: 'Output result in JSON format (implies --quiet)'
|
|
725
|
+
def execute(name, task)
|
|
726
|
+
# Suppress all logging in JSON mode for clean output
|
|
727
|
+
Legate.logger.level = Logger::FATAL if json_mode?
|
|
728
|
+
|
|
729
|
+
name_sym = name.to_sym
|
|
730
|
+
status_message("Loading agent '#{name}' to execute task: \"#{task}\"...")
|
|
731
|
+
agent_definition_object = Legate::GlobalDefinitionRegistry.find(name_sym)
|
|
732
|
+
|
|
733
|
+
unless agent_definition_object
|
|
734
|
+
output_error("Agent definition '#{name}' not found.", metadata: { agent: name })
|
|
735
|
+
exit(1)
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
session_service_instance = @@session_service_for_execute
|
|
739
|
+
session_id_opt = options[:session_id]
|
|
740
|
+
legate_session = nil
|
|
741
|
+
if session_id_opt
|
|
742
|
+
legate_session = session_service_instance.get_session(session_id: session_id_opt)
|
|
743
|
+
if legate_session then status_message("Continuing session: #{session_id_opt}", :cyan)
|
|
744
|
+
else
|
|
745
|
+
status_message("Warning: Session ID '#{session_id_opt}' provided but not found. Starting a new session.", :yellow)
|
|
746
|
+
session_id_opt = nil
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
unless legate_session
|
|
750
|
+
legate_session = session_service_instance.create_session(app_name: name, user_id: options[:user_id])
|
|
751
|
+
session_id_opt = legate_session.id
|
|
752
|
+
status_message("Started new session: #{session_id_opt}", :cyan)
|
|
753
|
+
status_message(' (Using in-memory session storage)', :cyan)
|
|
754
|
+
end
|
|
755
|
+
|
|
756
|
+
agent = nil
|
|
757
|
+
e_outer = nil
|
|
758
|
+
begin
|
|
759
|
+
# Pass the definition object. Session service for the agent instance itself will use global default
|
|
760
|
+
# or the one passed if Legate::Agent.new supported it directly for its own session_service attr.
|
|
761
|
+
# The run_task method will use the session_service_instance passed to it for actual session operations.
|
|
762
|
+
agent = Legate::Agent.new(
|
|
763
|
+
definition: agent_definition_object,
|
|
764
|
+
session_service: session_service_instance
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
status_message(" - Agent uses model: #{agent.model_name}", :cyan)
|
|
768
|
+
|
|
769
|
+
# Tool loading is now handled by Legate::Agent#initialize via the definition.
|
|
770
|
+
loaded_tool_instances = agent.tools
|
|
771
|
+
loaded_tool_names = loaded_tool_instances.map(&:name)
|
|
772
|
+
defined_tool_names = agent_definition_object.tool_names.to_a
|
|
773
|
+
missing_tools = defined_tool_names - loaded_tool_names
|
|
774
|
+
|
|
775
|
+
status_message(" - Warning: Tools defined but not loaded/found: [#{missing_tools.join(', ')}]", :yellow) unless missing_tools.empty?
|
|
776
|
+
status_message(" - Loaded tools: [#{loaded_tool_names.join(', ')}]", :cyan)
|
|
777
|
+
status_message(' - Starting agent runtime...', :cyan)
|
|
778
|
+
agent.start
|
|
779
|
+
status_message('started.', :cyan)
|
|
780
|
+
status_message(" - Running task in session #{session_id_opt}: '#{task}'...", :cyan)
|
|
781
|
+
final_event_or_error = agent.run_task(
|
|
782
|
+
session_id: session_id_opt,
|
|
783
|
+
user_input: task,
|
|
784
|
+
session_service: session_service_instance
|
|
785
|
+
)
|
|
786
|
+
status_message('finished.', :cyan)
|
|
787
|
+
status_message("\nTask Result:", :bold)
|
|
788
|
+
output_result(final_event_or_error, metadata: { session_id: session_id_opt, agent: name }, format_method: :format_cli_result)
|
|
789
|
+
rescue StandardError => e
|
|
790
|
+
e_outer = e
|
|
791
|
+
output_error(e, metadata: { agent: name, session_id: session_id_opt })
|
|
792
|
+
puts e.backtrace.first(5).join("\n") unless json_mode?
|
|
793
|
+
ensure
|
|
794
|
+
if agent&.running?
|
|
795
|
+
status_message(' - Stopping agent runtime...', :cyan)
|
|
796
|
+
agent.stop
|
|
797
|
+
status_message('stopped.', :cyan)
|
|
798
|
+
end
|
|
799
|
+
exit(1) if e_outer
|
|
800
|
+
end
|
|
801
|
+
end # End 'execute' command
|
|
802
|
+
|
|
803
|
+
# --- CHAT COMMAND ---
|
|
804
|
+
desc 'chat AGENT_NAME', 'Interactively chat with an agent definition'
|
|
805
|
+
long_desc <<-LONGDESC
|
|
806
|
+
Starts an interactive chat session with the specified agent.
|
|
807
|
+
The agent definition is loaded from the definition registry.
|
|
808
|
+
|
|
809
|
+
Session Handling:
|
|
810
|
+
- Uses an in-memory session that is lost when the chat ends.
|
|
811
|
+
- Use `--session-id <ID>` to resume a specific existing session. If the ID
|
|
812
|
+
is not found, a new session will be created.
|
|
813
|
+
|
|
814
|
+
Type "exit" or "quit" to end the chat.
|
|
815
|
+
LONGDESC
|
|
816
|
+
method_option :session_id, type: :string, desc: 'ID of an existing session to resume.'
|
|
817
|
+
method_option :user_id, type: :string, default: 'cli_user', desc: 'User ID for the session'
|
|
818
|
+
def chat(agent_name_str)
|
|
819
|
+
::CLI::UI::StdoutRouter.enable
|
|
820
|
+
agent_name_sym = agent_name_str.to_sym
|
|
821
|
+
|
|
822
|
+
agent_definition_object = Legate::GlobalDefinitionRegistry.find(agent_name_sym)
|
|
823
|
+
unless agent_definition_object
|
|
824
|
+
::CLI::UI.puts "{{red:Error: Agent definition '#{agent_name_str}' not found.}}"
|
|
825
|
+
exit(1)
|
|
826
|
+
end
|
|
827
|
+
|
|
828
|
+
# Convert AgentDefinition to hash for display/compatibility
|
|
829
|
+
definition = Legate::GlobalDefinitionRegistry.get_definition(agent_name_sym)
|
|
830
|
+
|
|
831
|
+
session_service_instance = Legate::SessionService::InMemory.new
|
|
832
|
+
|
|
833
|
+
current_session_id = options[:session_id]
|
|
834
|
+
legate_session = nil # Will hold the loaded Legate::Session object
|
|
835
|
+
|
|
836
|
+
::CLI::UI::Frame.open("Chat Session with #{agent_name_str}", color: :blue) do
|
|
837
|
+
::CLI::UI.puts "{{bold:Agent Description:}} #{definition[:description]}"
|
|
838
|
+
if current_session_id
|
|
839
|
+
legate_session = session_service_instance.get_session(session_id: current_session_id)
|
|
840
|
+
if legate_session
|
|
841
|
+
::CLI::UI.puts "{{green:Resuming session:}} #{current_session_id} (in-memory)"
|
|
842
|
+
# --- MODIFIED: Display history if session is loaded and has events ---
|
|
843
|
+
if legate_session.events && !legate_session.events.empty?
|
|
844
|
+
::CLI::UI.puts "\n{{bold:━━━ Recent Conversation History ━━━}}"
|
|
845
|
+
|
|
846
|
+
# Group events by conversation turns
|
|
847
|
+
history_events = legate_session.events.last(20) # Show more history items
|
|
848
|
+
current_date = nil
|
|
849
|
+
|
|
850
|
+
history_events.each do |event|
|
|
851
|
+
# Extract timestamp from event and format it
|
|
852
|
+
event_time = event.timestamp ? Time.at(event.timestamp) : Time.now
|
|
853
|
+
formatted_time = event_time.strftime('%H:%M:%S')
|
|
854
|
+
|
|
855
|
+
# Show date separator if this is a new day
|
|
856
|
+
event_date = event_time.strftime('%Y-%m-%d')
|
|
857
|
+
if current_date != event_date
|
|
858
|
+
current_date = event_date
|
|
859
|
+
::CLI::UI.puts "\n{{bold:┅┅┅ #{event_time.strftime('%B %d, %Y')} ┅┅┅}}"
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
if event.role == :user
|
|
863
|
+
# For user role, event.content is the string message
|
|
864
|
+
_format_chat_turn_output_cli_ui(event.content, :user, formatted_time)
|
|
865
|
+
elsif event.role == :agent
|
|
866
|
+
# For agent role, event.content is the hash {status:, result:, ...}
|
|
867
|
+
_format_chat_turn_output_cli_ui(event.content, :agent, formatted_time)
|
|
868
|
+
end
|
|
869
|
+
# Tool events are generally not shown in simple chat history
|
|
870
|
+
end
|
|
871
|
+
::CLI::UI.puts "{{bold:━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━}}\n"
|
|
872
|
+
else
|
|
873
|
+
::CLI::UI.puts '{{italic:No previous messages in this session.}}'
|
|
874
|
+
end
|
|
875
|
+
# --- END MODIFICATION ---
|
|
876
|
+
else
|
|
877
|
+
::CLI::UI.puts "{{yellow:Warning: Session ID '#{current_session_id}' not found. Starting new session.}}"
|
|
878
|
+
current_session_id = nil # Force new session creation
|
|
879
|
+
end
|
|
880
|
+
end
|
|
881
|
+
unless legate_session # If still no session (either not provided, or provided but not found)
|
|
882
|
+
legate_session = session_service_instance.create_session(app_name: agent_name_str, user_id: options[:user_id])
|
|
883
|
+
current_session_id = legate_session.id # Update current_session_id with the new one
|
|
884
|
+
::CLI::UI.puts "{{green:Started new session:}} #{current_session_id} (in-memory)"
|
|
885
|
+
end
|
|
886
|
+
::CLI::UI.puts "{{gray:Type 'exit' or 'quit' to end the chat.}}"
|
|
887
|
+
end
|
|
888
|
+
::CLI::UI.puts '---'
|
|
889
|
+
|
|
890
|
+
agent = nil
|
|
891
|
+
begin
|
|
892
|
+
# Instantiate the agent with its definition object and the session service
|
|
893
|
+
agent = Legate::Agent.new(
|
|
894
|
+
definition: agent_definition_object,
|
|
895
|
+
session_service: session_service_instance
|
|
896
|
+
)
|
|
897
|
+
# Tool setup is now handled within Legate::Agent#initialize based on the definition object.
|
|
898
|
+
|
|
899
|
+
agent.start
|
|
900
|
+
rescue StandardError => e
|
|
901
|
+
::CLI::UI.puts "{{red:Error initializing or starting agent: #{e.message}}}"
|
|
902
|
+
exit(1)
|
|
903
|
+
end
|
|
904
|
+
|
|
905
|
+
loop do
|
|
906
|
+
user_input = ::CLI::UI::Prompt.ask('You')
|
|
907
|
+
break if user_input.nil?
|
|
908
|
+
|
|
909
|
+
user_input.strip!
|
|
910
|
+
break if %w[exit quit].include?(user_input.downcase)
|
|
911
|
+
next if user_input.empty?
|
|
912
|
+
|
|
913
|
+
final_event = nil
|
|
914
|
+
session_lost_flag = false
|
|
915
|
+
begin
|
|
916
|
+
::CLI::UI::Spinner.spin('Agent thinking...') do |_spinner|
|
|
917
|
+
current_legate_session_for_task = session_service_instance.get_session(session_id: current_session_id)
|
|
918
|
+
unless current_legate_session_for_task
|
|
919
|
+
::CLI::UI.puts "{{red:Error: Session '#{current_session_id}' lost. Please restart chat.}}"
|
|
920
|
+
session_lost_flag = true
|
|
921
|
+
break
|
|
922
|
+
end
|
|
923
|
+
|
|
924
|
+
final_event = agent.run_task(
|
|
925
|
+
session_id: current_session_id,
|
|
926
|
+
user_input: user_input,
|
|
927
|
+
session_service: session_service_instance
|
|
928
|
+
)
|
|
929
|
+
end
|
|
930
|
+
break if session_lost_flag
|
|
931
|
+
|
|
932
|
+
_format_chat_turn_output_cli_ui(final_event)
|
|
933
|
+
rescue StandardError => e
|
|
934
|
+
::CLI::UI::Frame.open('Error During Task', color: :red) do
|
|
935
|
+
::CLI::UI.puts e.message
|
|
936
|
+
end
|
|
937
|
+
end
|
|
938
|
+
end
|
|
939
|
+
ensure
|
|
940
|
+
agent&.stop if agent&.running?
|
|
941
|
+
::CLI::UI.puts '{{yellow:Chat ended.}}'
|
|
942
|
+
end
|
|
943
|
+
# --- END CHAT COMMAND ---
|
|
944
|
+
|
|
945
|
+
def self.exit_on_failure?
|
|
946
|
+
true
|
|
947
|
+
end
|
|
948
|
+
end # End AgentCommands class
|
|
949
|
+
end # End CLI module
|
|
950
|
+
end # End Legate module
|