rails_console_ai 0.26.0 → 0.27.0
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
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: db0c0b3b95cc349906845d4058e1eadc6be4ab67e25f32a4b8e4262e61340a87
|
|
4
|
+
data.tar.gz: 0ba79ae87d8975193b2b6033279dc41a8841cf71e7f015ce8f36dafa6d62d463
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 615da46e5aa24149783309d69a3b739db1170459c1bf6d929c8ecfc875fbc0654117a7c5d15057aaf7d421660afa9959192f4112b25e722e5e224ce3de768fb0
|
|
7
|
+
data.tar.gz: cf807c3dfc0fae08a79013606c5e00383bc26994461c02e4135f8efb495540a0f677bff62eae01c2f396e9ce2de8ede7c82a95415b69831425447aa93361dbdb
|
|
@@ -249,7 +249,7 @@ module RailsConsoleAi
|
|
|
249
249
|
output_id = @executor.store_output(result_str)
|
|
250
250
|
if result_str.length > LARGE_OUTPUT_THRESHOLD
|
|
251
251
|
preview = result_str[0, LARGE_OUTPUT_PREVIEW_CHARS]
|
|
252
|
-
context_msg += "\n#{preview}\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use
|
|
252
|
+
context_msg += "\n#{preview}\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use explore_output with output_id=#{output_id} for focused queries, or recall_output to expand in place]"
|
|
253
253
|
elsif !output_parts.empty?
|
|
254
254
|
context_msg += "\n#{result_str}"
|
|
255
255
|
end
|
|
@@ -332,7 +332,7 @@ module RailsConsoleAi
|
|
|
332
332
|
context_msg = "Code was executed (safety override). "
|
|
333
333
|
if result_str.length > LARGE_OUTPUT_THRESHOLD
|
|
334
334
|
context_msg += result_str[0, LARGE_OUTPUT_PREVIEW_CHARS]
|
|
335
|
-
context_msg += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use
|
|
335
|
+
context_msg += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use explore_output with output_id=#{output_id} for focused queries, or recall_output to expand in place]"
|
|
336
336
|
else
|
|
337
337
|
context_msg += result_str
|
|
338
338
|
end
|
|
@@ -360,7 +360,7 @@ module RailsConsoleAi
|
|
|
360
360
|
context_msg = "Code was executed. "
|
|
361
361
|
if result_str.length > LARGE_OUTPUT_THRESHOLD
|
|
362
362
|
context_msg += result_str[0, LARGE_OUTPUT_PREVIEW_CHARS]
|
|
363
|
-
context_msg += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use
|
|
363
|
+
context_msg += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{result_str.length} chars — use explore_output with output_id=#{output_id} for focused queries, or recall_output to expand in place]"
|
|
364
364
|
else
|
|
365
365
|
context_msg += result_str
|
|
366
366
|
end
|
|
@@ -903,7 +903,7 @@ module RailsConsoleAi
|
|
|
903
903
|
tool_msg[:output_id] = output_id
|
|
904
904
|
if full_text.length > LARGE_OUTPUT_THRESHOLD
|
|
905
905
|
truncated = full_text[0, LARGE_OUTPUT_PREVIEW_CHARS]
|
|
906
|
-
truncated += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{full_text.length} chars — use
|
|
906
|
+
truncated += "\n\n[Output truncated at #{LARGE_OUTPUT_PREVIEW_CHARS} of #{full_text.length} chars — use explore_output with output_id=#{output_id} for focused queries, or recall_output to expand in place]"
|
|
907
907
|
tool_msg = provider.format_tool_result(tc[:id], truncated)
|
|
908
908
|
tool_msg[:output_id] = output_id
|
|
909
909
|
end
|
|
@@ -1041,6 +1041,10 @@ module RailsConsoleAi
|
|
|
1041
1041
|
when 'save_skill' then "(\"#{args['name']}\")"
|
|
1042
1042
|
when 'delete_skill' then "(\"#{args['name']}\")"
|
|
1043
1043
|
when 'recall_output' then "(#{args['id']})"
|
|
1044
|
+
when 'explore_output'
|
|
1045
|
+
task_preview = args['task'].to_s[0, 80]
|
|
1046
|
+
task_preview += '...' if args['task'].to_s.length > 80
|
|
1047
|
+
"(id: #{args['output_id']}, \"#{task_preview}\")"
|
|
1044
1048
|
when 'execute_plan'
|
|
1045
1049
|
steps = args['steps']
|
|
1046
1050
|
steps ? "(#{steps.length} steps)" : ''
|
|
@@ -1409,7 +1413,7 @@ module RailsConsoleAi
|
|
|
1409
1413
|
end
|
|
1410
1414
|
|
|
1411
1415
|
def trim_message(msg)
|
|
1412
|
-
ref = "[Output omitted — use
|
|
1416
|
+
ref = "[Output omitted — use explore_output with output_id=#{msg[:output_id]} for focused queries, or recall_output to expand in place]"
|
|
1413
1417
|
|
|
1414
1418
|
if msg[:content].is_a?(Array)
|
|
1415
1419
|
trimmed_content = msg[:content].map do |block|
|
|
@@ -12,12 +12,15 @@ module RailsConsoleAi
|
|
|
12
12
|
|
|
13
13
|
attr_reader :input_tokens, :output_tokens, :model_used
|
|
14
14
|
|
|
15
|
-
def initialize(task:, agent_config:, binding_context:, parent_channel:, executor
|
|
15
|
+
def initialize(task:, agent_config:, binding_context:, parent_channel:, executor:,
|
|
16
|
+
output_payload: nil, output_local_name: :output)
|
|
16
17
|
@task = task
|
|
17
18
|
@agent_config = agent_config || {}
|
|
18
19
|
@binding_context = binding_context
|
|
19
20
|
@parent_channel = parent_channel
|
|
20
21
|
@parent_executor = executor
|
|
22
|
+
@output_payload = output_payload
|
|
23
|
+
@output_local_name = output_local_name
|
|
21
24
|
@input_tokens = 0
|
|
22
25
|
@output_tokens = 0
|
|
23
26
|
@model_used = nil
|
|
@@ -29,7 +32,16 @@ module RailsConsoleAi
|
|
|
29
32
|
task_label: @agent_config['name']
|
|
30
33
|
)
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
effective_binding =
|
|
36
|
+
if @output_payload
|
|
37
|
+
b = @binding_context.eval("proc { binding }.call")
|
|
38
|
+
b.local_variable_set(@output_local_name, @output_payload)
|
|
39
|
+
b
|
|
40
|
+
else
|
|
41
|
+
@binding_context
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
executor = Executor.new(effective_binding, channel: channel)
|
|
33
45
|
allowed_tools = @agent_config['tools'] ? Array(@agent_config['tools']) : nil
|
|
34
46
|
tools = Tools::Registry.new(executor: executor, mode: :sub_agent, channel: channel, allowed_tools: allowed_tools)
|
|
35
47
|
provider = build_provider
|
|
@@ -6,7 +6,7 @@ module RailsConsoleAi
|
|
|
6
6
|
attr_reader :definitions, :last_sub_agent_usage
|
|
7
7
|
|
|
8
8
|
# Tools that should never be cached (side effects or user interaction)
|
|
9
|
-
NO_CACHE = %w[ask_user save_memory delete_memory recall_memory execute_code execute_plan activate_skill save_skill delete_skill delegate_task].freeze
|
|
9
|
+
NO_CACHE = %w[ask_user save_memory delete_memory recall_memory execute_code execute_plan activate_skill save_skill delete_skill delegate_task explore_output].freeze
|
|
10
10
|
|
|
11
11
|
def initialize(executor: nil, mode: :default, channel: nil, allowed_tools: nil)
|
|
12
12
|
@executor = executor
|
|
@@ -188,7 +188,7 @@ module RailsConsoleAi
|
|
|
188
188
|
if @executor
|
|
189
189
|
register(
|
|
190
190
|
name: 'recall_output',
|
|
191
|
-
description: '
|
|
191
|
+
description: 'Expand a previously omitted/truncated output back into this conversation\'s context, where it will persist for the rest of the session. Prefer `explore_output` if you only need a specific answer about the output — that keeps this conversation lean. Use `recall_output` only when you need the full content alongside other context here. Use the output id shown in the "[Output omitted]" or "[Output truncated]" placeholder.',
|
|
192
192
|
parameters: {
|
|
193
193
|
'type' => 'object',
|
|
194
194
|
'properties' => {
|
|
@@ -204,7 +204,7 @@ module RailsConsoleAi
|
|
|
204
204
|
|
|
205
205
|
register(
|
|
206
206
|
name: 'recall_outputs',
|
|
207
|
-
description: '
|
|
207
|
+
description: 'Expand multiple previously omitted outputs back into this conversation. Prefer `explore_output` per-id for focused queries. Use the output ids shown in "[Output omitted]" or "[Output truncated]" placeholders.',
|
|
208
208
|
parameters: {
|
|
209
209
|
'type' => 'object',
|
|
210
210
|
'properties' => {
|
|
@@ -214,6 +214,22 @@ module RailsConsoleAi
|
|
|
214
214
|
},
|
|
215
215
|
handler: ->(args) { "recall_outputs handled by conversation engine" }
|
|
216
216
|
)
|
|
217
|
+
|
|
218
|
+
if @mode != :sub_agent
|
|
219
|
+
register(
|
|
220
|
+
name: 'explore_output',
|
|
221
|
+
description: 'Prefer this over recall_output when you have a specific question about a large omitted/truncated output (e.g. "find the item where X", "how many match Y", "what is the value at index N", "parse the JSON and return field Z"). Spawns a sub-agent with the full output bound to the local Ruby variable `output` (a String); the sub-agent runs execute_code against it and returns a concise answer. The full output does NOT enter this conversation.',
|
|
222
|
+
parameters: {
|
|
223
|
+
'type' => 'object',
|
|
224
|
+
'properties' => {
|
|
225
|
+
'output_id' => { 'type' => 'integer', 'description' => 'The output id shown in the "[Output omitted]" or "[Output truncated]" placeholder.' },
|
|
226
|
+
'task' => { 'type' => 'string', 'description' => 'The specific question or task. Be concrete — the sub-agent only sees this task and the output.' }
|
|
227
|
+
},
|
|
228
|
+
'required' => ['output_id', 'task']
|
|
229
|
+
},
|
|
230
|
+
handler: ->(args) { explore_output(args['output_id'].to_i, args['task']) }
|
|
231
|
+
)
|
|
232
|
+
end
|
|
217
233
|
end
|
|
218
234
|
|
|
219
235
|
unless @mode == :init
|
|
@@ -317,6 +333,45 @@ module RailsConsoleAi
|
|
|
317
333
|
)
|
|
318
334
|
end
|
|
319
335
|
|
|
336
|
+
EXPLORE_OUTPUT_AGENT_CONFIG = {
|
|
337
|
+
'name' => 'output-explorer',
|
|
338
|
+
'tools' => ['execute_code'],
|
|
339
|
+
'max_rounds' => 8,
|
|
340
|
+
'body' => <<~PROMPT.freeze
|
|
341
|
+
You are exploring a single chunk of captured tool output on behalf of the main assistant.
|
|
342
|
+
|
|
343
|
+
The full output is bound to the local variable `output` (a String). You do NOT see it
|
|
344
|
+
directly — it lives in Ruby memory. Use `execute_code` with Ruby to query it:
|
|
345
|
+
- `output.length`, `output.lines.count`
|
|
346
|
+
- `output[start, len]`, `output.lines[n]`
|
|
347
|
+
- `output.scan(/pattern/)`, `output.include?("...")`
|
|
348
|
+
- `JSON.parse(output)` if it looks like JSON, then drill in
|
|
349
|
+
- any other Ruby string/collection methods
|
|
350
|
+
|
|
351
|
+
Print only the specific slice or summary the task requires — never dump the whole `output`.
|
|
352
|
+
Return a concise factual answer. No preamble.
|
|
353
|
+
PROMPT
|
|
354
|
+
}.freeze
|
|
355
|
+
|
|
356
|
+
def explore_output(output_id, task)
|
|
357
|
+
require 'rails_console_ai/sub_agent'
|
|
358
|
+
|
|
359
|
+
payload = @executor.recall_output(output_id)
|
|
360
|
+
return "No output found with id #{output_id}" unless payload
|
|
361
|
+
|
|
362
|
+
sub = SubAgent.new(
|
|
363
|
+
task: task,
|
|
364
|
+
agent_config: EXPLORE_OUTPUT_AGENT_CONFIG,
|
|
365
|
+
binding_context: @executor.binding_context,
|
|
366
|
+
parent_channel: @channel,
|
|
367
|
+
executor: @executor,
|
|
368
|
+
output_payload: payload.dup
|
|
369
|
+
)
|
|
370
|
+
result = sub.run
|
|
371
|
+
@last_sub_agent_usage = { input: sub.input_tokens, output: sub.output_tokens, model: sub.model_used }
|
|
372
|
+
"Exploration result (#{sub.input_tokens + sub.output_tokens} tokens used, #{payload.length} chars explored):\n#{result}"
|
|
373
|
+
end
|
|
374
|
+
|
|
320
375
|
def delegate_task(task, agent_name = nil)
|
|
321
376
|
require 'rails_console_ai/sub_agent'
|
|
322
377
|
require 'rails_console_ai/agent_loader'
|