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.
@@ -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
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