language-operator 0.1.66 → 0.1.70
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/examples/chat_endpoint_agent.rb +78 -0
- data/examples/hybrid_agent.rb +227 -0
- data/examples/pure_agent_test.rb +26 -0
- data/lib/language_operator/agent/web_server.rb +8 -5
- data/lib/language_operator/agent.rb +31 -2
- data/lib/language_operator/dsl/agent_definition.rb +38 -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 +0 -6
- metadata +20 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d6a8a654ac7b3fea9f1d176029ba86153b44cc746dacb8173054c6c2e0740def
|
|
4
|
+
data.tar.gz: d67180ae9aa7e99ad55b0764936028348039359367320a7e76afad95d5cd84a6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7244d148d067fb2733a23c193c499bb13bc336891cdd5fbbb932cc1ecf40111cf2bfb8f3e25ac9cd290b655915a3e2ab5e3c88bfb133063ba47991835d7e485e
|
|
7
|
+
data.tar.gz: d11fa46700dd377f76804c3808848b4ee321982db18c25611834b660c60babbffb80fbcfe26abc8e32d21e01b26776fdb97f699b7e769d6a7a2337fadea591ba
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
language-operator (0.1.
|
|
4
|
+
language-operator (0.1.70)
|
|
5
5
|
faraday (~> 2.0)
|
|
6
|
+
json-schema (~> 5.0)
|
|
6
7
|
k8s-ruby (~> 0.17)
|
|
7
8
|
lru_redux (~> 1.1)
|
|
8
9
|
mcp (~> 0.4)
|
|
@@ -27,14 +28,14 @@ PATH
|
|
|
27
28
|
GEM
|
|
28
29
|
remote: https://rubygems.org/
|
|
29
30
|
specs:
|
|
30
|
-
addressable (2.8.
|
|
31
|
-
public_suffix (>= 2.0.2, <
|
|
31
|
+
addressable (2.8.8)
|
|
32
|
+
public_suffix (>= 2.0.2, < 8.0)
|
|
32
33
|
ast (2.4.3)
|
|
33
34
|
base64 (0.3.0)
|
|
34
35
|
benchmark (0.5.0)
|
|
35
36
|
bigdecimal (3.3.1)
|
|
36
37
|
coderay (1.1.3)
|
|
37
|
-
concurrent-ruby (1.3.
|
|
38
|
+
concurrent-ruby (1.3.6)
|
|
38
39
|
crack (1.0.1)
|
|
39
40
|
bigdecimal
|
|
40
41
|
rexml
|
|
@@ -72,24 +73,24 @@ GEM
|
|
|
72
73
|
logger
|
|
73
74
|
faraday-multipart (1.1.1)
|
|
74
75
|
multipart-post (~> 2.0)
|
|
75
|
-
faraday-net_http (3.4.
|
|
76
|
-
net-http (
|
|
76
|
+
faraday-net_http (3.4.2)
|
|
77
|
+
net-http (~> 0.5)
|
|
77
78
|
faraday-retry (2.3.2)
|
|
78
79
|
faraday (~> 2.0)
|
|
79
|
-
google-protobuf (4.33.
|
|
80
|
+
google-protobuf (4.33.2)
|
|
80
81
|
bigdecimal
|
|
81
82
|
rake (>= 13)
|
|
82
|
-
google-protobuf (4.33.
|
|
83
|
+
google-protobuf (4.33.2-x86_64-linux-gnu)
|
|
83
84
|
bigdecimal
|
|
84
85
|
rake (>= 13)
|
|
85
86
|
googleapis-common-protos-types (1.22.0)
|
|
86
87
|
google-protobuf (~> 4.26)
|
|
87
88
|
hashdiff (1.2.1)
|
|
88
89
|
http-2 (1.1.1)
|
|
89
|
-
httpx (1.
|
|
90
|
+
httpx (1.7.0)
|
|
90
91
|
http-2 (>= 1.0.0)
|
|
91
92
|
ice_nine (0.11.2)
|
|
92
|
-
json (2.
|
|
93
|
+
json (2.18.0)
|
|
93
94
|
json-schema (5.2.2)
|
|
94
95
|
addressable (~> 2.8)
|
|
95
96
|
bigdecimal (~> 3.1)
|
|
@@ -117,10 +118,10 @@ GEM
|
|
|
117
118
|
json_rpc_handler (~> 0.1)
|
|
118
119
|
memory_profiler (1.1.0)
|
|
119
120
|
method_source (1.1.0)
|
|
120
|
-
multi_json (1.
|
|
121
|
+
multi_json (1.18.0)
|
|
121
122
|
multipart-post (2.4.1)
|
|
122
|
-
net-http (0.
|
|
123
|
-
uri
|
|
123
|
+
net-http (0.9.1)
|
|
124
|
+
uri (>= 0.11.1)
|
|
124
125
|
nio4r (2.7.5)
|
|
125
126
|
opentelemetry-api (1.7.0)
|
|
126
127
|
opentelemetry-common (0.23.0)
|
|
@@ -136,7 +137,7 @@ GEM
|
|
|
136
137
|
opentelemetry-api (~> 1.7)
|
|
137
138
|
opentelemetry-common (~> 0.21)
|
|
138
139
|
opentelemetry-registry (~> 0.1)
|
|
139
|
-
opentelemetry-instrumentation-http (0.27.
|
|
140
|
+
opentelemetry-instrumentation-http (0.27.1)
|
|
140
141
|
opentelemetry-instrumentation-base (~> 0.25)
|
|
141
142
|
opentelemetry-instrumentation-rack (0.29.0)
|
|
142
143
|
opentelemetry-instrumentation-base (~> 0.25)
|
|
@@ -158,18 +159,18 @@ GEM
|
|
|
158
159
|
racc
|
|
159
160
|
pastel (0.8.0)
|
|
160
161
|
tty-color (~> 0.5)
|
|
161
|
-
prism (1.
|
|
162
|
+
prism (1.7.0)
|
|
162
163
|
pry (0.15.2)
|
|
163
164
|
coderay (~> 1.1)
|
|
164
165
|
method_source (~> 1.0)
|
|
165
|
-
public_suffix (
|
|
166
|
+
public_suffix (7.0.0)
|
|
166
167
|
puma (6.6.1)
|
|
167
168
|
nio4r (~> 2.0)
|
|
168
169
|
racc (1.8.1)
|
|
169
170
|
rack (3.2.4)
|
|
170
171
|
rack-test (2.2.0)
|
|
171
172
|
rack (>= 1.3)
|
|
172
|
-
rackup (2.
|
|
173
|
+
rackup (2.3.1)
|
|
173
174
|
rack (>= 3)
|
|
174
175
|
rainbow (3.1.1)
|
|
175
176
|
rake (13.3.1)
|
|
@@ -191,7 +192,7 @@ GEM
|
|
|
191
192
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
192
193
|
rspec-support (~> 3.13.0)
|
|
193
194
|
rspec-support (3.13.6)
|
|
194
|
-
rubocop (1.
|
|
195
|
+
rubocop (1.82.1)
|
|
195
196
|
json (~> 2.3)
|
|
196
197
|
language_server-protocol (~> 3.17.0.2)
|
|
197
198
|
lint_roller (~> 1.1.0)
|
|
@@ -199,10 +200,10 @@ GEM
|
|
|
199
200
|
parser (>= 3.3.0.2)
|
|
200
201
|
rainbow (>= 2.2.2, < 4.0)
|
|
201
202
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
202
|
-
rubocop-ast (>= 1.
|
|
203
|
+
rubocop-ast (>= 1.48.0, < 2.0)
|
|
203
204
|
ruby-progressbar (~> 1.7)
|
|
204
205
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
205
|
-
rubocop-ast (1.
|
|
206
|
+
rubocop-ast (1.48.0)
|
|
206
207
|
parser (>= 3.3.7.2)
|
|
207
208
|
prism (~> 1.4)
|
|
208
209
|
rubocop-performance (1.26.1)
|
|
@@ -220,12 +221,12 @@ GEM
|
|
|
220
221
|
marcel (~> 1.0)
|
|
221
222
|
ruby_llm-schema (~> 0.2.1)
|
|
222
223
|
zeitwerk (~> 2)
|
|
223
|
-
ruby_llm-mcp (0.
|
|
224
|
+
ruby_llm-mcp (0.8.0)
|
|
224
225
|
httpx (~> 1.4)
|
|
225
226
|
json-schema (~> 5.0)
|
|
226
227
|
ruby_llm (~> 1.9)
|
|
227
228
|
zeitwerk (~> 2)
|
|
228
|
-
ruby_llm-schema (0.2.
|
|
229
|
+
ruby_llm-schema (0.2.5)
|
|
229
230
|
strings (0.2.1)
|
|
230
231
|
strings-ansi (~> 0.2)
|
|
231
232
|
unicode-display_width (>= 1.5, < 3.0)
|
|
@@ -262,8 +263,8 @@ GEM
|
|
|
262
263
|
wisper (2.0.1)
|
|
263
264
|
yajl-ruby (1.4.3)
|
|
264
265
|
yaml-safe_load_stream3 (0.1.2)
|
|
265
|
-
yard (0.9.
|
|
266
|
-
zeitwerk (2.7.
|
|
266
|
+
yard (0.9.38)
|
|
267
|
+
zeitwerk (2.7.4)
|
|
267
268
|
|
|
268
269
|
PLATFORMS
|
|
269
270
|
ruby
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Chat Endpoint Agent Example
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates how to create an agent that exposes
|
|
7
|
+
# an OpenAI-compatible chat completion endpoint.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# ruby examples/chat_endpoint_agent.rb
|
|
11
|
+
#
|
|
12
|
+
# Then test with curl:
|
|
13
|
+
# curl -X POST http://localhost:8080/v1/chat/completions \
|
|
14
|
+
# -H "Content-Type: application/json" \
|
|
15
|
+
# -d '{
|
|
16
|
+
# "model": "github-expert-v1",
|
|
17
|
+
# "messages": [
|
|
18
|
+
# {"role": "user", "content": "How do I create a pull request?"}
|
|
19
|
+
# ]
|
|
20
|
+
# }'
|
|
21
|
+
|
|
22
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
|
23
|
+
|
|
24
|
+
require 'language_operator'
|
|
25
|
+
|
|
26
|
+
# Define an agent with chat endpoint capabilities
|
|
27
|
+
LanguageOperator::Dsl.define do
|
|
28
|
+
agent "github-expert" do
|
|
29
|
+
description "GitHub API and workflow expert"
|
|
30
|
+
mode :reactive
|
|
31
|
+
|
|
32
|
+
# This is the key - expose the agent as an OpenAI-compatible chat endpoint
|
|
33
|
+
as_chat_endpoint do
|
|
34
|
+
system_prompt <<~PROMPT
|
|
35
|
+
You are a GitHub expert assistant with deep knowledge of:
|
|
36
|
+
- GitHub API and workflows
|
|
37
|
+
- Pull requests, issues, and code review
|
|
38
|
+
- GitHub Actions and CI/CD
|
|
39
|
+
- Repository management and best practices
|
|
40
|
+
|
|
41
|
+
Provide helpful, accurate answers about GitHub topics.
|
|
42
|
+
Keep responses concise but informative.
|
|
43
|
+
PROMPT
|
|
44
|
+
|
|
45
|
+
# Configure the endpoint parameters
|
|
46
|
+
model "github-expert-v1" # Model name returned in API responses
|
|
47
|
+
temperature 0.7 # Balanced creativity and consistency
|
|
48
|
+
max_tokens 2000 # Limit response length
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Optional: Add constraints for safety and cost management
|
|
52
|
+
constraints do
|
|
53
|
+
timeout '30s'
|
|
54
|
+
requests_per_minute 30
|
|
55
|
+
daily_budget 1000 # $10/day
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Start the agent
|
|
61
|
+
if __FILE__ == $PROGRAM_NAME
|
|
62
|
+
puts "Starting GitHub Expert Chat Endpoint Agent..."
|
|
63
|
+
puts "Server will be available at http://localhost:8080"
|
|
64
|
+
puts ""
|
|
65
|
+
puts "Endpoints available:"
|
|
66
|
+
puts " POST /v1/chat/completions - Chat completion (OpenAI-compatible)"
|
|
67
|
+
puts " GET /v1/models - List available models"
|
|
68
|
+
puts " GET /health - Health check"
|
|
69
|
+
puts " GET /ready - Readiness check"
|
|
70
|
+
puts ""
|
|
71
|
+
puts "Test with curl:"
|
|
72
|
+
puts ' curl -X POST http://localhost:8080/v1/chat/completions \\'
|
|
73
|
+
puts ' -H "Content-Type: application/json" \\'
|
|
74
|
+
puts ' -d \'{"model": "github-expert-v1", "messages": [{"role": "user", "content": "How do I create a pull request?"}]}\''
|
|
75
|
+
puts ""
|
|
76
|
+
|
|
77
|
+
LanguageOperator::Agent.run
|
|
78
|
+
end
|
|
@@ -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,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
|
|
@@ -433,8 +433,11 @@ module LanguageOperator
|
|
|
433
433
|
# Build prompt from messages
|
|
434
434
|
prompt = build_prompt_from_messages(messages)
|
|
435
435
|
|
|
436
|
-
# Execute agent
|
|
437
|
-
result = @chat_agent.
|
|
436
|
+
# Execute agent using the correct method
|
|
437
|
+
result = @chat_agent.execute_goal(prompt)
|
|
438
|
+
|
|
439
|
+
# Extract content from result (handle both String and Message objects)
|
|
440
|
+
result_content = result.is_a?(String) ? result : result.content
|
|
438
441
|
|
|
439
442
|
# Build OpenAI-compatible response
|
|
440
443
|
{
|
|
@@ -447,15 +450,15 @@ module LanguageOperator
|
|
|
447
450
|
index: 0,
|
|
448
451
|
message: {
|
|
449
452
|
role: 'assistant',
|
|
450
|
-
content:
|
|
453
|
+
content: result_content
|
|
451
454
|
},
|
|
452
455
|
finish_reason: 'stop'
|
|
453
456
|
}
|
|
454
457
|
],
|
|
455
458
|
usage: {
|
|
456
459
|
prompt_tokens: estimate_tokens(prompt),
|
|
457
|
-
completion_tokens: estimate_tokens(
|
|
458
|
-
total_tokens: estimate_tokens(prompt) + estimate_tokens(
|
|
460
|
+
completion_tokens: estimate_tokens(result_content),
|
|
461
|
+
total_tokens: estimate_tokens(prompt) + estimate_tokens(result_content)
|
|
459
462
|
}
|
|
460
463
|
}
|
|
461
464
|
end
|
|
@@ -159,11 +159,40 @@ module LanguageOperator
|
|
|
159
159
|
|
|
160
160
|
case agent.mode
|
|
161
161
|
when 'autonomous', 'interactive'
|
|
162
|
+
# Hybrid mode: All agents now run main work AND web server (chat endpoints always enabled)
|
|
163
|
+
logger.info('Starting hybrid agent (autonomous + web server)',
|
|
164
|
+
agent_name: agent_def.name,
|
|
165
|
+
chat_endpoint_enabled: true, # Always true now
|
|
166
|
+
has_webhooks: agent_def.webhooks.any?,
|
|
167
|
+
has_mcp_tools: !!(agent_def.mcp_server&.tools?))
|
|
168
|
+
|
|
169
|
+
# Start web server in background thread
|
|
170
|
+
web_server = LanguageOperator::Agent::WebServer.new(agent)
|
|
171
|
+
agent_def.webhooks.each { |webhook_def| webhook_def.register(web_server) }
|
|
172
|
+
web_server.register_mcp_tools(agent_def.mcp_server) if agent_def.mcp_server&.tools?
|
|
173
|
+
web_server.register_chat_endpoint(agent_def.chat_endpoint, agent) # Always register chat endpoint
|
|
174
|
+
|
|
175
|
+
web_thread = Thread.new do
|
|
176
|
+
web_server.start
|
|
177
|
+
rescue StandardError => e
|
|
178
|
+
logger.error('Web server error', error: e.message, backtrace: e.backtrace[0..5])
|
|
179
|
+
raise
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Set up signal handlers for graceful shutdown
|
|
183
|
+
%w[INT TERM].each do |signal|
|
|
184
|
+
Signal.trap(signal) do
|
|
185
|
+
logger.info('Received shutdown signal, stopping hybrid agent')
|
|
186
|
+
web_server.cleanup if web_server.respond_to?(:cleanup)
|
|
187
|
+
web_thread.kill if web_thread&.alive?
|
|
188
|
+
exit 0
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Run main work in foreground
|
|
162
193
|
if uses_dsl_v1
|
|
163
|
-
# DSL v1: Execute main block with task executor in persistent mode
|
|
164
194
|
execute_main_block_persistent(agent, agent_def)
|
|
165
195
|
elsif uses_dsl_v0
|
|
166
|
-
# DSL v0: Execute workflow in autonomous mode
|
|
167
196
|
executor = LanguageOperator::Agent::Executor.new(agent)
|
|
168
197
|
executor.execute_workflow(agent_def)
|
|
169
198
|
else
|
|
@@ -49,7 +49,7 @@ module LanguageOperator
|
|
|
49
49
|
include LanguageOperator::Loggable
|
|
50
50
|
|
|
51
51
|
attr_reader :name, :description, :persona, :schedule, :objectives, :main, :tasks,
|
|
52
|
-
:constraints, :output_config, :execution_mode, :webhooks, :mcp_server
|
|
52
|
+
:constraints, :output_config, :execution_mode, :webhooks, :mcp_server
|
|
53
53
|
|
|
54
54
|
def initialize(name)
|
|
55
55
|
@name = name
|
|
@@ -314,6 +314,16 @@ module LanguageOperator
|
|
|
314
314
|
@mcp_server
|
|
315
315
|
end
|
|
316
316
|
|
|
317
|
+
# Get chat endpoint definition (always available)
|
|
318
|
+
#
|
|
319
|
+
# Returns the chat endpoint definition. If none was explicitly configured,
|
|
320
|
+
# returns a default chat endpoint with basic configuration.
|
|
321
|
+
#
|
|
322
|
+
# @return [ChatEndpointDefinition] The chat endpoint definition
|
|
323
|
+
def chat_endpoint
|
|
324
|
+
@chat_endpoint ||= create_default_chat_endpoint
|
|
325
|
+
end
|
|
326
|
+
|
|
317
327
|
# Define chat endpoint capabilities
|
|
318
328
|
#
|
|
319
329
|
# Allows this agent to respond to OpenAI-compatible chat completion requests.
|
|
@@ -324,7 +334,7 @@ module LanguageOperator
|
|
|
324
334
|
def as_chat_endpoint(&block)
|
|
325
335
|
@chat_endpoint ||= ChatEndpointDefinition.new(@name)
|
|
326
336
|
@chat_endpoint.instance_eval(&block) if block
|
|
327
|
-
|
|
337
|
+
# Note: Don't force mode change - agents can be autonomous AND have chat endpoints
|
|
328
338
|
@chat_endpoint
|
|
329
339
|
end
|
|
330
340
|
|
|
@@ -353,6 +363,32 @@ module LanguageOperator
|
|
|
353
363
|
|
|
354
364
|
private
|
|
355
365
|
|
|
366
|
+
# Create default chat endpoint configuration
|
|
367
|
+
#
|
|
368
|
+
# @return [ChatEndpointDefinition] Default chat endpoint
|
|
369
|
+
def create_default_chat_endpoint
|
|
370
|
+
endpoint = ChatEndpointDefinition.new(@name)
|
|
371
|
+
|
|
372
|
+
# Set default system prompt based on agent description
|
|
373
|
+
default_prompt = if @description
|
|
374
|
+
"You are #{@description.downcase}. Provide helpful assistance based on your capabilities."
|
|
375
|
+
else
|
|
376
|
+
"You are an AI agent named #{@name}. Provide helpful assistance to users."
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
endpoint.system_prompt(default_prompt)
|
|
380
|
+
endpoint.model(@name) # Use agent name as model name
|
|
381
|
+
endpoint.temperature(0.7) # Balanced default
|
|
382
|
+
endpoint.max_tokens(2000) # Reasonable default
|
|
383
|
+
|
|
384
|
+
logger.debug('Created default chat endpoint',
|
|
385
|
+
agent_name: @name,
|
|
386
|
+
model_name: @name,
|
|
387
|
+
system_prompt: default_prompt[0..100])
|
|
388
|
+
|
|
389
|
+
endpoint
|
|
390
|
+
end
|
|
391
|
+
|
|
356
392
|
def logger_component
|
|
357
393
|
"Agent:#{@name}"
|
|
358
394
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"$id": "https://github.com/language-operator/language-operator-gem/schema/agent-dsl.json",
|
|
4
4
|
"title": "Language Operator Agent DSL",
|
|
5
5
|
"description": "Schema for defining autonomous AI agents using the Language Operator DSL",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.70",
|
|
7
7
|
"type": "object",
|
|
8
8
|
"properties": {
|
|
9
9
|
"name": {
|
data/synth/003/Makefile
CHANGED
|
@@ -27,17 +27,11 @@ inspect:
|
|
|
27
27
|
learning-status:
|
|
28
28
|
$(AICTL) learning status $(AGENT)
|
|
29
29
|
|
|
30
|
-
learn:
|
|
31
|
-
kubectl patch languageagent $(AGENT) --type='merge' -p='{"status":{"runsPendingLearning":10}}'
|
|
32
|
-
|
|
33
|
-
|
|
34
30
|
logs:
|
|
35
31
|
$(AICTL) logs $(AGENT)
|
|
36
32
|
|
|
37
33
|
clean:
|
|
38
34
|
$(AICTL) delete $(AGENT) --force
|
|
39
|
-
kubectl delete configmaps -l app.kubernetes.io/name=$(AGENT) --ignore-not-found=true
|
|
40
|
-
kubectl delete configmaps --field-selector metadata.name~=$(AGENT)-v --ignore-not-found=true
|
|
41
35
|
|
|
42
36
|
save:
|
|
43
37
|
$(AICTL) code $(AGENT) --raw > agent.rb
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: language-operator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.70
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James Ryan
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: mcp
|
|
@@ -65,6 +65,20 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '1.3'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: json-schema
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '5.0'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '5.0'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
83
|
name: puma
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -508,6 +522,9 @@ files:
|
|
|
508
522
|
- docs/understanding-generated-code.md
|
|
509
523
|
- docs/using-tools.md
|
|
510
524
|
- docs/webhooks.md
|
|
525
|
+
- examples/chat_endpoint_agent.rb
|
|
526
|
+
- examples/hybrid_agent.rb
|
|
527
|
+
- examples/pure_agent_test.rb
|
|
511
528
|
- examples/ux_helpers_demo.rb
|
|
512
529
|
- lib/language_operator.rb
|
|
513
530
|
- lib/language_operator/agent.rb
|
|
@@ -676,7 +693,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
676
693
|
- !ruby/object:Gem::Version
|
|
677
694
|
version: '0'
|
|
678
695
|
requirements: []
|
|
679
|
-
rubygems_version: 3.6.
|
|
696
|
+
rubygems_version: 3.6.9
|
|
680
697
|
specification_version: 4
|
|
681
698
|
summary: Ruby SDK for Language Operator
|
|
682
699
|
test_files: []
|