Skip to content

Instantly share code, notes, and snippets.

@kieranklaassen
Created July 15, 2025 04:06
Show Gist options
  • Save kieranklaassen/804ba99f5f4bcae3af5705a12b0caab8 to your computer and use it in GitHub Desktop.
Save kieranklaassen/804ba99f5f4bcae3af5705a12b0caab8 to your computer and use it in GitHub Desktop.
class PromptReader
PROMPTS_DIR = Rails.root.join("app", "prompts")
# Reads the content of a prompt file or Leva::Prompt object and renders it with Liquid templating
#
# @example Read a prompt file without Liquid templating
# PromptReader.read("welcome_message")
#
# @example Read a prompt file with Liquid templating
# PromptReader.read("greeting", name: "John")
#
# @example Read a prompt file in a subfolder
# PromptReader.read("chatbot/welcome_message")
# PromptReader.read("summaries/newsletter_summary_prompt")
#
# @example Read a Leva::Prompt object with Liquid templating
# leva_prompt = Leva::Prompt.find(1)
# PromptReader.read(leva_prompt, name: "John")
#
# @param prompt [String, Leva::Prompt] the name of the prompt file to read (without extension) or a Leva::Prompt object
# Can include subfolder paths like "folder/prompt_name"
# @param options [Hash] optional parameters to render within the Liquid template
# @return [String, nil] the content of the file or Leva::Prompt, rendered with Liquid templating, or nil if file does not exist
def self.read(prompt, **options)
content = if prompt.is_a?(Leva::Prompt)
prompt.user_prompt
else
# Support for nested folders by directly joining the path with .md extension
file_path = PROMPTS_DIR.join("#{prompt}.md")
# If file doesn't exist, try to find it without adding .md (in case it already has an extension)
unless File.exist?(file_path)
alt_path = PROMPTS_DIR.join(prompt)
file_path = alt_path if File.exist?(alt_path)
end
# Log the file path for debugging
Rails.logger.debug("Looking for prompt at: #{file_path}")
File.exist?(file_path) ? File.read(file_path) : nil
end
# Log if content is nil
if content.nil?
Rails.logger.warn("Prompt content is nil for: #{prompt}")
return nil
end
if options.any?
template = Liquid::Template.parse(content)
# Ensure missing variables are left intact in the template
template_vars = options.transform_keys(&:to_s)
missing_keys = template.root.nodelist.select { |node| node.is_a?(Liquid::Variable) && !template_vars.key?(node.name.name) }.map { |node| node.name.name }
missing_keys.each { |key| template_vars[key] = "{{ #{key} }}" }
template.render(template_vars)
else
content
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment