Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/prompts/test/prompt_with_metadata.en.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
version: 1.0
author: Test Author
change_notes: Initial version
---
This is a test prompt with metadata.
39 changes: 37 additions & 2 deletions lib/promptly.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,26 @@
require_relative "promptly/cache"
require_relative "promptly/helper"
require_relative "promptly/validator"
require "yaml"

module Promptly
class Error < StandardError; end

class Prompt
attr_reader :content, :version, :author, :change_notes

def initialize(content:, version: nil, author: nil, change_notes: nil)
@content = content
@version = version
@author = author
@change_notes = change_notes
end

def to_s
content
end
end

def self.render_template(template, locals: {}, engine: :erb)
Renderer.render(template, locals: locals, engine: engine)
end
Expand Down Expand Up @@ -51,8 +67,27 @@ def self.render(identifier, locale: nil, locals: {}, cache: true, ttl: nil)
raise Error, "Template not found for '#{identifier}' (locale: #{locale.inspect}) under #{prompts_path}" unless path

engine = Locator.engine_for(path)
template = File.read(path)
Renderer.render(template, locals: locals, engine: engine)
file_content = File.read(path)

# Extract YAML front matter
match = file_content.match(/\A---\n(.*)
---\s*\n/m)
if match
metadata = YAML.safe_load(match[1])
template = match.post_match
else
metadata = {}
template = file_content
end

content = Renderer.render(template, locals: locals, engine: engine)

Prompt.new(
content: content,
version: metadata["version"],
author: metadata["author"],
change_notes: metadata["change_notes"]
)
end

def self.default_prompts_path
Expand Down
8 changes: 8 additions & 0 deletions spec/prompt_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@
it "renders the prompt" do
expect("This is a test prompt.").to expect_prompt_render("test/prompt")
end

it "renders the prompt with metadata" do
prompt = Promptly.render("test/prompt_with_metadata")
expect(prompt.to_s).to eq("This is a test prompt with metadata.")
expect(prompt.version).to eq(1.0)
expect(prompt.author).to eq("Test Author")
expect(prompt.change_notes).to eq("Initial version")
end
end
end
12 changes: 6 additions & 6 deletions spec/render_with_cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@ def keys

# First call should read file and cache result
result1 = Promptly.render("test", locals: {name: "John"})
expect(result1).to eq("Hello John!")
expect(result1.content).to eq("Hello John!")
expect(cache_store.size).to eq(1)

# Second call should use cached result
result2 = Promptly.render("test", locals: {name: "John"})
expect(result2).to eq("Hello John!")
expect(result2.content).to eq("Hello John!")

# Verify file was only read once by checking cache was used
cache_key = cache_store.keys.first
expect(cache_store.read(cache_key)).to eq("Hello John!")
expect(cache_store.read(cache_key).content).to eq("Hello John!")
end

it "generates different cache keys for different parameters" do
Expand Down Expand Up @@ -105,7 +105,7 @@ def keys

# This should not use cache
result = Promptly.render("test", locals: {name: "John"}, cache: false)
expect(result).to eq("Hello John!")
expect(result.content).to eq("Hello John!")
expect(cache_store.size).to eq(0)
end

Expand All @@ -122,8 +122,8 @@ def keys
result_en = Promptly.render("greeting", locale: :en, locals: {name: "John"})
result_es = Promptly.render("greeting", locale: :es, locals: {name: "Juan"})

expect(result_en).to eq("Hello John!")
expect(result_es).to eq("¡Hola Juan!")
expect(result_en.content).to eq("Hello John!")
expect(result_es.content).to eq("¡Hola Juan!")
expect(cache_store.size).to eq(2)
end
end
2 changes: 1 addition & 1 deletion spec/support/prompt_helper.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module PromptHelper
def expect_prompt_render(identifier, locals: {})
RSpec::Matchers::BuiltIn::Include.new(Promptly.render(identifier, locals: locals))
RSpec::Matchers::BuiltIn::Include.new(Promptly.render(identifier, locals: locals).to_s)
end
end
34 changes: 34 additions & 0 deletions wiki/Prompt-Version-Metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Prompt Version Metadata

Promptly allows you to add optional metadata fields to your prompt templates, such as `version`, `author`, and `change_notes`. This metadata can be useful for tracking changes to your prompts and for documentation purposes.

### Adding Metadata to Prompts

To add metadata to a prompt, include a YAML front matter block at the beginning of your template file. The front matter block must start and end with `---`.

**Example:**

```erb
---
version: 1.0
author: John Doe
change_notes: Initial version of the welcome email.
---
You are a friendly customer success manager writing a personalized welcome email.
...
```

### Accessing Metadata

When you render a prompt, the `Promptly.render` method returns a `Prompt` object. This object contains the rendered content of the prompt, as well as the metadata fields.

**Example:**

```ruby
prompt = Promptly.render("user_onboarding/welcome_email")

puts prompt.content # The rendered content of the email
puts prompt.version # 1.0
puts prompt.author # John Doe
puts prompt.change_notes # Initial version of the welcome email.
```