language-operator 0.1.67 → 0.1.71
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 +4 -4
- data/Gemfile.lock +25 -24
- data/components/agent/Gemfile +1 -1
- data/docs/persona-driven-system-prompts.md +346 -0
- data/examples/basic_agent_with_default_chat.rb +99 -0
- data/examples/chat_endpoint_agent.rb +66 -0
- data/examples/hybrid_agent.rb +227 -0
- data/examples/identity_aware_chat_agent.rb +83 -0
- data/examples/pure_agent_test.rb +26 -0
- data/examples/ux_helpers_demo.rb +0 -0
- data/lib/language_operator/agent/metadata_collector.rb +222 -0
- data/lib/language_operator/agent/prompt_builder.rb +282 -0
- data/lib/language_operator/agent/web_server.rb +91 -25
- data/lib/language_operator/agent.rb +31 -2
- data/lib/language_operator/dsl/agent_definition.rb +5 -19
- data/lib/language_operator/dsl/chat_endpoint_definition.rb +112 -2
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
- data/lib/language_operator/version.rb +1 -1
- data/synth/003/Makefile +6 -0
- metadata +23 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Hybrid Agent Example
|
|
5
|
+
#
|
|
6
|
+
# This demonstrates an agent that runs BOTH:
|
|
7
|
+
# 1. Continuous autonomous work (background monitoring)
|
|
8
|
+
# 2. Chat endpoint for interactive queries
|
|
9
|
+
#
|
|
10
|
+
# The agent will:
|
|
11
|
+
# - Monitor system status every 30 seconds (autonomous mode)
|
|
12
|
+
# - Expose chat endpoint at /v1/chat/completions (web server)
|
|
13
|
+
# - Handle webhooks for alerts (reactive features)
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# ruby examples/hybrid_agent.rb
|
|
17
|
+
#
|
|
18
|
+
# Test chat endpoint:
|
|
19
|
+
# curl -X POST http://localhost:8080/v1/chat/completions \
|
|
20
|
+
# -H "Content-Type: application/json" \
|
|
21
|
+
# -d '{
|
|
22
|
+
# "model": "system-monitor-v1",
|
|
23
|
+
# "messages": [
|
|
24
|
+
# {"role": "user", "content": "What is the current system status?"}
|
|
25
|
+
# ]
|
|
26
|
+
# }'
|
|
27
|
+
|
|
28
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
29
|
+
|
|
30
|
+
require 'language_operator'
|
|
31
|
+
|
|
32
|
+
# Define a hybrid agent
|
|
33
|
+
LanguageOperator::Dsl.define do
|
|
34
|
+
agent "system-monitor" do
|
|
35
|
+
description "Autonomous system monitor with chat interface"
|
|
36
|
+
|
|
37
|
+
# This agent runs in autonomous mode (continuous loop)
|
|
38
|
+
mode :autonomous
|
|
39
|
+
|
|
40
|
+
# Define the main autonomous work
|
|
41
|
+
task :check_system_status,
|
|
42
|
+
instructions: "Check current system status (CPU, memory, disk, network)",
|
|
43
|
+
inputs: {},
|
|
44
|
+
outputs: {
|
|
45
|
+
status: 'string',
|
|
46
|
+
cpu_usage: 'number',
|
|
47
|
+
memory_usage: 'number',
|
|
48
|
+
disk_usage: 'number',
|
|
49
|
+
timestamp: 'string'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
task :analyze_trends,
|
|
53
|
+
instructions: "Analyze system trends and identify potential issues",
|
|
54
|
+
inputs: {
|
|
55
|
+
status: 'string',
|
|
56
|
+
cpu_usage: 'number',
|
|
57
|
+
memory_usage: 'number',
|
|
58
|
+
disk_usage: 'number'
|
|
59
|
+
},
|
|
60
|
+
outputs: {
|
|
61
|
+
analysis: 'string',
|
|
62
|
+
alerts: 'array'
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
task :log_status,
|
|
66
|
+
instructions: "Log the system status and analysis",
|
|
67
|
+
inputs: {
|
|
68
|
+
status: 'string',
|
|
69
|
+
analysis: 'string',
|
|
70
|
+
alerts: 'array',
|
|
71
|
+
timestamp: 'string'
|
|
72
|
+
},
|
|
73
|
+
outputs: { logged: 'boolean' }
|
|
74
|
+
|
|
75
|
+
# Main autonomous loop - runs continuously
|
|
76
|
+
main do |inputs|
|
|
77
|
+
puts "🔄 Running system monitoring cycle..."
|
|
78
|
+
|
|
79
|
+
# Check system status
|
|
80
|
+
status_data = execute_task(:check_system_status)
|
|
81
|
+
puts "📊 System status: #{status_data[:status]} (CPU: #{status_data[:cpu_usage]}%)"
|
|
82
|
+
|
|
83
|
+
# Analyze trends
|
|
84
|
+
analysis_data = execute_task(:analyze_trends, inputs: status_data)
|
|
85
|
+
|
|
86
|
+
# Log results
|
|
87
|
+
execute_task(:log_status, inputs: status_data.merge(analysis_data))
|
|
88
|
+
|
|
89
|
+
if analysis_data[:alerts].any?
|
|
90
|
+
puts "⚠️ Alerts: #{analysis_data[:alerts].join(', ')}"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
puts "😴 Sleeping for 30 seconds..."
|
|
94
|
+
sleep 30
|
|
95
|
+
|
|
96
|
+
# Return data for next cycle
|
|
97
|
+
status_data.merge(analysis_data)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Chat endpoint for interactive queries
|
|
101
|
+
as_chat_endpoint do
|
|
102
|
+
system_prompt <<~PROMPT
|
|
103
|
+
You are a system monitoring assistant with real-time access to system metrics.
|
|
104
|
+
|
|
105
|
+
You can help with:
|
|
106
|
+
- Current system status and performance metrics
|
|
107
|
+
- Historical trends and analysis
|
|
108
|
+
- Performance optimization recommendations
|
|
109
|
+
- Alert investigation and troubleshooting
|
|
110
|
+
- System health assessments
|
|
111
|
+
|
|
112
|
+
Provide clear, actionable insights about system performance.
|
|
113
|
+
Use technical terminology appropriately but explain complex concepts.
|
|
114
|
+
PROMPT
|
|
115
|
+
|
|
116
|
+
model "system-monitor-v1"
|
|
117
|
+
temperature 0.3 # More factual for system data
|
|
118
|
+
max_tokens 1500
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Webhook for external alerts
|
|
122
|
+
webhook "/alert" do
|
|
123
|
+
method :post
|
|
124
|
+
|
|
125
|
+
on_request do |context|
|
|
126
|
+
alert_data = JSON.parse(context[:body])
|
|
127
|
+
puts "🚨 Received alert: #{alert_data['message']}"
|
|
128
|
+
|
|
129
|
+
# Could trigger immediate system check or escalation
|
|
130
|
+
{
|
|
131
|
+
status: 'received',
|
|
132
|
+
alert_id: alert_data['id'],
|
|
133
|
+
processed_at: Time.now.iso8601
|
|
134
|
+
}
|
|
135
|
+
end
|
|
136
|
+
rescue JSON::ParserError => e
|
|
137
|
+
{
|
|
138
|
+
error: 'Invalid JSON in alert payload',
|
|
139
|
+
message: e.message
|
|
140
|
+
}
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# MCP tools for system operations
|
|
144
|
+
as_mcp_server do
|
|
145
|
+
tool "get_current_metrics" do
|
|
146
|
+
description "Get real-time system metrics"
|
|
147
|
+
|
|
148
|
+
execute do |params|
|
|
149
|
+
# Simulate system metrics collection
|
|
150
|
+
{
|
|
151
|
+
cpu_percent: rand(0.0..100.0).round(1),
|
|
152
|
+
memory_percent: rand(0.0..100.0).round(1),
|
|
153
|
+
disk_percent: rand(0.0..100.0).round(1),
|
|
154
|
+
load_average: [rand(0.0..4.0), rand(0.0..4.0), rand(0.0..4.0)].map { |x| x.round(2) },
|
|
155
|
+
uptime_seconds: rand(3600..2_592_000),
|
|
156
|
+
timestamp: Time.now.iso8601
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
tool "restart_service" do
|
|
162
|
+
description "Restart a system service"
|
|
163
|
+
parameter "service_name", type: :string, required: true, description: "Name of service to restart"
|
|
164
|
+
|
|
165
|
+
execute do |params|
|
|
166
|
+
service_name = params["service_name"]
|
|
167
|
+
puts "🔄 Restarting service: #{service_name}"
|
|
168
|
+
|
|
169
|
+
# Simulate service restart
|
|
170
|
+
success = rand > 0.1 # 90% success rate
|
|
171
|
+
|
|
172
|
+
{
|
|
173
|
+
service: service_name,
|
|
174
|
+
action: 'restart',
|
|
175
|
+
success: success,
|
|
176
|
+
message: success ? "Service restarted successfully" : "Failed to restart service",
|
|
177
|
+
timestamp: Time.now.iso8601
|
|
178
|
+
}
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Constraints for safety and resource management
|
|
184
|
+
constraints do
|
|
185
|
+
timeout '60s'
|
|
186
|
+
max_iterations 999999 # Run indefinitely
|
|
187
|
+
requests_per_minute 60 # For chat/webhook endpoints
|
|
188
|
+
daily_budget 2000 # $20/day
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# Start the hybrid agent
|
|
194
|
+
if __FILE__ == $PROGRAM_NAME
|
|
195
|
+
puts "🚀 Starting Hybrid System Monitor Agent"
|
|
196
|
+
puts ""
|
|
197
|
+
puts "This agent will:"
|
|
198
|
+
puts " ✅ Run autonomous system monitoring every 30 seconds"
|
|
199
|
+
puts " ✅ Expose chat endpoint at http://localhost:8080/v1/chat/completions"
|
|
200
|
+
puts " ✅ Handle alerts via POST /alert webhook"
|
|
201
|
+
puts " ✅ Provide MCP tools for system operations"
|
|
202
|
+
puts ""
|
|
203
|
+
puts "Available endpoints:"
|
|
204
|
+
puts " POST /v1/chat/completions - Chat with the monitoring assistant"
|
|
205
|
+
puts " GET /v1/models - List available models"
|
|
206
|
+
puts " POST /alert - Receive system alerts"
|
|
207
|
+
puts " POST /mcp - MCP protocol endpoint"
|
|
208
|
+
puts " GET /health - Health check"
|
|
209
|
+
puts " GET /ready - Readiness check"
|
|
210
|
+
puts ""
|
|
211
|
+
puts "Test commands:"
|
|
212
|
+
puts ""
|
|
213
|
+
puts "# Chat with the agent"
|
|
214
|
+
puts "curl -X POST http://localhost:8080/v1/chat/completions \\"
|
|
215
|
+
puts ' -H "Content-Type: application/json" \\'
|
|
216
|
+
puts ' -d \'{"model": "system-monitor-v1", "messages": [{"role": "user", "content": "What is the system status?"}]}\''
|
|
217
|
+
puts ""
|
|
218
|
+
puts "# Send an alert"
|
|
219
|
+
puts "curl -X POST http://localhost:8080/alert \\"
|
|
220
|
+
puts ' -H "Content-Type: application/json" \\'
|
|
221
|
+
puts ' -d \'{"id": "alert-001", "message": "High CPU usage detected", "severity": "warning"}\''
|
|
222
|
+
puts ""
|
|
223
|
+
puts "Press Ctrl+C to stop"
|
|
224
|
+
puts "=" * 80
|
|
225
|
+
|
|
226
|
+
LanguageOperator::Agent.run
|
|
227
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Identity-Aware Chat Agent Example
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates automatic persona-driven system prompts that provide
|
|
7
|
+
# ALL agents with awareness of their identity, role, environment, and operational context.
|
|
8
|
+
#
|
|
9
|
+
# Key Features (AUTOMATIC):
|
|
10
|
+
# - Agent knows its name, role, and purpose
|
|
11
|
+
# - Operational context (uptime, cluster, status) awareness
|
|
12
|
+
# - Environment information (namespace, tools available)
|
|
13
|
+
# - Professional, contextual conversation style
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# ruby examples/identity_aware_chat_agent.rb
|
|
17
|
+
#
|
|
18
|
+
# Test with:
|
|
19
|
+
# curl -X POST http://localhost:8080/v1/chat/completions \
|
|
20
|
+
# -H "Content-Type: application/json" \
|
|
21
|
+
# -d '{"model": "say-something-v2", "messages": [{"role": "user", "content": "hello"}]}'
|
|
22
|
+
|
|
23
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
24
|
+
|
|
25
|
+
require 'language_operator'
|
|
26
|
+
|
|
27
|
+
# Define an identity-aware agent
|
|
28
|
+
LanguageOperator::Dsl.define do
|
|
29
|
+
agent "say-something" do
|
|
30
|
+
description "A helpful assistant that specializes in creating engaging, concise messages and interactions. You excel at understanding context and providing meaningful responses that fit the conversation."
|
|
31
|
+
mode :reactive
|
|
32
|
+
|
|
33
|
+
# Identity-aware chat endpoint is automatic!
|
|
34
|
+
# No configuration needed - every agent gets it by default.
|
|
35
|
+
|
|
36
|
+
# Add some constraints for safety
|
|
37
|
+
constraints do
|
|
38
|
+
timeout '30s'
|
|
39
|
+
requests_per_minute 20
|
|
40
|
+
daily_budget 500 # $5/day
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Start the agent
|
|
46
|
+
if __FILE__ == $PROGRAM_NAME
|
|
47
|
+
puts "🚀 Starting Identity-Aware Say-Something Agent..."
|
|
48
|
+
puts ""
|
|
49
|
+
puts "ALL agents automatically include persona-driven system prompts with:"
|
|
50
|
+
puts " ✓ Agent identity awareness (name, role, purpose)"
|
|
51
|
+
puts " ✓ Operational context (uptime, status, environment)"
|
|
52
|
+
puts " ✓ Dynamic prompt generation based on current state"
|
|
53
|
+
puts " ✓ Professional, contextual conversation style"
|
|
54
|
+
puts ""
|
|
55
|
+
puts "Server will be available at http://localhost:8080"
|
|
56
|
+
puts ""
|
|
57
|
+
puts "📝 Example conversation:"
|
|
58
|
+
puts " User: 'hello'"
|
|
59
|
+
puts " Agent: 'Hello! I'm say-something, running in the code-games cluster."
|
|
60
|
+
puts " I've been active for 15m now, helping log interesting messages"
|
|
61
|
+
puts " and interactions. How can I assist you today?'"
|
|
62
|
+
puts ""
|
|
63
|
+
puts "🔧 Endpoints:"
|
|
64
|
+
puts " POST /v1/chat/completions - Chat completion (OpenAI-compatible)"
|
|
65
|
+
puts " GET /v1/models - List available models"
|
|
66
|
+
puts " GET /health - Health check"
|
|
67
|
+
puts " GET /ready - Readiness check"
|
|
68
|
+
puts ""
|
|
69
|
+
puts "🧪 Test command:"
|
|
70
|
+
puts " curl -X POST http://localhost:8080/v1/chat/completions \\"
|
|
71
|
+
puts " -H 'Content-Type: application/json' \\"
|
|
72
|
+
puts ' -d \'{"model": "say-something-v2", "messages": [{"role": "user", "content": "hello"}]}\''
|
|
73
|
+
puts ""
|
|
74
|
+
puts "💡 Try different questions to see automatic identity awareness:"
|
|
75
|
+
puts " - 'hello' or 'hi there' for context-aware introductions"
|
|
76
|
+
puts " - 'what are you?' or 'tell me about yourself'"
|
|
77
|
+
puts " - 'how long have you been running?'"
|
|
78
|
+
puts " - 'what can you do?' or 'what tools do you have?'"
|
|
79
|
+
puts " - 'what cluster are you in?'"
|
|
80
|
+
puts ""
|
|
81
|
+
|
|
82
|
+
LanguageOperator::Agent.run
|
|
83
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Pure Agent Definition (no Ruby setup code)
|
|
2
|
+
# This demonstrates that agents automatically get chat endpoints by default
|
|
3
|
+
|
|
4
|
+
agent "test-basic" do
|
|
5
|
+
description "Simple test agent to verify default chat endpoints"
|
|
6
|
+
mode :autonomous
|
|
7
|
+
|
|
8
|
+
# Simple task
|
|
9
|
+
task :do_work,
|
|
10
|
+
instructions: "Perform some basic work",
|
|
11
|
+
inputs: {},
|
|
12
|
+
outputs: {
|
|
13
|
+
message: 'string',
|
|
14
|
+
timestamp: 'string'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
main do |inputs|
|
|
18
|
+
work_result = execute_task(:do_work)
|
|
19
|
+
work_result
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
constraints do
|
|
23
|
+
max_iterations 5 # Just run a few times for testing
|
|
24
|
+
timeout '10s'
|
|
25
|
+
end
|
|
26
|
+
end
|
data/examples/ux_helpers_demo.rb
CHANGED
|
File without changes
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../loggable'
|
|
4
|
+
|
|
5
|
+
module LanguageOperator
|
|
6
|
+
module Agent
|
|
7
|
+
# Agent Metadata Collector
|
|
8
|
+
#
|
|
9
|
+
# Collects runtime and configuration metadata about an agent for use in
|
|
10
|
+
# persona-driven system prompts and conversation context.
|
|
11
|
+
#
|
|
12
|
+
# Provides information about:
|
|
13
|
+
# - Agent identity (name, description, persona)
|
|
14
|
+
# - Runtime environment (cluster, namespace, mode)
|
|
15
|
+
# - Operational state (uptime, workspace, status)
|
|
16
|
+
# - Configuration details (capabilities, constraints)
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# collector = MetadataCollector.new(agent)
|
|
20
|
+
# metadata = collector.collect
|
|
21
|
+
# puts metadata[:identity][:name] # => "my-agent"
|
|
22
|
+
# puts metadata[:runtime][:uptime] # => "2h 15m"
|
|
23
|
+
class MetadataCollector
|
|
24
|
+
include LanguageOperator::Loggable
|
|
25
|
+
|
|
26
|
+
attr_reader :agent, :start_time
|
|
27
|
+
|
|
28
|
+
# Initialize metadata collector
|
|
29
|
+
#
|
|
30
|
+
# @param agent [LanguageOperator::Agent::Base] The agent instance
|
|
31
|
+
def initialize(agent)
|
|
32
|
+
@agent = agent
|
|
33
|
+
@start_time = Time.now
|
|
34
|
+
@logger = logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Collect all available metadata
|
|
38
|
+
#
|
|
39
|
+
# @return [Hash] Complete metadata structure
|
|
40
|
+
def collect
|
|
41
|
+
{
|
|
42
|
+
identity: collect_identity,
|
|
43
|
+
runtime: collect_runtime,
|
|
44
|
+
environment: collect_environment,
|
|
45
|
+
operational: collect_operational,
|
|
46
|
+
capabilities: collect_capabilities
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Collect basic identity information
|
|
51
|
+
#
|
|
52
|
+
# @return [Hash] Identity metadata
|
|
53
|
+
def collect_identity
|
|
54
|
+
config = @agent.config || {}
|
|
55
|
+
agent_config = config.dig('agent') || {}
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
name: ENV.fetch('AGENT_NAME', agent_config['name'] || 'unknown'),
|
|
59
|
+
description: agent_config['instructions'] || agent_config['description'] || 'AI Agent',
|
|
60
|
+
persona: agent_config['persona'] || ENV.fetch('PERSONA_NAME', nil),
|
|
61
|
+
mode: @agent.mode || 'unknown',
|
|
62
|
+
version: LanguageOperator::VERSION
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Collect runtime environment information
|
|
67
|
+
#
|
|
68
|
+
# @return [Hash] Runtime metadata
|
|
69
|
+
def collect_runtime
|
|
70
|
+
{
|
|
71
|
+
uptime: calculate_uptime,
|
|
72
|
+
started_at: @start_time.iso8601,
|
|
73
|
+
process_id: Process.pid,
|
|
74
|
+
workspace_available: @agent.workspace_available?,
|
|
75
|
+
mcp_servers_connected: @agent.respond_to?(:servers_info) ? @agent.servers_info.length : 0
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Collect deployment environment information
|
|
80
|
+
#
|
|
81
|
+
# @return [Hash] Environment metadata
|
|
82
|
+
def collect_environment
|
|
83
|
+
{
|
|
84
|
+
cluster: ENV.fetch('AGENT_CLUSTER', nil),
|
|
85
|
+
namespace: ENV.fetch('AGENT_NAMESPACE', ENV.fetch('KUBERNETES_NAMESPACE', nil)),
|
|
86
|
+
workspace_path: @agent.workspace_path,
|
|
87
|
+
kubernetes_enabled: !ENV.fetch('KUBERNETES_SERVICE_HOST', nil).nil?,
|
|
88
|
+
telemetry_enabled: !ENV.fetch('OTEL_EXPORTER_OTLP_ENDPOINT', nil).nil?
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Collect operational state information
|
|
93
|
+
#
|
|
94
|
+
# @return [Hash] Operational metadata
|
|
95
|
+
def collect_operational
|
|
96
|
+
status = determine_agent_status
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
status: status,
|
|
100
|
+
ready: status == 'ready',
|
|
101
|
+
mode: @agent.mode,
|
|
102
|
+
workspace: {
|
|
103
|
+
path: @agent.workspace_path,
|
|
104
|
+
available: @agent.workspace_available?,
|
|
105
|
+
writable: workspace_writable?
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Collect agent capabilities and constraints
|
|
111
|
+
#
|
|
112
|
+
# @return [Hash] Capabilities metadata
|
|
113
|
+
def collect_capabilities
|
|
114
|
+
config = @agent.config || {}
|
|
115
|
+
|
|
116
|
+
# Extract MCP server tools if available
|
|
117
|
+
tools = []
|
|
118
|
+
if @agent.respond_to?(:servers_info)
|
|
119
|
+
@agent.servers_info.each do |server|
|
|
120
|
+
tools << {
|
|
121
|
+
server: server[:name],
|
|
122
|
+
tool_count: server[:tool_count] || 0
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Extract constraints if configured
|
|
128
|
+
constraints = config.dig('constraints') || {}
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
tools: tools,
|
|
132
|
+
total_tools: tools.sum { |t| t[:tool_count] },
|
|
133
|
+
constraints: constraints.empty? ? nil : constraints,
|
|
134
|
+
llm_provider: config.dig('llm', 'provider') || ENV.fetch('LLM_PROVIDER', 'unknown'),
|
|
135
|
+
llm_model: config.dig('llm', 'model') || ENV.fetch('MODEL', 'unknown')
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Get formatted summary suitable for system prompts
|
|
140
|
+
#
|
|
141
|
+
# @return [Hash] Formatted summary for prompt injection
|
|
142
|
+
def summary_for_prompt
|
|
143
|
+
metadata = collect
|
|
144
|
+
identity = metadata[:identity]
|
|
145
|
+
runtime = metadata[:runtime]
|
|
146
|
+
environment = metadata[:environment]
|
|
147
|
+
operational = metadata[:operational]
|
|
148
|
+
capabilities = metadata[:capabilities]
|
|
149
|
+
|
|
150
|
+
{
|
|
151
|
+
agent_name: identity[:name],
|
|
152
|
+
agent_description: identity[:description],
|
|
153
|
+
agent_mode: identity[:mode],
|
|
154
|
+
uptime: runtime[:uptime],
|
|
155
|
+
cluster: environment[:cluster],
|
|
156
|
+
namespace: environment[:namespace],
|
|
157
|
+
status: operational[:status],
|
|
158
|
+
workspace_available: operational[:ready],
|
|
159
|
+
tool_count: capabilities[:total_tools],
|
|
160
|
+
llm_model: capabilities[:llm_model]
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def logger_component
|
|
167
|
+
'Agent::MetadataCollector'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Calculate human-readable uptime
|
|
171
|
+
#
|
|
172
|
+
# @return [String] Formatted uptime string
|
|
173
|
+
def calculate_uptime
|
|
174
|
+
seconds = Time.now - @start_time
|
|
175
|
+
return 'just started' if seconds < 60
|
|
176
|
+
|
|
177
|
+
minutes = (seconds / 60).floor
|
|
178
|
+
hours = (minutes / 60).floor
|
|
179
|
+
days = (hours / 24).floor
|
|
180
|
+
|
|
181
|
+
if days > 0
|
|
182
|
+
"#{days}d #{hours % 24}h #{minutes % 60}m"
|
|
183
|
+
elsif hours > 0
|
|
184
|
+
"#{hours}h #{minutes % 60}m"
|
|
185
|
+
else
|
|
186
|
+
"#{minutes}m"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Determine current agent status
|
|
191
|
+
#
|
|
192
|
+
# @return [String] Status string
|
|
193
|
+
def determine_agent_status
|
|
194
|
+
return 'not_ready' unless @agent.workspace_available?
|
|
195
|
+
return 'starting' if calculate_uptime == 'just started'
|
|
196
|
+
|
|
197
|
+
# Check if agent is connected and functional
|
|
198
|
+
if @agent.respond_to?(:servers_info) && @agent.servers_info.any?
|
|
199
|
+
'ready'
|
|
200
|
+
elsif @agent.respond_to?(:servers_info) && @agent.servers_info.empty?
|
|
201
|
+
'ready_no_tools'
|
|
202
|
+
else
|
|
203
|
+
'ready'
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Check if workspace is writable
|
|
208
|
+
#
|
|
209
|
+
# @return [Boolean] True if workspace is writable
|
|
210
|
+
def workspace_writable?
|
|
211
|
+
return false unless @agent.workspace_available?
|
|
212
|
+
|
|
213
|
+
test_file = File.join(@agent.workspace_path, '.write_test')
|
|
214
|
+
File.write(test_file, 'test')
|
|
215
|
+
File.delete(test_file)
|
|
216
|
+
true
|
|
217
|
+
rescue StandardError
|
|
218
|
+
false
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|