ruby_llm-agents 0.2.0 → 0.2.2
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/lib/ruby_llm/agents/instrumentation.rb +82 -33
- data/lib/ruby_llm/agents/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b9c413f46174a6ad550e9ff2a676c92e116d6a8199cc3ff1c8ecb24e48b89b53
|
|
4
|
+
data.tar.gz: e4def5034b05f731418622879f15ee8e77be73534e239855612770874545da29
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9926a9f54daef58388cc5f6fabbacaf3dbaf3d03f590ef97dc81d180dd3ccb53153f78ec54c8a32247d1abc18c19084a04bf2b31b1378109dd76d159e84bff6a
|
|
7
|
+
data.tar.gz: 225a42a4f27f25f41b84521df11464e1bdb64ddbbddc6d8c7476d2039bddc9ddaa9ad96078aeb9e13bddd79ef1ba88a1697b8abb82a9b6239b8b5d00a836e8c0
|
|
@@ -28,9 +28,12 @@ module RubyLLM
|
|
|
28
28
|
|
|
29
29
|
# Wrap agent execution with metrics tracking
|
|
30
30
|
# Creates execution record at start with 'running' status, updates on completion
|
|
31
|
+
# Uses ensure block to guarantee status is updated even if complete_execution fails
|
|
31
32
|
def instrument_execution(&block)
|
|
32
33
|
started_at = Time.current
|
|
33
34
|
@last_response = nil
|
|
35
|
+
@execution_status_updated = false
|
|
36
|
+
original_error = nil
|
|
34
37
|
|
|
35
38
|
# Create execution record immediately with running status
|
|
36
39
|
execution = create_running_execution(started_at)
|
|
@@ -46,24 +49,34 @@ module RubyLLM
|
|
|
46
49
|
status: "success",
|
|
47
50
|
response: @last_response
|
|
48
51
|
)
|
|
52
|
+
@execution_status_updated = true
|
|
49
53
|
|
|
50
54
|
result
|
|
51
55
|
rescue Timeout::Error => e
|
|
56
|
+
original_error = e
|
|
52
57
|
complete_execution(
|
|
53
58
|
execution,
|
|
54
59
|
completed_at: Time.current,
|
|
55
60
|
status: "timeout",
|
|
56
61
|
error: e
|
|
57
62
|
)
|
|
63
|
+
@execution_status_updated = true
|
|
58
64
|
raise
|
|
59
65
|
rescue => e
|
|
66
|
+
original_error = e
|
|
60
67
|
complete_execution(
|
|
61
68
|
execution,
|
|
62
69
|
completed_at: Time.current,
|
|
63
70
|
status: "error",
|
|
64
71
|
error: e
|
|
65
72
|
)
|
|
73
|
+
@execution_status_updated = true
|
|
66
74
|
raise
|
|
75
|
+
ensure
|
|
76
|
+
# Guarantee execution is marked as error if complete_execution failed
|
|
77
|
+
unless @execution_status_updated
|
|
78
|
+
mark_execution_failed!(execution, error: original_error)
|
|
79
|
+
end
|
|
67
80
|
end
|
|
68
81
|
end
|
|
69
82
|
|
|
@@ -110,16 +123,11 @@ module RubyLLM
|
|
|
110
123
|
status: status
|
|
111
124
|
}
|
|
112
125
|
|
|
113
|
-
# Add response data if available
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
cached_tokens: response.cached_tokens || 0,
|
|
119
|
-
cache_creation_tokens: response.cache_creation_tokens || 0,
|
|
120
|
-
model_id: response.model_id || model,
|
|
121
|
-
response: serialize_response(response)
|
|
122
|
-
)
|
|
126
|
+
# Add response data if available (using safe extraction)
|
|
127
|
+
response_data = safe_extract_response_data(response)
|
|
128
|
+
if response_data.any?
|
|
129
|
+
update_data.merge!(response_data)
|
|
130
|
+
update_data[:model_id] ||= model
|
|
123
131
|
end
|
|
124
132
|
|
|
125
133
|
# Add error data if failed
|
|
@@ -134,11 +142,16 @@ module RubyLLM
|
|
|
134
142
|
|
|
135
143
|
# Calculate costs if token data is available
|
|
136
144
|
if execution.input_tokens && execution.output_tokens
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
begin
|
|
146
|
+
execution.calculate_costs!
|
|
147
|
+
execution.save!
|
|
148
|
+
rescue StandardError => cost_error
|
|
149
|
+
Rails.logger.warn("[RubyLLM::Agents] Cost calculation failed: #{cost_error.message}")
|
|
150
|
+
end
|
|
139
151
|
end
|
|
140
152
|
rescue StandardError => e
|
|
141
153
|
Rails.logger.error("[RubyLLM::Agents] Failed to update execution record: #{e.message}")
|
|
154
|
+
raise # Re-raise so ensure block can handle emergency update
|
|
142
155
|
end
|
|
143
156
|
|
|
144
157
|
# Fallback for when initial execution creation failed
|
|
@@ -158,15 +171,11 @@ module RubyLLM
|
|
|
158
171
|
user_prompt: safe_user_prompt
|
|
159
172
|
}
|
|
160
173
|
|
|
161
|
-
if
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
cache_creation_tokens: response.cache_creation_tokens || 0,
|
|
167
|
-
model_id: response.model_id || model,
|
|
168
|
-
response: serialize_response(response)
|
|
169
|
-
)
|
|
174
|
+
# Add response data if available (using safe extraction)
|
|
175
|
+
response_data = safe_extract_response_data(response)
|
|
176
|
+
if response_data.any?
|
|
177
|
+
execution_data.merge!(response_data)
|
|
178
|
+
execution_data[:model_id] ||= model
|
|
170
179
|
end
|
|
171
180
|
|
|
172
181
|
if error
|
|
@@ -208,18 +217,6 @@ module RubyLLM
|
|
|
208
217
|
end
|
|
209
218
|
end
|
|
210
219
|
|
|
211
|
-
# Serialize full RubyLLM::Message response to JSON
|
|
212
|
-
def serialize_response(response)
|
|
213
|
-
{
|
|
214
|
-
content: response.content,
|
|
215
|
-
model_id: response.model_id,
|
|
216
|
-
input_tokens: response.input_tokens,
|
|
217
|
-
output_tokens: response.output_tokens,
|
|
218
|
-
cached_tokens: response.cached_tokens,
|
|
219
|
-
cache_creation_tokens: response.cache_creation_tokens
|
|
220
|
-
}.compact
|
|
221
|
-
end
|
|
222
|
-
|
|
223
220
|
# Hook for subclasses to add custom metadata
|
|
224
221
|
def execution_metadata
|
|
225
222
|
{}
|
|
@@ -240,6 +237,58 @@ module RubyLLM
|
|
|
240
237
|
Rails.logger.warn("[RubyLLM::Agents] Could not capture user_prompt: #{e.message}")
|
|
241
238
|
nil
|
|
242
239
|
end
|
|
240
|
+
|
|
241
|
+
# Safely extract a value from response, returning default if method doesn't exist
|
|
242
|
+
def safe_response_value(response, method, default = nil)
|
|
243
|
+
return default unless response.respond_to?(method)
|
|
244
|
+
response.public_send(method)
|
|
245
|
+
rescue StandardError
|
|
246
|
+
default
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# Safely extract all response data with fallbacks
|
|
250
|
+
def safe_extract_response_data(response)
|
|
251
|
+
return {} unless response.is_a?(RubyLLM::Message)
|
|
252
|
+
|
|
253
|
+
{
|
|
254
|
+
input_tokens: safe_response_value(response, :input_tokens),
|
|
255
|
+
output_tokens: safe_response_value(response, :output_tokens),
|
|
256
|
+
cached_tokens: safe_response_value(response, :cached_tokens, 0),
|
|
257
|
+
cache_creation_tokens: safe_response_value(response, :cache_creation_tokens, 0),
|
|
258
|
+
model_id: safe_response_value(response, :model_id),
|
|
259
|
+
response: safe_serialize_response(response)
|
|
260
|
+
}.compact
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Safe version of serialize_response
|
|
264
|
+
def safe_serialize_response(response)
|
|
265
|
+
{
|
|
266
|
+
content: safe_response_value(response, :content),
|
|
267
|
+
model_id: safe_response_value(response, :model_id),
|
|
268
|
+
input_tokens: safe_response_value(response, :input_tokens),
|
|
269
|
+
output_tokens: safe_response_value(response, :output_tokens),
|
|
270
|
+
cached_tokens: safe_response_value(response, :cached_tokens, 0),
|
|
271
|
+
cache_creation_tokens: safe_response_value(response, :cache_creation_tokens, 0)
|
|
272
|
+
}.compact
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Emergency fallback - mark execution as error using update_columns
|
|
276
|
+
# Bypasses callbacks/validations to ensure status is always updated
|
|
277
|
+
def mark_execution_failed!(execution, error: nil)
|
|
278
|
+
return unless execution&.id
|
|
279
|
+
return unless execution.status == "running"
|
|
280
|
+
|
|
281
|
+
update_data = {
|
|
282
|
+
status: "error",
|
|
283
|
+
completed_at: Time.current,
|
|
284
|
+
error_class: error&.class&.name || "InstrumentationError",
|
|
285
|
+
error_message: (error&.message || "Execution status update failed").to_s.truncate(65535)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
execution.class.where(id: execution.id, status: "running").update_all(update_data)
|
|
289
|
+
rescue StandardError => e
|
|
290
|
+
Rails.logger.error("[RubyLLM::Agents] CRITICAL: Failed emergency status update for execution #{execution&.id}: #{e.message}")
|
|
291
|
+
end
|
|
243
292
|
end
|
|
244
293
|
end
|
|
245
294
|
end
|