discourse-ai/lib/ai_bot/tools/javascript_evaluator.rb

107 lines
2.3 KiB
Ruby

# frozen_string_literal: true
require "mini_racer"
require "json"
module DiscourseAi
module AiBot
module Tools
class JavascriptEvaluator < Tool
TIMEOUT = 500
MAX_MEMORY = 10_000_000
MARSHAL_STACK_DEPTH = 20
def self.signature
{
name: name,
description: "Evaluates JavaScript code using MiniRacer",
parameters: [
{
name: "script",
description: "The JavaScript code to evaluate",
type: "string",
required: true,
},
],
}
end
def self.name
"javascript_evaluator"
end
def script
parameters[:script].to_s
end
def timeout
@timeout || TIMEOUT
end
def timeout=(value)
@timeout = value
end
def max_memory
@max_memory || MAX_MEMORY
end
def max_memory=(value)
@max_memory = value
end
def invoke
context =
MiniRacer::Context.new(
timeout: timeout,
max_memory: MAX_MEMORY,
marshal_stack_depth: MARSHAL_STACK_DEPTH,
)
# works around llms like anthropic loving console.log
eval_script = <<~JS
let console = {};
console.log = function(val) {
return val;
};
#{script}
JS
result = context.eval(eval_script)
# only do special handling and truncating for long strings
if result.to_s.length > 1000
result = truncate(result.to_s, max_length: 10_000, percent_length: 0.3, llm: llm)
end
{ result: result }
rescue MiniRacer::ScriptTerminatedError => e
{ error: "JavaScript execution timed out: #{e.message}" }
rescue MiniRacer::V8OutOfMemoryError => e
{ error: "JavaScript execution exceeded memory limit: #{e.message}" }
rescue MiniRacer::Error => e
{ error: "JavaScript execution error: #{e.message}" }
end
def details
<<~MD
```
#{script}
```
MD
end
private
def description_args
{ script: script }
end
end
end
end
end