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,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'sass-embedded'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'logger'
|
|
6
|
+
|
|
7
|
+
module Legate
|
|
8
|
+
module Web
|
|
9
|
+
# Sass compiler for the web interface
|
|
10
|
+
class SassCompiler
|
|
11
|
+
class << self
|
|
12
|
+
def compile_all
|
|
13
|
+
new.compile_all
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@logger = Logger.new($stdout)
|
|
19
|
+
@logger.level = Logger::INFO
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def compile_all
|
|
23
|
+
@logger.info('Compiling Sass files...')
|
|
24
|
+
|
|
25
|
+
# Ensure the output directory exists
|
|
26
|
+
FileUtils.mkdir_p(output_dir)
|
|
27
|
+
|
|
28
|
+
# Find all Sass files
|
|
29
|
+
sass_files = Dir.glob(File.join(source_dir, '**', '*.scss'))
|
|
30
|
+
|
|
31
|
+
sass_files.each do |sass_file|
|
|
32
|
+
compile_file(sass_file)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@logger.info('Sass compilation complete!')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def compile_file(sass_file)
|
|
39
|
+
relative_path = sass_file.sub("#{source_dir}/", '')
|
|
40
|
+
output_file = File.join(output_dir, relative_path.sub('.scss', '.css'))
|
|
41
|
+
|
|
42
|
+
# Ensure the output directory exists
|
|
43
|
+
FileUtils.mkdir_p(File.dirname(output_file))
|
|
44
|
+
|
|
45
|
+
@logger.info("Compiling #{relative_path} to #{output_file.sub("#{output_dir}/", '')}")
|
|
46
|
+
|
|
47
|
+
begin
|
|
48
|
+
# Compile the Sass file
|
|
49
|
+
input = File.read(sass_file)
|
|
50
|
+
result = Sass.compile_string(input, style: 'expanded', load_paths: [File.dirname(sass_file)])
|
|
51
|
+
|
|
52
|
+
# Write the compiled CSS to the output file
|
|
53
|
+
File.write(output_file, result.css)
|
|
54
|
+
|
|
55
|
+
@logger.info("Successfully compiled #{relative_path}")
|
|
56
|
+
rescue StandardError => e
|
|
57
|
+
@logger.error("Error compiling #{relative_path}: #{e.message}")
|
|
58
|
+
@logger.error(e.backtrace.join("\n"))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def source_dir
|
|
65
|
+
@source_dir ||= File.expand_path('public/styles', __dir__)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def output_dir
|
|
69
|
+
@output_dir ||= File.expand_path('public/css', __dir__)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/ File: lib/legate/web/views/_active_session_info.slim
|
|
2
|
+
/ Renders the Run Details "session" block inside the chat inspector.
|
|
3
|
+
/ Keeps id + hx-swap-oob so session new/switch/delete can update it out-of-band.
|
|
4
|
+
|
|
5
|
+
div#active-session-info.inspector-session(hx-swap-oob="outerHTML")
|
|
6
|
+
- if active_session_details
|
|
7
|
+
.inspector-section
|
|
8
|
+
.inspector-label Session ID
|
|
9
|
+
.inspector-session-id
|
|
10
|
+
span.inspector-session-mono(title=active_session_details.id)= active_session_details.id
|
|
11
|
+
button.inspector-copy(type="button" title="Copy session ID" onclick="navigator.clipboard && navigator.clipboard.writeText('#{active_session_details.id}')")
|
|
12
|
+
i.fas.fa-copy
|
|
13
|
+
.inspector-section
|
|
14
|
+
.inspector-meta-row
|
|
15
|
+
span.inspector-meta-key Created
|
|
16
|
+
span.inspector-meta-val= active_session_details.created_at.strftime("%b %d, %H:%M")
|
|
17
|
+
.inspector-meta-row
|
|
18
|
+
span.inspector-meta-key Last activity
|
|
19
|
+
span.inspector-meta-val= active_session_details.updated_at.strftime("%b %d, %H:%M")
|
|
20
|
+
.inspector-meta-row
|
|
21
|
+
span.inspector-meta-key Events
|
|
22
|
+
span.inspector-meta-val= active_session_details.events&.count || 0
|
|
23
|
+
- else
|
|
24
|
+
.inspector-section
|
|
25
|
+
p.inspector-empty No active session.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/ File: lib/legate/web/views/_activity_list.slim
|
|
2
|
+
/ Partial for rendering the activity stream list
|
|
3
|
+
|
|
4
|
+
- if @recent_activity && @recent_activity.any?
|
|
5
|
+
.activity-list
|
|
6
|
+
- @recent_activity.each do |event|
|
|
7
|
+
.activity-item
|
|
8
|
+
- icon_class = case event[:type]
|
|
9
|
+
- when :agent_started then 'fa-play-circle has-text-success'
|
|
10
|
+
- when :agent_stopped then 'fa-stop-circle has-text-danger'
|
|
11
|
+
- when :agent_created then 'fa-plus-circle has-text-info'
|
|
12
|
+
- when :agent_deleted then 'fa-trash has-text-danger'
|
|
13
|
+
- when :task_executed then 'fa-bolt has-text-warning'
|
|
14
|
+
- when :app_started then 'fa-rocket has-text-primary'
|
|
15
|
+
- else 'fa-info-circle has-text-grey'
|
|
16
|
+
span.activity-icon
|
|
17
|
+
i.fas(class=icon_class)
|
|
18
|
+
span.activity-text
|
|
19
|
+
- case event[:type]
|
|
20
|
+
- when :agent_started
|
|
21
|
+
| Agent
|
|
22
|
+
strong= event[:details][:name]
|
|
23
|
+
| started
|
|
24
|
+
- when :agent_stopped
|
|
25
|
+
| Agent
|
|
26
|
+
strong= event[:details][:name]
|
|
27
|
+
| stopped
|
|
28
|
+
- when :agent_created
|
|
29
|
+
| Agent
|
|
30
|
+
strong= event[:details][:name]
|
|
31
|
+
| created
|
|
32
|
+
- when :agent_deleted
|
|
33
|
+
| Agent
|
|
34
|
+
strong= event[:details][:name]
|
|
35
|
+
| deleted
|
|
36
|
+
- when :task_executed
|
|
37
|
+
| Task executed on
|
|
38
|
+
strong= event[:details][:agent]
|
|
39
|
+
- when :app_started
|
|
40
|
+
| Application started
|
|
41
|
+
- else
|
|
42
|
+
= event[:type].to_s.tr('_', ' ').capitalize
|
|
43
|
+
span.activity-time= time_ago_in_words(event[:timestamp])
|
|
44
|
+
- else
|
|
45
|
+
.has-text-centered.has-text-grey.py-5
|
|
46
|
+
span.icon.is-large
|
|
47
|
+
i.fas.fa-clock.fa-2x style="opacity: 0.3;"
|
|
48
|
+
p.mt-3 No recent activity
|
|
49
|
+
p.is-size-7 Activity will appear here as you interact with agents
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/ File: lib/legate/web/views/_agent_card.slim
|
|
2
|
+
/ Renders one agent as a "Legion" command-console card.
|
|
3
|
+
/ Expects 'agent_info' (hash: :name, :description, :running, :configured_tools,
|
|
4
|
+
/ :agent_type, :model) and 'available_tools' (list). Replaces the old table row;
|
|
5
|
+
/ HTMX start/stop/delete swap `closest .agent-card` with this same partial.
|
|
6
|
+
|
|
7
|
+
- agent_name = agent_info[:name]
|
|
8
|
+
- encoded_agent_name = URI.encode_www_form_component(agent_name.to_s).gsub('+', '%20')
|
|
9
|
+
- safe_agent_id = agent_name.to_s.gsub(/[^a-zA-Z0-9_-]/, '-')
|
|
10
|
+
- is_running = agent_info[:running]
|
|
11
|
+
- model_display = (agent_info[:model] && !agent_info[:model].to_s.empty?) ? agent_info[:model] : Legate::Agent::DEFAULT_MODEL
|
|
12
|
+
- card_id = "agent-card-#{safe_agent_id}"
|
|
13
|
+
- is_new_card = locals.fetch(:agent_info, {}).fetch(:is_new, false)
|
|
14
|
+
- agent_type = agent_info[:agent_type]&.to_sym || :llm
|
|
15
|
+
- configured_tools = agent_info[:configured_tools] || []
|
|
16
|
+
- find_tool_desc = lambda { |name| available_tools&.find { |t| t[:name].to_s == name.to_s }&.[](:description) || name.to_s }
|
|
17
|
+
- type_label = { sequential: 'Sequential', parallel: 'Parallel', loop: 'Loop' }[agent_type] || 'LLM'
|
|
18
|
+
- search_blob = [agent_name, agent_info[:description], configured_tools.join(' '), model_display].join(' ').downcase
|
|
19
|
+
|
|
20
|
+
article.agent-card(id=card_id data-agent-name=agent_name data-search=search_blob class=("is-running" if is_running) class=("is-newly-added" if is_new_card))
|
|
21
|
+
header.agent-card-head
|
|
22
|
+
.agent-card-titles
|
|
23
|
+
a.agent-card-name(href="/agents/#{encoded_agent_name}")= agent_name
|
|
24
|
+
span.agent-type-chip= type_label
|
|
25
|
+
span.status-pill(class=(is_running ? 'is-running' : 'is-idle'))
|
|
26
|
+
span.status-dot
|
|
27
|
+
span= is_running ? 'Running' : 'Idle'
|
|
28
|
+
|
|
29
|
+
p.agent-card-desc= agent_info[:description]
|
|
30
|
+
|
|
31
|
+
.agent-card-meta
|
|
32
|
+
span.mono-chip.model-chip(title="Language model")
|
|
33
|
+
span.icon.is-small
|
|
34
|
+
i.fas.fa-microchip
|
|
35
|
+
span= model_display
|
|
36
|
+
|
|
37
|
+
.agent-card-tools
|
|
38
|
+
- if configured_tools.any?
|
|
39
|
+
- configured_tools.each do |tool_name|
|
|
40
|
+
a.tool-chip(href="/tools/#{tool_name}" title=find_tool_desc.call(tool_name))= tool_name
|
|
41
|
+
- else
|
|
42
|
+
span.tool-chip.is-empty no tools configured
|
|
43
|
+
|
|
44
|
+
footer.agent-card-foot
|
|
45
|
+
- unless is_running
|
|
46
|
+
button.legion-action.is-run(type="button" title="Start agent" hx-post="/agents/#{encoded_agent_name}/start" hx-target="closest .agent-card" hx-swap="outerHTML")
|
|
47
|
+
i.fas.fa-play
|
|
48
|
+
span Run
|
|
49
|
+
- if is_running
|
|
50
|
+
button.legion-action.is-stop(type="button" title="Stop agent" hx-post="/agents/#{encoded_agent_name}/stop" hx-target="closest .agent-card" hx-swap="outerHTML")
|
|
51
|
+
i.fas.fa-stop
|
|
52
|
+
span Stop
|
|
53
|
+
a.legion-action.is-icon.is-details(href="/agents/#{encoded_agent_name}" title="View details")
|
|
54
|
+
i.fas.fa-external-link-alt
|
|
55
|
+
button.legion-action.is-icon.is-danger(type="button" title="Delete agent" hx-delete="/agents/#{encoded_agent_name}" hx-target="closest .agent-card" hx-swap="outerHTML" hx-confirm="Are you sure you want to delete '#{agent_name}'?")
|
|
56
|
+
i.fas.fa-trash-alt
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/ File: lib/legate/web/views/_agent_generator_modal.slim
|
|
2
|
+
/ AI agent generator: describe → review editable fields → Add to Legion (live).
|
|
3
|
+
/ Generates a STRUCTURED definition (no file/restart); .rb export kept for power users.
|
|
4
|
+
|
|
5
|
+
#agent-generator-modal.modal
|
|
6
|
+
.modal-background onclick="closeAgentGeneratorModal()"
|
|
7
|
+
.modal-card style="width: 92%; max-width: 920px; max-height: 92vh;"
|
|
8
|
+
header.modal-card-head
|
|
9
|
+
p.modal-card-title
|
|
10
|
+
span.icon.mr-2
|
|
11
|
+
i.fas.fa-wand-magic-sparkles
|
|
12
|
+
| Generate Agent with AI
|
|
13
|
+
button.delete aria-label="close" onclick="closeAgentGeneratorModal()"
|
|
14
|
+
|
|
15
|
+
section.modal-card-body style="display: flex; flex-direction: column; gap: 1.25rem;"
|
|
16
|
+
/ Input
|
|
17
|
+
#agent-generator-input-section
|
|
18
|
+
.field
|
|
19
|
+
label.label
|
|
20
|
+
span.icon.is-small.mr-1
|
|
21
|
+
i.fas.fa-comment-dots
|
|
22
|
+
| Describe your agent
|
|
23
|
+
.control
|
|
24
|
+
textarea#agent-generator-description.textarea(
|
|
25
|
+
rows="4"
|
|
26
|
+
placeholder="e.g. 'An agent that summarizes customer feedback', 'A support assistant that can echo and look things up'"
|
|
27
|
+
)
|
|
28
|
+
p.help Describe what the agent should do. The AI fills in a configuration you can review and tweak before adding it.
|
|
29
|
+
.field
|
|
30
|
+
.control
|
|
31
|
+
button#agent-generator-btn.button.is-primary(onclick="generateAgentDefinition()")
|
|
32
|
+
span.icon
|
|
33
|
+
i.fas.fa-wand-magic-sparkles
|
|
34
|
+
span Generate
|
|
35
|
+
span#agent-generator-loading.ml-3 style="display: none;"
|
|
36
|
+
span.icon.has-text-primary
|
|
37
|
+
i.fas.fa-spinner.fa-spin
|
|
38
|
+
span.has-text-grey Generating…
|
|
39
|
+
|
|
40
|
+
/ Error
|
|
41
|
+
#agent-generator-error.notification.is-danger.is-light style="display: none;"
|
|
42
|
+
button.delete onclick="this.parentElement.style.display='none'"
|
|
43
|
+
span#agent-generator-error-message
|
|
44
|
+
|
|
45
|
+
/ Review (editable, hidden until generated)
|
|
46
|
+
#agent-generator-review-section style="display: none;"
|
|
47
|
+
.agent-gen-review-banner
|
|
48
|
+
span.icon.has-text-primary
|
|
49
|
+
i.fas.fa-circle-check
|
|
50
|
+
span Review and tweak the configuration, then add it to your legion.
|
|
51
|
+
|
|
52
|
+
.field
|
|
53
|
+
label.label Name
|
|
54
|
+
.control.has-icons-left
|
|
55
|
+
input#agent-gen-name.input type="text" placeholder="snake_case_name"
|
|
56
|
+
span.icon.is-small.is-left
|
|
57
|
+
i.fas.fa-robot
|
|
58
|
+
.columns.is-variable.is-3
|
|
59
|
+
.column.is-half
|
|
60
|
+
.field
|
|
61
|
+
label.label Model
|
|
62
|
+
.control
|
|
63
|
+
.select.is-fullwidth
|
|
64
|
+
select#agent-gen-model
|
|
65
|
+
- (@available_models || []).each do |m|
|
|
66
|
+
option value=m = m
|
|
67
|
+
.column.is-half
|
|
68
|
+
.field
|
|
69
|
+
label.label Type
|
|
70
|
+
.control
|
|
71
|
+
.select.is-fullwidth
|
|
72
|
+
select#agent-gen-type
|
|
73
|
+
option value="llm" LLM Agent
|
|
74
|
+
option value="sequential" Sequential Workflow
|
|
75
|
+
option value="parallel" Parallel Workflow
|
|
76
|
+
option value="loop" Loop Workflow
|
|
77
|
+
.field
|
|
78
|
+
label.label Description
|
|
79
|
+
.control
|
|
80
|
+
textarea#agent-gen-description-field.textarea rows="2"
|
|
81
|
+
.field
|
|
82
|
+
label.label Instruction (System Prompt)
|
|
83
|
+
.control
|
|
84
|
+
textarea#agent-gen-instruction.textarea rows="5"
|
|
85
|
+
.field
|
|
86
|
+
label.label Tools
|
|
87
|
+
.agent-gen-tools
|
|
88
|
+
- if @available_tools && !@available_tools.empty?
|
|
89
|
+
- @available_tools.each do |t|
|
|
90
|
+
label.checkbox.agent-gen-tool-item
|
|
91
|
+
input.agent-gen-tool type="checkbox" value=t[:name]
|
|
92
|
+
span.agent-gen-tool-name= t[:name]
|
|
93
|
+
- else
|
|
94
|
+
p.has-text-grey No tools available to select.
|
|
95
|
+
.field
|
|
96
|
+
label.label Output Key (optional)
|
|
97
|
+
.control
|
|
98
|
+
input#agent-gen-output-key.input type="text" placeholder="result_key"
|
|
99
|
+
|
|
100
|
+
/ Suggested tools to create (capabilities no installed tool provides)
|
|
101
|
+
.field#agent-gen-suggested-field style="display: none;"
|
|
102
|
+
label.label
|
|
103
|
+
span.icon.is-small.mr-1
|
|
104
|
+
i.fas.fa-lightbulb
|
|
105
|
+
| Suggested tools to create
|
|
106
|
+
p.help.mb-2 This agent could use capabilities no installed tool provides yet. Build them with the tool generator — they’ll be available to add next time.
|
|
107
|
+
#agent-gen-suggested.agent-gen-suggested
|
|
108
|
+
|
|
109
|
+
details.agent-gen-code-details
|
|
110
|
+
summary
|
|
111
|
+
span.icon.is-small
|
|
112
|
+
i.fas.fa-code
|
|
113
|
+
| View / export generated Ruby
|
|
114
|
+
.buttons.mt-2
|
|
115
|
+
button.button.is-small.is-light(onclick="copyAgentCode()" title="Copy code")
|
|
116
|
+
span.icon.is-small
|
|
117
|
+
i.fas.fa-copy
|
|
118
|
+
span Copy
|
|
119
|
+
button.button.is-small.is-light(onclick="downloadAgentCode()" title="Download as .rb")
|
|
120
|
+
span.icon.is-small
|
|
121
|
+
i.fas.fa-download
|
|
122
|
+
span Export .rb
|
|
123
|
+
.code-preview-container style="max-height: 280px; overflow: auto; border-radius: 6px;"
|
|
124
|
+
pre
|
|
125
|
+
code#agent-generator-code.language-ruby
|
|
126
|
+
|
|
127
|
+
footer.modal-card-foot style="justify-content: space-between;"
|
|
128
|
+
button.button onclick="closeAgentGeneratorModal()" Close
|
|
129
|
+
.buttons.mb-0
|
|
130
|
+
button#agent-gen-regenerate.button.is-light style="display: none;" onclick="generateAgentDefinition()"
|
|
131
|
+
span.icon.is-small
|
|
132
|
+
i.fas.fa-sync
|
|
133
|
+
span Regenerate
|
|
134
|
+
button#agent-gen-add-btn.button.is-primary style="display: none;" onclick="addAgentToLegion()"
|
|
135
|
+
span.icon
|
|
136
|
+
i.fas.fa-circle-plus
|
|
137
|
+
span Add to Legion
|
|
138
|
+
|
|
139
|
+
javascript:
|
|
140
|
+
let generatedAgentCode = '';
|
|
141
|
+
let suggestedAgentName = 'generated_agent';
|
|
142
|
+
|
|
143
|
+
function openAgentGeneratorModal() {
|
|
144
|
+
document.getElementById('agent-generator-modal').classList.add('is-active');
|
|
145
|
+
document.getElementById('agent-generator-description').focus();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function closeAgentGeneratorModal() {
|
|
149
|
+
document.getElementById('agent-generator-modal').classList.remove('is-active');
|
|
150
|
+
document.getElementById('agent-generator-error').style.display = 'none';
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function showAgentGeneratorError(message) {
|
|
154
|
+
document.getElementById('agent-generator-error-message').textContent = message;
|
|
155
|
+
document.getElementById('agent-generator-error').style.display = 'block';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function agSetSelect(id, value) {
|
|
159
|
+
const sel = document.getElementById(id);
|
|
160
|
+
if (!sel) return;
|
|
161
|
+
if (Array.from(sel.options).some(o => o.value === value)) sel.value = value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function generateAgentDefinition() {
|
|
165
|
+
const description = document.getElementById('agent-generator-description').value.trim();
|
|
166
|
+
if (!description) {
|
|
167
|
+
showAgentGeneratorError('Please describe the agent you want to create.');
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const btn = document.getElementById('agent-generator-btn');
|
|
171
|
+
const loading = document.getElementById('agent-generator-loading');
|
|
172
|
+
btn.disabled = true;
|
|
173
|
+
loading.style.display = 'inline';
|
|
174
|
+
document.getElementById('agent-generator-error').style.display = 'none';
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const tokenEl = document.querySelector('meta[name="csrf-token"]');
|
|
178
|
+
const response = await fetch('/agents/generate/definition', {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: {
|
|
181
|
+
'Content-Type': 'application/json',
|
|
182
|
+
'X-CSRF-Token': tokenEl ? tokenEl.content : ''
|
|
183
|
+
},
|
|
184
|
+
body: JSON.stringify({ description })
|
|
185
|
+
});
|
|
186
|
+
const data = await response.json();
|
|
187
|
+
if (!response.ok) throw new Error(data.error || 'Generation failed');
|
|
188
|
+
populateAgentReview(data);
|
|
189
|
+
document.getElementById('agent-generator-review-section').style.display = 'block';
|
|
190
|
+
document.getElementById('agent-gen-add-btn').style.display = '';
|
|
191
|
+
document.getElementById('agent-gen-regenerate').style.display = '';
|
|
192
|
+
} catch (error) {
|
|
193
|
+
showAgentGeneratorError(error.message);
|
|
194
|
+
} finally {
|
|
195
|
+
btn.disabled = false;
|
|
196
|
+
loading.style.display = 'none';
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function populateAgentReview(data) {
|
|
201
|
+
document.getElementById('agent-gen-name').value = data.name || '';
|
|
202
|
+
agSetSelect('agent-gen-model', data.model);
|
|
203
|
+
agSetSelect('agent-gen-type', data.agent_type || 'llm');
|
|
204
|
+
document.getElementById('agent-gen-description-field').value = data.description || '';
|
|
205
|
+
document.getElementById('agent-gen-instruction').value = data.instruction || '';
|
|
206
|
+
document.getElementById('agent-gen-output-key').value = data.output_key || '';
|
|
207
|
+
|
|
208
|
+
const tools = data.tools || [];
|
|
209
|
+
document.querySelectorAll('.agent-gen-tool').forEach(cb => { cb.checked = tools.includes(cb.value); });
|
|
210
|
+
|
|
211
|
+
renderSuggestedTools(data.suggested_tools || []);
|
|
212
|
+
|
|
213
|
+
generatedAgentCode = data.code || '';
|
|
214
|
+
suggestedAgentName = data.name || 'generated_agent';
|
|
215
|
+
const codeEl = document.getElementById('agent-generator-code');
|
|
216
|
+
codeEl.classList.remove('hljs');
|
|
217
|
+
delete codeEl.dataset.highlighted;
|
|
218
|
+
codeEl.textContent = generatedAgentCode;
|
|
219
|
+
if (typeof hljs !== 'undefined') hljs.highlightElement(codeEl);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function renderSuggestedTools(suggested) {
|
|
223
|
+
const field = document.getElementById('agent-gen-suggested-field');
|
|
224
|
+
const list = document.getElementById('agent-gen-suggested');
|
|
225
|
+
list.innerHTML = '';
|
|
226
|
+
if (!suggested.length) { field.style.display = 'none'; return; }
|
|
227
|
+
|
|
228
|
+
suggested.forEach(t => {
|
|
229
|
+
const item = document.createElement('div');
|
|
230
|
+
item.className = 'agent-gen-suggested-item';
|
|
231
|
+
|
|
232
|
+
const info = document.createElement('div');
|
|
233
|
+
info.className = 'agent-gen-suggested-info';
|
|
234
|
+
const nm = document.createElement('span');
|
|
235
|
+
nm.className = 'agent-gen-suggested-name';
|
|
236
|
+
nm.textContent = t.name;
|
|
237
|
+
info.appendChild(nm);
|
|
238
|
+
if (t.description) {
|
|
239
|
+
const d = document.createElement('span');
|
|
240
|
+
d.className = 'agent-gen-suggested-desc';
|
|
241
|
+
d.textContent = t.description;
|
|
242
|
+
info.appendChild(d);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const btn = document.createElement('button');
|
|
246
|
+
btn.type = 'button';
|
|
247
|
+
btn.className = 'button is-small is-primary is-light agent-gen-build-btn';
|
|
248
|
+
btn.innerHTML = '<span class="icon is-small"><i class="fas fa-wand-magic-sparkles"></i></span><span>Build →</span>';
|
|
249
|
+
btn.addEventListener('click', () => buildSuggestedTool(t.name, t.description));
|
|
250
|
+
|
|
251
|
+
item.appendChild(info);
|
|
252
|
+
item.appendChild(btn);
|
|
253
|
+
list.appendChild(item);
|
|
254
|
+
});
|
|
255
|
+
field.style.display = 'block';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function buildSuggestedTool(name, description) {
|
|
259
|
+
if (typeof openToolGeneratorModal !== 'function') {
|
|
260
|
+
showAgentGeneratorError('The tool generator is not available here.');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
// Remember to come back to the agent builder once this tool is installed,
|
|
264
|
+
// so the new tool gets added + checked (it didn't exist at page load).
|
|
265
|
+
window.legateReturnToAgentBuilder = name;
|
|
266
|
+
closeAgentGeneratorModal();
|
|
267
|
+
const field = document.getElementById('tool-generator-description');
|
|
268
|
+
if (field) {
|
|
269
|
+
const base = (description && description.trim()) ? description.trim() : ('A tool named "' + name + '"');
|
|
270
|
+
field.value = base + (name ? ('\n\nName it "' + name + '".') : '');
|
|
271
|
+
}
|
|
272
|
+
openToolGeneratorModal();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Called by the tool modal after a suggested tool is installed live. Adds the
|
|
276
|
+
// newly-registered tool to the review form (checked), drops the suggestion, and
|
|
277
|
+
// re-opens the agent builder so "Add to Legion" includes the new tool.
|
|
278
|
+
function returnToAgentBuilderWithTool(installedName, originalSuggestion) {
|
|
279
|
+
const list = document.querySelector('.agent-gen-tools');
|
|
280
|
+
if (installedName && list) {
|
|
281
|
+
let cb = Array.from(document.querySelectorAll('.agent-gen-tool')).find(c => c.value === installedName);
|
|
282
|
+
if (!cb) {
|
|
283
|
+
const label = document.createElement('label');
|
|
284
|
+
label.className = 'checkbox agent-gen-tool-item';
|
|
285
|
+
cb = document.createElement('input');
|
|
286
|
+
cb.type = 'checkbox';
|
|
287
|
+
cb.className = 'agent-gen-tool';
|
|
288
|
+
cb.value = installedName;
|
|
289
|
+
const span = document.createElement('span');
|
|
290
|
+
span.className = 'agent-gen-tool-name';
|
|
291
|
+
span.textContent = installedName;
|
|
292
|
+
label.appendChild(cb);
|
|
293
|
+
label.appendChild(document.createTextNode(' '));
|
|
294
|
+
label.appendChild(span);
|
|
295
|
+
list.appendChild(label);
|
|
296
|
+
}
|
|
297
|
+
cb.checked = true;
|
|
298
|
+
}
|
|
299
|
+
// Remove the suggestion we just satisfied (match the original suggestion name).
|
|
300
|
+
document.querySelectorAll('.agent-gen-suggested-item').forEach(item => {
|
|
301
|
+
const nm = item.querySelector('.agent-gen-suggested-name');
|
|
302
|
+
if (nm && (nm.textContent === originalSuggestion || nm.textContent === installedName)) item.remove();
|
|
303
|
+
});
|
|
304
|
+
const field = document.getElementById('agent-gen-suggested-field');
|
|
305
|
+
if (field && !document.querySelector('.agent-gen-suggested-item')) field.style.display = 'none';
|
|
306
|
+
|
|
307
|
+
if (typeof openAgentGeneratorModal === 'function') openAgentGeneratorModal();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function addAgentToLegion() {
|
|
311
|
+
const name = document.getElementById('agent-gen-name').value.trim();
|
|
312
|
+
if (!name) { showAgentGeneratorError('The agent needs a name.'); return; }
|
|
313
|
+
|
|
314
|
+
const params = new URLSearchParams();
|
|
315
|
+
params.append('name', name);
|
|
316
|
+
params.append('description', document.getElementById('agent-gen-description-field').value.trim());
|
|
317
|
+
params.append('instruction', document.getElementById('agent-gen-instruction').value);
|
|
318
|
+
params.append('model', document.getElementById('agent-gen-model').value);
|
|
319
|
+
params.append('agent_type', document.getElementById('agent-gen-type').value);
|
|
320
|
+
const ok = document.getElementById('agent-gen-output-key').value.trim();
|
|
321
|
+
if (ok) params.append('output_key', ok);
|
|
322
|
+
document.querySelectorAll('.agent-gen-tool:checked').forEach(cb => params.append('tools[]', cb.value));
|
|
323
|
+
|
|
324
|
+
const addBtn = document.getElementById('agent-gen-add-btn');
|
|
325
|
+
addBtn.classList.add('is-loading');
|
|
326
|
+
try {
|
|
327
|
+
const tokenEl = document.querySelector('meta[name="csrf-token"]');
|
|
328
|
+
const response = await fetch('/agents', {
|
|
329
|
+
method: 'POST',
|
|
330
|
+
headers: {
|
|
331
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
332
|
+
'X-CSRF-Token': tokenEl ? tokenEl.content : ''
|
|
333
|
+
},
|
|
334
|
+
body: params.toString()
|
|
335
|
+
});
|
|
336
|
+
const html = await response.text();
|
|
337
|
+
if (!response.ok) throw new Error('Could not add the agent. ' + (html.replace(/<[^>]*>/g, '').trim() || ''));
|
|
338
|
+
|
|
339
|
+
const grid = document.getElementById('agent-list-grid');
|
|
340
|
+
if (grid) {
|
|
341
|
+
const tmp = document.createElement('div');
|
|
342
|
+
tmp.innerHTML = html;
|
|
343
|
+
const card = tmp.querySelector('.agent-card');
|
|
344
|
+
if (card) grid.appendChild(card);
|
|
345
|
+
}
|
|
346
|
+
const empty = document.getElementById('no-agents-state');
|
|
347
|
+
if (empty) empty.classList.add('is-hidden');
|
|
348
|
+
if (typeof filterAgents === 'function') filterAgents();
|
|
349
|
+
closeAgentGeneratorModal();
|
|
350
|
+
} catch (error) {
|
|
351
|
+
showAgentGeneratorError(error.message);
|
|
352
|
+
} finally {
|
|
353
|
+
addBtn.classList.remove('is-loading');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function copyAgentCode() {
|
|
358
|
+
try {
|
|
359
|
+
await navigator.clipboard.writeText(generatedAgentCode);
|
|
360
|
+
} catch (err) {
|
|
361
|
+
showAgentGeneratorError('Failed to copy to clipboard');
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function downloadAgentCode() {
|
|
366
|
+
const blob = new Blob([generatedAgentCode], { type: 'text/plain' });
|
|
367
|
+
const url = URL.createObjectURL(blob);
|
|
368
|
+
const a = document.createElement('a');
|
|
369
|
+
a.href = url;
|
|
370
|
+
a.download = `${suggestedAgentName}.rb`;
|
|
371
|
+
document.body.appendChild(a);
|
|
372
|
+
a.click();
|
|
373
|
+
document.body.removeChild(a);
|
|
374
|
+
URL.revokeObjectURL(url);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
document.addEventListener('keydown', function(e) {
|
|
378
|
+
if (e.key === 'Escape') {
|
|
379
|
+
const modal = document.getElementById('agent-generator-modal');
|
|
380
|
+
if (modal && modal.classList.contains('is-active')) closeAgentGeneratorModal();
|
|
381
|
+
}
|
|
382
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/ File: lib/legate/web/views/_agent_status_controls.slim
|
|
2
|
+
- agent_name = agent_data.is_a?(Hash) ? agent_data[:name] : agent_data.name
|
|
3
|
+
- is_running = agent_data.is_a?(Hash) ? agent_data[:running] : agent_data.running?
|
|
4
|
+
- tool_count = agent_data.is_a?(Hash) ? (agent_data[:tool_count] || 0) : 0
|
|
5
|
+
- container_id = "agent-status-controls-#{agent_name.gsub(/[^a-zA-Z0-9_-]/, '-')}"
|
|
6
|
+
- encoded_agent_name = URI.encode_www_form_component(agent_name.to_s).gsub('+', '%20')
|
|
7
|
+
|
|
8
|
+
div.agent-header-controls(id=container_id)
|
|
9
|
+
/ Status and action on same row
|
|
10
|
+
.agent-status-action-row
|
|
11
|
+
/ Status badge
|
|
12
|
+
span.tag.is-medium.is-rounded.agent-status-badge class=(is_running ? 'is-success is-running' : 'is-danger')
|
|
13
|
+
span.icon.is-small
|
|
14
|
+
i(class="fas #{is_running ? 'fa-circle' : 'fa-circle'}")
|
|
15
|
+
span= is_running ? 'Running' : 'Stopped'
|
|
16
|
+
|
|
17
|
+
/ Action button - compact
|
|
18
|
+
- if is_running
|
|
19
|
+
button.button.is-danger.agent-action-btn(
|
|
20
|
+
hx-post="/agents/#{encoded_agent_name}/stop/detail"
|
|
21
|
+
hx-target="##{container_id}"
|
|
22
|
+
hx-swap="innerHTML")
|
|
23
|
+
span.icon.is-small.htmx-indicator
|
|
24
|
+
i.fas.fa-spinner.fa-spin
|
|
25
|
+
span.icon.is-small
|
|
26
|
+
i.fas.fa-stop
|
|
27
|
+
span Stop
|
|
28
|
+
- else
|
|
29
|
+
button.button.is-success.agent-action-btn(
|
|
30
|
+
hx-post="/agents/#{encoded_agent_name}/start/detail"
|
|
31
|
+
hx-target="##{container_id}"
|
|
32
|
+
hx-swap="innerHTML")
|
|
33
|
+
span.icon.is-small.htmx-indicator
|
|
34
|
+
i.fas.fa-spinner.fa-spin
|
|
35
|
+
span.icon.is-small
|
|
36
|
+
i.fas.fa-play
|
|
37
|
+
span Start
|
|
38
|
+
|
|
39
|
+
/ Quick actions menu
|
|
40
|
+
.dropdown.is-right.agent-quick-actions-dropdown
|
|
41
|
+
.dropdown-trigger
|
|
42
|
+
button.button.is-light.agent-quick-actions-btn(type="button" aria-haspopup="true" aria-controls="agent-actions-menu")
|
|
43
|
+
span.icon.is-small
|
|
44
|
+
i.fas.fa-ellipsis-v
|
|
45
|
+
.dropdown-menu#agent-actions-menu(role="menu")
|
|
46
|
+
.dropdown-content
|
|
47
|
+
a.dropdown-item(href="#config" data-tab-link="config")
|
|
48
|
+
span.icon.is-small
|
|
49
|
+
i.fas.fa-cog
|
|
50
|
+
span Edit Configuration
|
|
51
|
+
a.dropdown-item(href="/agents/#{encoded_agent_name}/duplicate" hx-post="/agents/#{encoded_agent_name}/duplicate" hx-swap="none")
|
|
52
|
+
span.icon.is-small
|
|
53
|
+
i.fas.fa-copy
|
|
54
|
+
span Duplicate Agent
|
|
55
|
+
a.dropdown-item(href="/agents/#{encoded_agent_name}/export" download="#{agent_name}.json")
|
|
56
|
+
span.icon.is-small
|
|
57
|
+
i.fas.fa-download
|
|
58
|
+
span Export JSON
|
|
59
|
+
a.dropdown-item(href="/agents/#{encoded_agent_name}/download" download="#{agent_name}.rb")
|
|
60
|
+
span.icon.is-small
|
|
61
|
+
i.fas.fa-file-code
|
|
62
|
+
span Download Ruby
|
|
63
|
+
a.dropdown-item(onclick="saveAgentToDisk('#{encoded_agent_name}')" style="cursor: pointer;" title="Write agents/#{agent_name}.rb so this agent survives a restart")
|
|
64
|
+
span.icon.is-small
|
|
65
|
+
i.fas.fa-hard-drive
|
|
66
|
+
span Save to agents/
|
|
67
|
+
hr.dropdown-divider
|
|
68
|
+
a.dropdown-item.has-text-danger(href="#" data-action="delete-agent" data-agent-name=agent_name)
|
|
69
|
+
span.icon.is-small
|
|
70
|
+
i.fas.fa-trash-alt
|
|
71
|
+
span Delete Agent
|