Module: Omakase::Executor
- Defined in:
- lib/omakase/executor.rb
Overview
Runs model-written Ruby in the agent's own context. ponytail: instance_eval is not a sandbox — see Omakase.executor to swap it.
Defined Under Namespace
Classes: Answer
Constant Summary collapse
- SOURCE =
"(generated)"- RESULT =
:omakase_result- OUTPUT =
:omakase_output- TIMEOUT =
30- MAX_OUTPUT =
4_000
Class Method Summary collapse
- .call(agent, code, timeout: TIMEOUT) ⇒ Object
-
.capturing(io) ⇒ Object
Thread-local, so concurrent agents never share a buffer.
-
.failure(error, code) ⇒ Object
The model can only fix what it can locate, so point at the line.
- .observation(parts) ⇒ Object
Class Method Details
.call(agent, code, timeout: TIMEOUT) ⇒ Object
18 19 20 21 22 23 24 25 26 27 |
# File 'lib/omakase/executor.rb', line 18 def call(agent, code, timeout: TIMEOUT) printed = StringIO.new answer = catch(RESULT) do value = capturing(printed) { Timeout.timeout(timeout) { agent.instance_eval(code, SOURCE, 1) } } return observation([printed.string.chomp, "=> #{value.inspect}"]) end Answer.new(value: answer) rescue ScriptError, StandardError => e observation([printed.string.chomp, failure(e, code)]) end |
.capturing(io) ⇒ Object
Thread-local, so concurrent agents never share a buffer. Agent#puts reads it.
43 44 45 46 47 48 49 |
# File 'lib/omakase/executor.rb', line 43 def capturing(io) previous = Thread.current[OUTPUT] Thread.current[OUTPUT] = io yield ensure Thread.current[OUTPUT] = previous end |
.failure(error, code) ⇒ Object
The model can only fix what it can locate, so point at the line.
30 31 32 33 34 |
# File 'lib/omakase/executor.rb', line 30 def failure(error, code) line = error.backtrace&.grep(/\A#{Regexp.escape(SOURCE)}:\d+/)&.first&.slice(/:(\d+)/, 1)&.to_i source = code.lines[line - 1]&.strip if line&.positive? ["#{error.class}: #{error.}", ("line #{line}: #{source}" if source)].compact.join("\n") end |
.observation(parts) ⇒ Object
36 37 38 39 40 |
# File 'lib/omakase/executor.rb', line 36 def observation(parts) text = parts.reject(&:empty?).join("\n") text = "#{text[0, MAX_OUTPUT]}\n… (truncated)" if text.length > MAX_OUTPUT text.empty? ? "(no output)" : text end |