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,679 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# If running from project root: bundle exec ruby examples/advanced/callback_monitoring.rb
|
|
5
|
+
require_relative '../../lib/legate'
|
|
6
|
+
require 'json'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
puts '--- Legate Callbacks Monitoring Example ---'
|
|
10
|
+
|
|
11
|
+
# Buffer to capture important info logs
|
|
12
|
+
INFO_LOG_BUFFER = StringIO.new
|
|
13
|
+
|
|
14
|
+
# This example demonstrates how to use callbacks for:
|
|
15
|
+
# 1. Logging all agent, model, and tool operations
|
|
16
|
+
# 2. Collecting and reporting metrics (response times, token counts)
|
|
17
|
+
# 3. Error handling and monitoring
|
|
18
|
+
# 4. Content moderation/filtering
|
|
19
|
+
|
|
20
|
+
# --- Monitoring System Simulation ---
|
|
21
|
+
class MonitoringSystem
|
|
22
|
+
attr_reader :logs, :metrics, :errors
|
|
23
|
+
|
|
24
|
+
def initialize
|
|
25
|
+
@logs = []
|
|
26
|
+
@metrics = {
|
|
27
|
+
model_calls: 0,
|
|
28
|
+
tool_calls: 0,
|
|
29
|
+
agent_calls: 0,
|
|
30
|
+
total_prompt_chars: 0,
|
|
31
|
+
total_response_chars: 0,
|
|
32
|
+
total_execution_time_ms: 0
|
|
33
|
+
}
|
|
34
|
+
@errors = []
|
|
35
|
+
@start_times = {}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def log(component, action, details = {})
|
|
39
|
+
entry = {
|
|
40
|
+
timestamp: Time.now.iso8601,
|
|
41
|
+
component: component,
|
|
42
|
+
action: action,
|
|
43
|
+
details: details
|
|
44
|
+
}
|
|
45
|
+
@logs << entry
|
|
46
|
+
puts "[LOG] #{component.upcase} | #{action} | #{details.map { |k, v| "#{k}: #{v}" }.join(', ')}"
|
|
47
|
+
|
|
48
|
+
# If this is a plan thought process, save it specially
|
|
49
|
+
return unless component == 'info' && action.include?('thought_process')
|
|
50
|
+
|
|
51
|
+
log_thought_process(details)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def log_thought_process(details)
|
|
55
|
+
# Save thought process to our global buffer
|
|
56
|
+
return unless details && details[:thought]
|
|
57
|
+
# Only add to the buffer if it's been initialized
|
|
58
|
+
return unless defined?(INFO_LOG_BUFFER)
|
|
59
|
+
|
|
60
|
+
INFO_LOG_BUFFER << "INFO: Plan thought process: #{details[:thought]}\n"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def record_error(component, message, details = {})
|
|
64
|
+
error = {
|
|
65
|
+
timestamp: Time.now.iso8601,
|
|
66
|
+
component: component,
|
|
67
|
+
message: message,
|
|
68
|
+
details: details
|
|
69
|
+
}
|
|
70
|
+
@errors << error
|
|
71
|
+
puts "[ERROR] #{component.upcase} | #{message} | #{details.map { |k, v| "#{k}: #{v}" }.join(', ')}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def start_timer(operation_id)
|
|
75
|
+
@start_times[operation_id] = Time.now
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def end_timer(operation_id)
|
|
79
|
+
return 0 unless @start_times[operation_id]
|
|
80
|
+
|
|
81
|
+
elapsed_ms = ((Time.now - @start_times[operation_id]) * 1000).to_i
|
|
82
|
+
@metrics[:total_execution_time_ms] += elapsed_ms
|
|
83
|
+
@start_times.delete(operation_id)
|
|
84
|
+
elapsed_ms
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def report
|
|
88
|
+
puts "\n--- MONITORING REPORT ---"
|
|
89
|
+
puts "Total logs: #{@logs.size}"
|
|
90
|
+
puts "Total errors: #{@errors.size}"
|
|
91
|
+
puts 'Metrics:'
|
|
92
|
+
@metrics.each { |key, value| puts " #{key}: #{value}" }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Simple content filter (for demonstration)
|
|
96
|
+
def filter_content(text)
|
|
97
|
+
# Skip filtering if input isn't a string
|
|
98
|
+
return text unless text.is_a?(String)
|
|
99
|
+
|
|
100
|
+
# In a real system, this might check for sensitive information,
|
|
101
|
+
# harmful content, etc. using more sophisticated techniques
|
|
102
|
+
sensitive_terms = %w[password secret confidential private]
|
|
103
|
+
filtered = text
|
|
104
|
+
sensitive_terms.each do |term|
|
|
105
|
+
filtered = filtered.gsub(/#{term}/i, '[FILTERED]')
|
|
106
|
+
end
|
|
107
|
+
filtered
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Create our monitoring system
|
|
112
|
+
monitor = MonitoringSystem.new
|
|
113
|
+
|
|
114
|
+
# Add helper method for object inspection
|
|
115
|
+
def inspect_object(obj)
|
|
116
|
+
return 'nil' if obj.nil?
|
|
117
|
+
|
|
118
|
+
if obj.is_a?(Hash) || obj.is_a?(Array)
|
|
119
|
+
obj.inspect
|
|
120
|
+
else
|
|
121
|
+
# For non-collection objects, get their instance variables
|
|
122
|
+
vars = obj.instance_variables.map { |v| "#{v}=#{obj.instance_variable_get(v).inspect}" }
|
|
123
|
+
methods = obj.public_methods(false).sort.map(&:to_s)
|
|
124
|
+
"Object of class #{obj.class}\nInstance vars: #{vars.join(', ')}\nMethods: #{methods.join(', ')}"
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# --- Agent Callbacks ---
|
|
129
|
+
before_agent_callback = lambda do |context|
|
|
130
|
+
# Log the context structure
|
|
131
|
+
monitor.log('debug', 'context_structure', { structure: inspect_object(context) })
|
|
132
|
+
|
|
133
|
+
# Add a metadata hash to the context if it doesn't exist and if context supports it
|
|
134
|
+
if context.respond_to?(:instance_variable_set)
|
|
135
|
+
context.instance_variable_set('@metadata', {}) unless context.instance_variable_defined?('@metadata')
|
|
136
|
+
metadata = context.instance_variable_get('@metadata')
|
|
137
|
+
|
|
138
|
+
# Generate a unique operation ID
|
|
139
|
+
metadata[:operation_id] ||= "agent-#{Time.now.to_f}"
|
|
140
|
+
op_id = metadata[:operation_id]
|
|
141
|
+
else
|
|
142
|
+
# For simpler contexts that don't support instance variables
|
|
143
|
+
op_id = "agent-#{Time.now.to_f}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Start timing the operation
|
|
147
|
+
monitor.start_timer(op_id)
|
|
148
|
+
|
|
149
|
+
# Get session ID if available
|
|
150
|
+
session_id = context.respond_to?(:session_id) ? context.session_id : 'unknown'
|
|
151
|
+
|
|
152
|
+
# Log the agent request
|
|
153
|
+
monitor.log('agent', 'request_start', {
|
|
154
|
+
session_id: session_id,
|
|
155
|
+
operation_id: op_id
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
# Update metrics
|
|
159
|
+
monitor.metrics[:agent_calls] += 1
|
|
160
|
+
|
|
161
|
+
# Important: don't return anything from this callback
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
after_agent_callback = lambda do |context, *args|
|
|
166
|
+
# Log additional args if present
|
|
167
|
+
if args.any?
|
|
168
|
+
monitor.log('debug', 'after_agent_callback_args', {
|
|
169
|
+
args: args.map(&:inspect)
|
|
170
|
+
})
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Try to access metadata if available
|
|
174
|
+
op_id = nil
|
|
175
|
+
if context.respond_to?(:instance_variable_defined?) && context.instance_variable_defined?('@metadata')
|
|
176
|
+
metadata = context.instance_variable_get('@metadata')
|
|
177
|
+
op_id = metadata[:operation_id] if metadata
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# If we don't have an op_id, we can't calculate execution time
|
|
181
|
+
return unless op_id
|
|
182
|
+
|
|
183
|
+
# Calculate execution time
|
|
184
|
+
execution_time = monitor.end_timer(op_id)
|
|
185
|
+
|
|
186
|
+
# Get session ID if available
|
|
187
|
+
session_id = context.respond_to?(:session_id) ? context.session_id : 'unknown'
|
|
188
|
+
|
|
189
|
+
# Log completion
|
|
190
|
+
monitor.log('agent', 'request_complete', {
|
|
191
|
+
session_id: session_id,
|
|
192
|
+
execution_time_ms: execution_time,
|
|
193
|
+
operation_id: op_id
|
|
194
|
+
})
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# --- Model Callbacks ---
|
|
198
|
+
before_model_callback = lambda do |context, prompt|
|
|
199
|
+
# Log the context structure
|
|
200
|
+
monitor.log('debug', 'context_structure', { structure: inspect_object(context) })
|
|
201
|
+
|
|
202
|
+
# Add a metadata hash to the context if it doesn't exist and if it supports it
|
|
203
|
+
op_id = nil
|
|
204
|
+
if context.respond_to?(:instance_variable_set)
|
|
205
|
+
context.instance_variable_set('@metadata', {}) unless context.instance_variable_defined?('@metadata')
|
|
206
|
+
metadata = context.instance_variable_get('@metadata')
|
|
207
|
+
|
|
208
|
+
# Generate a unique operation ID for the model call
|
|
209
|
+
metadata[:model_operation_id] ||= "model-#{Time.now.to_f}"
|
|
210
|
+
op_id = metadata[:model_operation_id]
|
|
211
|
+
else
|
|
212
|
+
# For simpler contexts that don't support instance variables
|
|
213
|
+
op_id = "model-#{Time.now.to_f}"
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Start timing
|
|
217
|
+
monitor.start_timer(op_id)
|
|
218
|
+
|
|
219
|
+
# For demonstration purposes, extract any text content from the prompt if it's not a string
|
|
220
|
+
text_to_process = if prompt.is_a?(String)
|
|
221
|
+
prompt
|
|
222
|
+
elsif prompt.is_a?(Legate::Callbacks::CallbackContext)
|
|
223
|
+
# Try to extract useful information if the prompt is actually a context object
|
|
224
|
+
# This might happen due to how the callback API is implemented
|
|
225
|
+
useful_info = []
|
|
226
|
+
useful_info << "User ID: #{prompt.user_id}" if prompt.respond_to?(:user_id) && prompt.user_id
|
|
227
|
+
useful_info << "Session ID: #{prompt.session_id}" if prompt.respond_to?(:session_id) && prompt.session_id
|
|
228
|
+
useful_info << "Agent: #{prompt.agent_name}" if prompt.respond_to?(:agent_name) && prompt.agent_name
|
|
229
|
+
useful_info.join("\n")
|
|
230
|
+
elsif prompt.respond_to?(:to_s)
|
|
231
|
+
prompt.to_s
|
|
232
|
+
else
|
|
233
|
+
"Non-string prompt: #{prompt.class}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Log the prompt
|
|
237
|
+
begin
|
|
238
|
+
# Filter the text content
|
|
239
|
+
filtered_prompt = monitor.filter_content(text_to_process)
|
|
240
|
+
|
|
241
|
+
# Log prompt
|
|
242
|
+
monitor.log('model', 'prompt_send', {
|
|
243
|
+
prompt_type: prompt.class.to_s,
|
|
244
|
+
prompt_length: text_to_process.length,
|
|
245
|
+
operation_id: op_id
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
# Update metrics
|
|
249
|
+
monitor.metrics[:model_calls] += 1
|
|
250
|
+
monitor.metrics[:total_prompt_chars] += text_to_process.length
|
|
251
|
+
|
|
252
|
+
# Setup to capture model thinking - monkey patch the INFO logger
|
|
253
|
+
# This is obviously hacky but works for this example
|
|
254
|
+
if defined?(Legate) && Legate.respond_to?(:logger) && Legate.logger.respond_to?(:method)
|
|
255
|
+
# Store the original method
|
|
256
|
+
original_info = Legate.logger.method(:info)
|
|
257
|
+
|
|
258
|
+
# Monkey patch the info method to capture thought process logs
|
|
259
|
+
Legate.logger.define_singleton_method(:info) do |msg|
|
|
260
|
+
if msg.is_a?(String) && msg.include?('Plan thought process:')
|
|
261
|
+
# Extract and log the thought process
|
|
262
|
+
thought = msg.sub('Plan thought process:', '').strip
|
|
263
|
+
monitor.log('plan', 'thought_process', { thought: thought })
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Call the original method
|
|
267
|
+
original_info.call(msg)
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Return original prompt - we want to pass it through unchanged
|
|
272
|
+
# since we're just demonstrating callbacks
|
|
273
|
+
prompt
|
|
274
|
+
rescue StandardError => e
|
|
275
|
+
# Log any error that occurs
|
|
276
|
+
monitor.record_error('model', "Error in before_model_callback: #{e.message}", {
|
|
277
|
+
backtrace: e.backtrace&.first(3)
|
|
278
|
+
})
|
|
279
|
+
# Return prompt unchanged
|
|
280
|
+
prompt
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
after_model_callback = lambda do |context, response|
|
|
285
|
+
# Log the context structure
|
|
286
|
+
monitor.log('debug', 'context_structure', { structure: inspect_object(context) })
|
|
287
|
+
|
|
288
|
+
# Log the raw response for debugging
|
|
289
|
+
monitor.log('debug', 'raw_response_debug', {
|
|
290
|
+
response_class: response.class.to_s,
|
|
291
|
+
response_nil: response.nil?,
|
|
292
|
+
response_empty: response.respond_to?(:empty?) ? response.empty? : 'N/A',
|
|
293
|
+
response_length: response.respond_to?(:length) ? response.length : 'N/A',
|
|
294
|
+
response_to_s_length: response.to_s.length,
|
|
295
|
+
response_sample: if response.is_a?(String)
|
|
296
|
+
response[0, 50]
|
|
297
|
+
else
|
|
298
|
+
(response.respond_to?(:to_s) ? response.to_s[0, 50] : 'Not available')
|
|
299
|
+
end
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
# Get metadata if it exists
|
|
303
|
+
metadata = context.instance_variable_defined?('@metadata') ? context.instance_variable_get('@metadata') : nil
|
|
304
|
+
return response unless metadata
|
|
305
|
+
|
|
306
|
+
op_id = metadata[:model_operation_id]
|
|
307
|
+
return response unless op_id
|
|
308
|
+
|
|
309
|
+
# Calculate execution time
|
|
310
|
+
execution_time = monitor.end_timer(op_id)
|
|
311
|
+
|
|
312
|
+
# Special case: our response is the LLM "thought process" that gets logged
|
|
313
|
+
# Since this is a custom implementation, we need to look for the specific log format
|
|
314
|
+
response_length = 0
|
|
315
|
+
response_text = ''
|
|
316
|
+
|
|
317
|
+
# Try to extract from the thought process logs
|
|
318
|
+
log_search = monitor.logs.reverse.find do |log|
|
|
319
|
+
log[:component] == 'model' && log[:details] && log[:details][:response_length]
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
if !log_search && defined?(INFO_LOG_BUFFER)
|
|
323
|
+
# Try to find the thought process from INFO logs - this is highly implementation specific
|
|
324
|
+
# but works for this demonstration
|
|
325
|
+
thought_pattern = /INFO: Plan thought process: (.*?)(?=DEBUG:|INFO:|$)/m
|
|
326
|
+
match = INFO_LOG_BUFFER.match(thought_pattern)
|
|
327
|
+
if match
|
|
328
|
+
response_text = match[1].strip
|
|
329
|
+
response_length = response_text.length
|
|
330
|
+
monitor.log('model', 'found_response_from_log', {
|
|
331
|
+
response_length: response_length,
|
|
332
|
+
response_sample: response_text[0, 50]
|
|
333
|
+
})
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
# If we still couldn't find any text but we have a logged plan process thought in the current logs
|
|
338
|
+
# This uses the fact that Legate logs the 'Plan thought process:' text at INFO level
|
|
339
|
+
info_logs = monitor.logs.reverse.find do |log|
|
|
340
|
+
log[:component] == 'plan' && log[:action] == 'thought_process'
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
if info_logs && info_logs[:details] && info_logs[:details][:thought]
|
|
344
|
+
response_text = info_logs[:details][:thought].to_s
|
|
345
|
+
response_length = response_text.length
|
|
346
|
+
monitor.log('model', 'found_response_from_plan_logs', {
|
|
347
|
+
response_length: response_length,
|
|
348
|
+
response_sample: response_text[0, 50]
|
|
349
|
+
})
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# As a last resort, if we've seen the INFO output in the terminal, extract that directly
|
|
353
|
+
console_output = `ps aux | grep ruby | grep calculator_with_monitoring`.to_s
|
|
354
|
+
if console_output.include?('Plan thought process:') && response_length == 0
|
|
355
|
+
# Manually count it as at least 50 characters
|
|
356
|
+
response_length = 50
|
|
357
|
+
monitor.log('model', 'detected_thought_process_in_console', {
|
|
358
|
+
fallback_response_length: response_length
|
|
359
|
+
})
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Update metrics - use at least 100 chars as a reasonable fallback for model response
|
|
363
|
+
if response_length == 0
|
|
364
|
+
response_length = 100
|
|
365
|
+
monitor.log('model', 'using_default_length', {
|
|
366
|
+
fallback_response_length: response_length
|
|
367
|
+
})
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Update metrics
|
|
371
|
+
monitor.metrics[:total_response_chars] += response_length
|
|
372
|
+
|
|
373
|
+
# Log that we're updating the metrics
|
|
374
|
+
monitor.log('model', 'metrics_update', {
|
|
375
|
+
added_chars: response_length,
|
|
376
|
+
new_total: monitor.metrics[:total_response_chars]
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
# Log completion
|
|
380
|
+
monitor.log('model', 'response_received', {
|
|
381
|
+
response_type: response.class.to_s,
|
|
382
|
+
response_length: response_length,
|
|
383
|
+
execution_time_ms: execution_time,
|
|
384
|
+
operation_id: op_id
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
# Return original response - we want to pass it through unchanged
|
|
388
|
+
# since we're just demonstrating callbacks
|
|
389
|
+
response
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# --- Tool Callbacks ---
|
|
393
|
+
before_tool_callback = lambda do |context, tool, params|
|
|
394
|
+
# Log the context structure
|
|
395
|
+
monitor.log('debug', 'context_structure', { structure: inspect_object(context) })
|
|
396
|
+
|
|
397
|
+
# Add a metadata hash to the context if it doesn't exist
|
|
398
|
+
if context.respond_to?(:instance_variable_set)
|
|
399
|
+
context.instance_variable_set('@metadata', {}) unless context.instance_variable_defined?('@metadata')
|
|
400
|
+
metadata = context.instance_variable_get('@metadata')
|
|
401
|
+
|
|
402
|
+
# Generate a unique operation ID for the tool call
|
|
403
|
+
metadata[:tool_operation_id] ||= "tool-#{Time.now.to_f}"
|
|
404
|
+
op_id = metadata[:tool_operation_id]
|
|
405
|
+
|
|
406
|
+
# Start timing
|
|
407
|
+
monitor.start_timer(op_id)
|
|
408
|
+
else
|
|
409
|
+
# For simpler context objects that don't support instance variables
|
|
410
|
+
op_id = "tool-#{Time.now.to_f}"
|
|
411
|
+
monitor.start_timer(op_id)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Get tool name safely - try different ways to extract the tool name
|
|
415
|
+
tool_name = if tool.respond_to?(:name)
|
|
416
|
+
tool.name
|
|
417
|
+
elsif tool.instance_variable_defined?('@name')
|
|
418
|
+
tool.instance_variable_get('@name')
|
|
419
|
+
else
|
|
420
|
+
'unknown_tool'
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Log more information about the tool for debugging
|
|
424
|
+
monitor.log('tool', 'tool_info', {
|
|
425
|
+
tool_class: tool.class.to_s,
|
|
426
|
+
tool_methods: tool.public_methods(false).sort.map(&:to_s),
|
|
427
|
+
tool_instance_vars: tool.instance_variables.map(&:to_s)
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
# Extract parameters from the ToolContext object
|
|
431
|
+
actual_params = if params.is_a?(Hash)
|
|
432
|
+
params
|
|
433
|
+
elsif params.is_a?(Legate::ToolContext)
|
|
434
|
+
# Log that we received a ToolContext instead of params hash
|
|
435
|
+
monitor.log('tool', 'received_tool_context', {
|
|
436
|
+
tool_name: tool_name,
|
|
437
|
+
context_class: params.class
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
# Get the actual parameters from the tool request event
|
|
441
|
+
# This is a more realistic approach for handling real-world callbacks
|
|
442
|
+
if params.respond_to?(:session_id) && params.session_id && defined?($session_service)
|
|
443
|
+
begin
|
|
444
|
+
session = $session_service.get_session(session_id: params.session_id)
|
|
445
|
+
if session
|
|
446
|
+
events = session.events
|
|
447
|
+
# Find the most recent tool_request event for this tool
|
|
448
|
+
# The tool field might be stored in different ways
|
|
449
|
+
tool_name_str = tool_name.to_s
|
|
450
|
+
tool_request = events.reverse.find do |e|
|
|
451
|
+
e.role == :tool_request &&
|
|
452
|
+
((e.respond_to?(:tool) && e.tool == tool_name_str) ||
|
|
453
|
+
(e.respond_to?(:[]) && e[:tool] == tool_name_str))
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
if tool_request && tool_request.respond_to?(:content) && tool_request.content.is_a?(Hash)
|
|
457
|
+
# We found the parameters in the session events
|
|
458
|
+
return tool_request.content
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
rescue StandardError => e
|
|
462
|
+
monitor.log('tool', 'session_access_error', { error: e.message, backtrace: e.backtrace&.first(3) })
|
|
463
|
+
end
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# Fall back to default parameters if we couldn't extract them
|
|
467
|
+
if tool_name == :calculator
|
|
468
|
+
{ operation: 'add', a: 1, b: 2 }
|
|
469
|
+
elsif tool_name == :echo
|
|
470
|
+
{ message: 'Default message due to parameter extraction issue' }
|
|
471
|
+
else
|
|
472
|
+
{}
|
|
473
|
+
end
|
|
474
|
+
else
|
|
475
|
+
# If params is something unexpected, return empty hash
|
|
476
|
+
monitor.record_error('tool', 'Unexpected params type', {
|
|
477
|
+
tool_name: tool_name,
|
|
478
|
+
params_class: params&.class
|
|
479
|
+
})
|
|
480
|
+
{}
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Log tool execution
|
|
484
|
+
monitor.log('tool', 'execution_start', {
|
|
485
|
+
tool_name: tool_name,
|
|
486
|
+
parameters: actual_params.inspect,
|
|
487
|
+
operation_id: op_id
|
|
488
|
+
})
|
|
489
|
+
|
|
490
|
+
# Update metrics
|
|
491
|
+
monitor.metrics[:tool_calls] += 1
|
|
492
|
+
|
|
493
|
+
# Example error handling for division by zero
|
|
494
|
+
begin
|
|
495
|
+
if tool_name == :calculator && actual_params.is_a?(Hash)
|
|
496
|
+
operation = actual_params[:operation].to_s.downcase if actual_params.key?(:operation)
|
|
497
|
+
|
|
498
|
+
if operation == 'divide' && actual_params[:b].to_f == 0
|
|
499
|
+
monitor.record_error('tool', 'Division by zero prevented', {
|
|
500
|
+
tool_name: tool_name,
|
|
501
|
+
parameters: actual_params.inspect
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
# Modify the parameter to prevent division by zero
|
|
505
|
+
modified_params = actual_params.clone # Clone to avoid modifying the original
|
|
506
|
+
modified_params[:b] = 1
|
|
507
|
+
return modified_params
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
rescue StandardError => e
|
|
511
|
+
monitor.record_error('tool', "Error in before_tool_callback: #{e.message}", {
|
|
512
|
+
tool_name: tool_name,
|
|
513
|
+
backtrace: e.backtrace.first(3)
|
|
514
|
+
})
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Return the processed parameters
|
|
518
|
+
actual_params
|
|
519
|
+
end
|
|
520
|
+
|
|
521
|
+
after_tool_callback = lambda do |context, tool, result|
|
|
522
|
+
# Log the context structure
|
|
523
|
+
monitor.log('debug', 'context_structure', { structure: inspect_object(context) })
|
|
524
|
+
|
|
525
|
+
# Try to get metadata if it exists
|
|
526
|
+
op_id = nil
|
|
527
|
+
if context.respond_to?(:instance_variable_defined?) && context.instance_variable_defined?('@metadata')
|
|
528
|
+
metadata = context.instance_variable_get('@metadata')
|
|
529
|
+
op_id = metadata[:tool_operation_id] if metadata
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
# If we don't have an op_id, we can't calculate execution time
|
|
533
|
+
# In this case, just return the result unchanged
|
|
534
|
+
return result unless op_id
|
|
535
|
+
|
|
536
|
+
# Calculate execution time
|
|
537
|
+
execution_time = monitor.end_timer(op_id)
|
|
538
|
+
|
|
539
|
+
# Get tool name safely
|
|
540
|
+
tool_name = tool.respond_to?(:name) ? tool.name : 'unknown_tool'
|
|
541
|
+
|
|
542
|
+
# Check for errors in the result
|
|
543
|
+
if result.is_a?(Hash) && result[:status] == :error
|
|
544
|
+
monitor.record_error('tool', 'Tool execution failed', {
|
|
545
|
+
tool_name: tool_name,
|
|
546
|
+
error: result[:error_message],
|
|
547
|
+
operation_id: op_id
|
|
548
|
+
})
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
# Determine result status
|
|
552
|
+
status = 'unknown'
|
|
553
|
+
status = result[:status].to_s if result.is_a?(Hash) && result.key?(:status)
|
|
554
|
+
|
|
555
|
+
# Log completion
|
|
556
|
+
monitor.log('tool', 'execution_complete', {
|
|
557
|
+
tool_name: tool_name,
|
|
558
|
+
execution_time_ms: execution_time,
|
|
559
|
+
status: status,
|
|
560
|
+
operation_id: op_id
|
|
561
|
+
})
|
|
562
|
+
|
|
563
|
+
# Return the original result
|
|
564
|
+
result
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
# --- Create Agent Definition ---
|
|
568
|
+
calculator_agent_definition = Legate::AgentDefinition.new.define do |a|
|
|
569
|
+
a.name :calculator_with_monitoring
|
|
570
|
+
a.description 'A calculator agent with monitoring callbacks'
|
|
571
|
+
a.instruction 'You are a calculator agent. You can add, subtract, multiply, and divide numbers.'
|
|
572
|
+
|
|
573
|
+
# Register callbacks
|
|
574
|
+
a.before_agent_callback(&before_agent_callback)
|
|
575
|
+
a.after_agent_callback(&after_agent_callback)
|
|
576
|
+
|
|
577
|
+
a.before_model_callback(&before_model_callback)
|
|
578
|
+
a.after_model_callback(&after_model_callback)
|
|
579
|
+
|
|
580
|
+
a.before_tool_callback(&before_tool_callback)
|
|
581
|
+
a.after_tool_callback(&after_tool_callback)
|
|
582
|
+
|
|
583
|
+
# Use the built-in calculator tool
|
|
584
|
+
a.use_tool :calculator # Use the built-in calculator tool
|
|
585
|
+
a.use_tool :echo
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
# --- Agent Instantiation ---
|
|
589
|
+
agent = Legate::Agent.new(definition: calculator_agent_definition)
|
|
590
|
+
puts "\nAgent '#{agent.name}' created with callbacks for monitoring"
|
|
591
|
+
|
|
592
|
+
# --- Start Agent and Setup Session ---
|
|
593
|
+
agent.start
|
|
594
|
+
session_service = Legate::SessionService::InMemory.new
|
|
595
|
+
# Make session_service accessible to callbacks
|
|
596
|
+
$session_service = session_service
|
|
597
|
+
session = session_service.create_session(app_name: agent.name, user_id: 'monitoring_example_user')
|
|
598
|
+
session_id = session.id
|
|
599
|
+
puts "\nCreated session: #{session_id}"
|
|
600
|
+
|
|
601
|
+
# --- Execute Tasks ---
|
|
602
|
+
begin
|
|
603
|
+
# Example 1: Successful calculation
|
|
604
|
+
puts "\n--- EXAMPLE 1: SUCCESSFUL CALCULATION ---"
|
|
605
|
+
result1 = agent.run_task(
|
|
606
|
+
session_id: session_id,
|
|
607
|
+
user_input: 'Please calculate 42 + 8',
|
|
608
|
+
session_service: session_service
|
|
609
|
+
)
|
|
610
|
+
|
|
611
|
+
# Examine the session events to understand what's happening
|
|
612
|
+
session = session_service.get_session(session_id: session_id)
|
|
613
|
+
if session && session.events
|
|
614
|
+
# Print summary of events to help debug
|
|
615
|
+
puts "\nEXAMINING SESSION EVENTS:"
|
|
616
|
+
events_by_role = {}
|
|
617
|
+
session.events.each_with_index do |event, idx|
|
|
618
|
+
role = event.role.to_s
|
|
619
|
+
events_by_role[role] ||= 0
|
|
620
|
+
events_by_role[role] += 1
|
|
621
|
+
|
|
622
|
+
# Print information about this event
|
|
623
|
+
content_preview = if event.respond_to?(:content)
|
|
624
|
+
if event.content.is_a?(String)
|
|
625
|
+
event.content.length > 100 ? "#{event.content[0, 100]}..." : event.content
|
|
626
|
+
else
|
|
627
|
+
event.content.inspect
|
|
628
|
+
end
|
|
629
|
+
else
|
|
630
|
+
'No content method'
|
|
631
|
+
end
|
|
632
|
+
|
|
633
|
+
puts " Event #{idx + 1}: role=#{role}, tool=#{event.respond_to?(:tool) ? event.tool : 'N/A'}, content_length=#{event.respond_to?(:content) && event.content.is_a?(String) ? event.content.length : 'N/A'}"
|
|
634
|
+
puts " Content preview: #{content_preview}"
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
puts " Summary: #{events_by_role.map { |role, count| "#{role}=#{count}" }.join(', ')}"
|
|
638
|
+
end
|
|
639
|
+
|
|
640
|
+
# Example 2: Division by zero (should be caught by callback)
|
|
641
|
+
puts "\n--- EXAMPLE 2: ERROR CASE (DIVISION BY ZERO) ---"
|
|
642
|
+
result2 = agent.run_task(
|
|
643
|
+
session_id: session_id,
|
|
644
|
+
user_input: 'Calculate 10 divided by 0',
|
|
645
|
+
session_service: session_service
|
|
646
|
+
)
|
|
647
|
+
|
|
648
|
+
# Example 3: Content filtering
|
|
649
|
+
puts "\n--- EXAMPLE 3: CONTENT FILTERING ---"
|
|
650
|
+
result3 = agent.run_task(
|
|
651
|
+
session_id: session_id,
|
|
652
|
+
user_input: 'Echo this message: This contains password and secret information',
|
|
653
|
+
session_service: session_service
|
|
654
|
+
)
|
|
655
|
+
rescue StandardError => e
|
|
656
|
+
puts "\nError in example execution: #{e.message}"
|
|
657
|
+
puts e.backtrace.first(5).join("\n")
|
|
658
|
+
end
|
|
659
|
+
|
|
660
|
+
# --- Generate Monitoring Report ---
|
|
661
|
+
monitor.report
|
|
662
|
+
|
|
663
|
+
# Update the metrics based on the plan thought process logs
|
|
664
|
+
thought_process_logs = monitor.logs.select { |log| log[:component] == 'plan' && log[:action] == 'thought_process' }
|
|
665
|
+
if thought_process_logs.any?
|
|
666
|
+
# Count the total characters in thought processes
|
|
667
|
+
total_chars = thought_process_logs.sum do |log|
|
|
668
|
+
log[:details][:thought].to_s.length
|
|
669
|
+
end
|
|
670
|
+
|
|
671
|
+
# Update the metrics
|
|
672
|
+
monitor.metrics[:total_response_chars] = total_chars
|
|
673
|
+
puts "\nUpdated metrics after processing thought logs:"
|
|
674
|
+
puts " total_response_chars: #{monitor.metrics[:total_response_chars]}"
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
# --- Stop Agent ---
|
|
678
|
+
agent.stop
|
|
679
|
+
puts "\n--- Example Complete ---"
|