fix: captain liquid render file system (#13647)

This commit is contained in:
Aakash Bakhle
2026-02-25 20:19:34 +05:30
committed by GitHub
parent 5aef9d2dd0
commit efe49f7da4
4 changed files with 43 additions and 6 deletions

View File

@@ -5,7 +5,7 @@ class Captain::PromptRenderer
def render(template_name, context = {})
template = load_template(template_name)
liquid_template = Liquid::Template.parse(template)
liquid_template.render(stringify_keys(context))
liquid_template.render(stringify_keys(context), registers: { file_system: snippet_file_system })
end
private
@@ -18,6 +18,13 @@ class Captain::PromptRenderer
File.read(template_path)
end
def snippet_file_system
@snippet_file_system ||= Liquid::LocalFileSystem.new(
Rails.root.join('enterprise/lib/captain/prompts/snippets'),
'%s.liquid'
)
end
def stringify_keys(hash)
hash.deep_stringify_keys
end

View File

@@ -14,11 +14,11 @@ Don't digress away from your instructions, and use all the available tools at yo
Here's the metadata we have about the current conversation and the contact associated with it:
{% if conversation -%}
{% render 'conversation' %}
{% render 'conversation', conversation: conversation %}
{% endif -%}
{% if contact -%}
{% render 'contact' %}
{% render 'contact', contact: contact %}
{% endif -%}
{% if campaign.id -%}

View File

@@ -14,11 +14,11 @@ If you believe the user's request is not within the scope of your role, you can
Here's the metadata we have about the current conversation and the contact associated with it:
{% if conversation -%}
{% render 'conversation' %}
{% render 'conversation', conversation: conversation %}
{% endif -%}
{% if contact -%}
{% render 'contact' %}
{% render 'contact', contact: contact %}
{% endif -%}
{% if campaign.id -%}

View File

@@ -58,7 +58,7 @@ RSpec.describe Captain::PromptRenderer do
it 'loads and parses liquid template' do
liquid_template_double = instance_double(Liquid::Template)
allow(Liquid::Template).to receive(:parse).with(template_content).and_return(liquid_template_double)
allow(liquid_template_double).to receive(:render).with(hash_including('name', 'balance')).and_return('rendered')
allow(liquid_template_double).to receive(:render).with(hash_including('name', 'balance'), anything).and_return('rendered')
result = described_class.render(template_name, context)
@@ -67,6 +67,36 @@ RSpec.describe Captain::PromptRenderer do
end
end
describe 'snippet rendering' do
let(:snippets_dir) { Rails.root.join('enterprise/lib/captain/prompts/snippets') }
let(:snippet_path) { snippets_dir.join('greeting.liquid') }
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:read).and_call_original
allow(File).to receive(:exist?).with(template_path).and_return(true)
# Create a controlled snippet to decouple from real snippet content
allow(File).to receive(:exist?).with(snippet_path.to_s).and_return(true)
allow(File).to receive(:read).with(snippet_path.to_s).and_return('Hello {{ name }}')
end
it 'resolves render tags from the snippets directory' do
allow(File).to receive(:read).with(template_path).and_return("{% render 'greeting', name: name %}")
result = described_class.render(template_name, { name: 'World' })
expect(result).to eq('Hello World')
end
it 'outputs a liquid error for missing snippets' do
allow(File).to receive(:read).with(template_path).and_return("{% render 'nonexistent' %}")
result = described_class.render(template_name, {})
expect(result).to include('Liquid error')
end
end
describe '.load_template' do
it 'reads template file from correct path' do
described_class.send(:load_template, template_name)