language-operator 0.1.58 → 0.1.59
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 +1 -1
- data/lib/language_operator/agent/base.rb +19 -0
- data/lib/language_operator/agent/task_executor.rb +60 -21
- data/lib/language_operator/agent/telemetry.rb +22 -11
- data/lib/language_operator/agent.rb +3 -0
- data/lib/language_operator/cli/base_command.rb +7 -1
- data/lib/language_operator/cli/commands/agent.rb +575 -0
- data/lib/language_operator/cli/formatters/optimization_formatter.rb +226 -0
- data/lib/language_operator/cli/formatters/progress_formatter.rb +1 -1
- data/lib/language_operator/client/base.rb +72 -2
- data/lib/language_operator/client/mcp_connector.rb +4 -6
- data/lib/language_operator/learning/adapters/base_adapter.rb +147 -0
- data/lib/language_operator/learning/adapters/jaeger_adapter.rb +218 -0
- data/lib/language_operator/learning/adapters/signoz_adapter.rb +432 -0
- data/lib/language_operator/learning/adapters/tempo_adapter.rb +236 -0
- data/lib/language_operator/learning/optimizer.rb +318 -0
- data/lib/language_operator/learning/pattern_detector.rb +260 -0
- data/lib/language_operator/learning/task_synthesizer.rb +261 -0
- data/lib/language_operator/learning/trace_analyzer.rb +280 -0
- 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/templates/task_synthesis.tmpl +97 -0
- data/lib/language_operator/ux/concerns/provider_helpers.rb +2 -2
- data/lib/language_operator/version.rb +1 -1
- data/synth/003/Makefile +8 -1
- data/synth/003/output.log +68 -0
- data/synth/README.md +1 -3
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 478995080aedadd2299a94fb609d3180634abd352440b985f4eece929275bdf8
|
|
4
|
+
data.tar.gz: f0b890c5825447e6ead0ec172d9467afd59138f92350457e733cf269125e6757
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 55a51a910de5d8580b741690ca56a2ba489cf5424892b6a9db2b31756936b8c8c11b1fe7d85b9c042cd8c56f04ab6f117af027dc28bc6cd47c6e571cbf53d7e6
|
|
7
|
+
data.tar.gz: fcf2dcad6af25f904c806d0a7b0216a7c6aa4d6d3803000096c33c350cb56050387dcb783394534226d1cc52def72628d57157dc54af60e694c229607c796147
|
data/Gemfile.lock
CHANGED
|
@@ -63,6 +63,9 @@ module LanguageOperator
|
|
|
63
63
|
else
|
|
64
64
|
raise "Unknown agent mode: #{normalized_mode}"
|
|
65
65
|
end
|
|
66
|
+
ensure
|
|
67
|
+
# Flush telemetry for short-lived processes (scheduled mode)
|
|
68
|
+
flush_telemetry if normalized_mode == 'scheduled'
|
|
66
69
|
end
|
|
67
70
|
end
|
|
68
71
|
|
|
@@ -112,6 +115,22 @@ module LanguageOperator
|
|
|
112
115
|
@web_server = WebServer.new(self)
|
|
113
116
|
@web_server.start
|
|
114
117
|
end
|
|
118
|
+
|
|
119
|
+
# Flush OpenTelemetry spans to ensure they're exported before process exits
|
|
120
|
+
#
|
|
121
|
+
# Critical for short-lived processes (CronJobs) that exit quickly.
|
|
122
|
+
# BatchSpanProcessor buffers spans and exports periodically, so without
|
|
123
|
+
# explicit flushing, spans may be lost when the process terminates.
|
|
124
|
+
#
|
|
125
|
+
# @return [void]
|
|
126
|
+
def flush_telemetry
|
|
127
|
+
return unless ENV.fetch('OTEL_EXPORTER_OTLP_ENDPOINT', nil)
|
|
128
|
+
|
|
129
|
+
OpenTelemetry.tracer_provider.force_flush
|
|
130
|
+
logger.info('OpenTelemetry spans flushed to OTLP endpoint')
|
|
131
|
+
rescue StandardError => e
|
|
132
|
+
logger.warn("Failed to flush telemetry: #{e.message}")
|
|
133
|
+
end
|
|
115
134
|
end
|
|
116
135
|
end
|
|
117
136
|
end
|
|
@@ -117,7 +117,8 @@ module LanguageOperator
|
|
|
117
117
|
task: task_name,
|
|
118
118
|
type: task_type,
|
|
119
119
|
timeout: timeout,
|
|
120
|
-
max_retries: max_retries
|
|
120
|
+
max_retries: max_retries,
|
|
121
|
+
inputs: summarize_values(inputs))
|
|
121
122
|
|
|
122
123
|
# Add timeout to span attributes after it's determined
|
|
123
124
|
OpenTelemetry::Trace.current_span&.set_attribute('task.timeout', timeout)
|
|
@@ -214,21 +215,38 @@ module LanguageOperator
|
|
|
214
215
|
|
|
215
216
|
# Helper method for symbolic tasks to execute tools
|
|
216
217
|
#
|
|
217
|
-
#
|
|
218
|
-
# execute_llm to leverage tools through the LLM interface, or call tools
|
|
219
|
-
# directly through the MCP client if needed.
|
|
218
|
+
# Executes an MCP tool directly through the agent's MCP clients.
|
|
220
219
|
#
|
|
221
|
-
# @param tool_name [String] Name of the tool
|
|
222
|
-
# @param action [String] Tool action/method
|
|
220
|
+
# @param tool_name [Symbol, String] Name of the tool to execute
|
|
223
221
|
# @param params [Hash] Tool parameters
|
|
224
|
-
# @return [Object] Tool response
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
222
|
+
# @return [Object] Tool response (parsed from tool result)
|
|
223
|
+
def execute_tool(tool_name, params = {})
|
|
224
|
+
tool_name_str = tool_name.to_s
|
|
225
|
+
|
|
226
|
+
logger.info('Tool call initiated by symbolic task',
|
|
227
|
+
tool: tool_name_str,
|
|
228
|
+
params: summarize_values(params))
|
|
229
|
+
|
|
230
|
+
# Find the tool across all MCP clients
|
|
231
|
+
tool = @agent.tools.find { |t| t.name == tool_name_str }
|
|
232
|
+
raise ArgumentError, "Tool '#{tool_name_str}' not found" unless tool
|
|
233
|
+
|
|
234
|
+
# Execute the tool (it's a Proc/lambda wrapped by RubyLLM)
|
|
235
|
+
result = tool.call(**params)
|
|
236
|
+
|
|
237
|
+
logger.debug('Tool call completed',
|
|
238
|
+
tool: tool_name_str,
|
|
239
|
+
result_preview: result.is_a?(String) ? result[0..200] : result.class.name)
|
|
240
|
+
|
|
241
|
+
# Try to parse JSON response if it looks like JSON
|
|
242
|
+
if result.is_a?(String) && (result.strip.start_with?('{') || result.strip.start_with?('['))
|
|
243
|
+
JSON.parse(result, symbolize_names: true)
|
|
244
|
+
else
|
|
245
|
+
result
|
|
246
|
+
end
|
|
247
|
+
rescue JSON::ParserError
|
|
248
|
+
# Not JSON, return as-is
|
|
249
|
+
result
|
|
232
250
|
end
|
|
233
251
|
|
|
234
252
|
# Helper method for symbolic tasks to call LLM directly
|
|
@@ -291,6 +309,25 @@ module LanguageOperator
|
|
|
291
309
|
'Agent::TaskExecutor'
|
|
292
310
|
end
|
|
293
311
|
|
|
312
|
+
# Summarize hash values for logging (truncate long strings)
|
|
313
|
+
#
|
|
314
|
+
# @param hash [Hash] Hash to summarize
|
|
315
|
+
# @return [Hash] Summarized hash with truncated values
|
|
316
|
+
def summarize_values(hash)
|
|
317
|
+
return {} unless hash.is_a?(Hash)
|
|
318
|
+
|
|
319
|
+
hash.transform_values do |v|
|
|
320
|
+
case v
|
|
321
|
+
when String
|
|
322
|
+
v.length > 100 ? "#{v[0..97]}... (#{v.length} chars)" : v
|
|
323
|
+
when Array
|
|
324
|
+
v.length > 5 ? "#{v.first(3).inspect}... (#{v.length} items)" : v.inspect
|
|
325
|
+
else
|
|
326
|
+
v.inspect
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
|
|
294
331
|
# Build prompt for neural task execution
|
|
295
332
|
#
|
|
296
333
|
# @param task [TaskDefinition] The task definition
|
|
@@ -316,10 +353,11 @@ module LanguageOperator
|
|
|
316
353
|
prompt += "\n"
|
|
317
354
|
|
|
318
355
|
prompt += "## Response Format\n"
|
|
319
|
-
prompt += "
|
|
320
|
-
prompt += "Do NOT include any explanations, thinking, or text before or after the JSON.\n"
|
|
321
|
-
prompt += "Do NOT use [THINK] tags or any other markup.\n"
|
|
356
|
+
prompt += "You may include your reasoning in [THINK]...[/THINK] tags if helpful.\n"
|
|
322
357
|
prompt += "Use available tools as needed to complete the task.\n"
|
|
358
|
+
prompt += "After using tools (if needed), return your final answer as valid JSON matching the output schema above.\n"
|
|
359
|
+
prompt += "Your final JSON response should come after any tool calls and thinking.\n"
|
|
360
|
+
prompt += "Do not include explanations outside of [THINK] tags - only the JSON output.\n"
|
|
323
361
|
|
|
324
362
|
prompt
|
|
325
363
|
end
|
|
@@ -503,10 +541,11 @@ module LanguageOperator
|
|
|
503
541
|
end
|
|
504
542
|
|
|
505
543
|
execution_time = Time.now - attempt_start
|
|
506
|
-
logger.
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
544
|
+
logger.info('Task completed',
|
|
545
|
+
task: task_name,
|
|
546
|
+
attempt: attempt + 1,
|
|
547
|
+
execution_time: execution_time.round(3),
|
|
548
|
+
outputs: summarize_values(result))
|
|
510
549
|
|
|
511
550
|
result
|
|
512
551
|
rescue Timeout::Error => e
|
|
@@ -26,29 +26,40 @@ module LanguageOperator
|
|
|
26
26
|
#
|
|
27
27
|
# @return [void]
|
|
28
28
|
def configure
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
return unless ENV.fetch('OTEL_EXPORTER_OTLP_ENDPOINT', nil)
|
|
30
|
+
|
|
31
|
+
# Configure custom error handler for detailed logging
|
|
32
|
+
OpenTelemetry.error_handler = lambda do |exception: nil, message: nil|
|
|
33
|
+
if exception
|
|
34
|
+
warn "OpenTelemetry error: #{message} - #{exception.class}: #{exception.message}"
|
|
35
|
+
warn exception.backtrace.first(5).join("\n") if exception.backtrace
|
|
36
|
+
else
|
|
37
|
+
warn "OpenTelemetry error: #{message}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
31
40
|
|
|
41
|
+
# Initialize OpenTelemetry SDK with OTLP exporter
|
|
42
|
+
# Uses environment variables set by the operator:
|
|
43
|
+
# - OTEL_EXPORTER_OTLP_ENDPOINT: http://host:port
|
|
44
|
+
# - OTEL_SERVICE_NAME: service name
|
|
32
45
|
OpenTelemetry::SDK.configure do |c|
|
|
33
|
-
c.service_name = 'language-operator-agent'
|
|
34
|
-
c.service_version = LanguageOperator::VERSION
|
|
46
|
+
c.service_name = ENV.fetch('OTEL_SERVICE_NAME', 'language-operator-agent')
|
|
35
47
|
|
|
36
|
-
#
|
|
37
|
-
c.resource = OpenTelemetry::SDK::Resources::Resource.create(
|
|
38
|
-
build_resource_attributes
|
|
39
|
-
)
|
|
48
|
+
# Add resource attributes
|
|
49
|
+
c.resource = OpenTelemetry::SDK::Resources::Resource.create(build_resource_attributes)
|
|
40
50
|
|
|
41
|
-
#
|
|
51
|
+
# Use OTLP HTTP exporter (reads endpoint from OTEL_EXPORTER_OTLP_ENDPOINT env var)
|
|
42
52
|
c.add_span_processor(
|
|
43
53
|
OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
|
|
44
54
|
OpenTelemetry::Exporter::OTLP::Exporter.new(
|
|
45
|
-
endpoint:
|
|
55
|
+
endpoint: "#{ENV.fetch('OTEL_EXPORTER_OTLP_ENDPOINT')}/v1/traces",
|
|
56
|
+
headers: {}
|
|
46
57
|
)
|
|
47
58
|
)
|
|
48
59
|
)
|
|
49
60
|
end
|
|
50
61
|
|
|
51
|
-
# Restore trace context from TRACEPARENT if present
|
|
62
|
+
# Restore trace context from TRACEPARENT if present for distributed tracing
|
|
52
63
|
restore_trace_context if ENV['TRACEPARENT']
|
|
53
64
|
rescue StandardError => e
|
|
54
65
|
warn "Failed to configure OpenTelemetry: #{e.message}"
|
|
@@ -185,6 +185,9 @@ module LanguageOperator
|
|
|
185
185
|
|
|
186
186
|
logger.info('Scheduled execution completed - exiting',
|
|
187
187
|
agent_name: agent_def.name)
|
|
188
|
+
|
|
189
|
+
# Flush telemetry for short-lived processes
|
|
190
|
+
agent.send(:flush_telemetry)
|
|
188
191
|
when 'reactive', 'http', 'webhook'
|
|
189
192
|
# Start web server with webhooks, MCP tools, and chat endpoint
|
|
190
193
|
web_server = LanguageOperator::Agent::WebServer.new(agent)
|
|
@@ -20,7 +20,13 @@ module LanguageOperator
|
|
|
20
20
|
yield
|
|
21
21
|
rescue StandardError => e
|
|
22
22
|
Formatters::ProgressFormatter.error("Failed to #{operation}: #{e.message}")
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
# Show backtrace for debugging
|
|
25
|
+
if ENV['DEBUG']
|
|
26
|
+
puts "\nBacktrace:"
|
|
27
|
+
puts e.backtrace.join("\n")
|
|
28
|
+
raise
|
|
29
|
+
end
|
|
24
30
|
|
|
25
31
|
exit 1
|
|
26
32
|
end
|