-
Notifications
You must be signed in to change notification settings - Fork 4
Description
Bug Description
Core::Base#execute overrides BaseAgent#execute but does not set Thread.current[:ruby_llm_agents_caller_context], so tools never receive the execution context. This means context is always nil inside any RubyLLM::Agents::Tool subclass, and agent params (like workspace_path) are inaccessible.
Versions
ruby_llm-agents3.11.0 (also present in 3.10.0)ruby_llm1.13.2- Ruby 4.0.1
Root Cause
BaseAgent#execute (base_agent.rb:810) correctly wraps the LLM call:
def execute(context)
previous_context = Thread.current[:ruby_llm_agents_caller_context]
Thread.current[:ruby_llm_agents_caller_context] = context
# ... build_client, execute_llm_call ...
ensure
Thread.current[:ruby_llm_agents_caller_context] = previous_context
endCore::Base#execute (core/base.rb:74) overrides this without calling super and without setting the thread-local:
def execute(context)
@execution_started_at = context.started_at || Time.current
run_callbacks(:before, context)
client = build_client(context)
response = execute_llm_call(client, context)
# ... no Thread.current assignment anywhere ...
endSince RubyLLM::Agents::Base includes Core::Base, method resolution picks Core::Base#execute over BaseAgent#execute. The thread-local is never set.
Impact
In RubyLLM::Agents::Tool#call (tool.rb:69):
pipeline_context = Thread.current[:ruby_llm_agents_caller_context]
@context = pipeline_context ? ToolContext.new(pipeline_context) : nilpipeline_context is always nil, so:
contextisnilin all toolscontext.workspace_path(or any agent param) raises or returns nil- Tool execution tracking (
start_tool_tracking) is skipped (noexecution_id)
Reproduction
class MyTool < RubyLLM::Agents::Tool
description "Debug tool"
param :path, desc: "A path", required: true
def execute(path:)
puts "context: #{context.inspect}" # => nil
puts "context.workspace_path: #{context&.workspace_path}" # => nil
"done"
end
end
class MyAgent < RubyLLM::Agents::Base
param :workspace_path, required: true
param :query, required: true
tools [MyTool]
system "You are a helpful assistant."
user "{query}"
end
MyAgent.call(query: "Use MyTool with path 'test'", workspace_path: "/tmp/test")
# => MyTool#execute runs but context is nilWorkaround
Override execute in your ApplicationAgent to set the thread-local:
class ApplicationAgent < RubyLLM::Agents::Base
def execute(context)
previous = Thread.current[:ruby_llm_agents_caller_context]
Thread.current[:ruby_llm_agents_caller_context] = context
super
ensure
Thread.current[:ruby_llm_agents_caller_context] = previous
end
endSuggested Fix
Add the thread-local context assignment to Core::Base#execute, or have it call super to inherit the wrapping from BaseAgent#execute.