spectre_ai 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/README.md +31 -0
- data/lib/spectre/prompt.rb +106 -94
- data/lib/spectre/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7c3acf59b77ad62e0095fb7f91aa0491a50f25d197f94c788ad5ce2bbefbf6f
|
4
|
+
data.tar.gz: 48b4a9dcda9a013a6dde32ca5008a7dd33a49f4d4678952b8fcbf2089d2ccca6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be7bf9f1570bad924509a8b8ad2a4671d019d20325696c4a2587e586f4a9314e395d822857cf9a277c84fd69e1d61e1231b356405266b5147187d6ec01d7dd33
|
7
|
+
data.tar.gz: f80dddebe6a99f7c946a8364f62f15112a974ad6cffe5a031baf5c1a9b151f39c12b870c60945277b8c57e78e51a0f0bb7147c620af11e4a59ed1ce485bfebab
|
data/CHANGELOG.md
CHANGED
@@ -66,4 +66,18 @@ Code Refactoring:
|
|
66
66
|
|
67
67
|
**Documentation:** Updated class-level documentation and method comments for better clarity and understanding of the class’s functionality and usage.
|
68
68
|
|
69
|
-
This version enhances the flexibility and robustness of the Completions class, enabling more complex interactions and better error handling for different types of API responses.
|
69
|
+
This version enhances the flexibility and robustness of the Completions class, enabling more complex interactions and better error handling for different types of API responses.
|
70
|
+
|
71
|
+
# Changelog for Version 1.1.1
|
72
|
+
|
73
|
+
**Release Date:** [11th Oct 2024]
|
74
|
+
|
75
|
+
**New Features:**
|
76
|
+
|
77
|
+
* **Nested Template Support in Prompts**
|
78
|
+
* You can now organize your prompt files in nested directories and render them using the `Spectre::Prompt.render` method.
|
79
|
+
* **Example**: To render a template from a nested folder:
|
80
|
+
```ruby
|
81
|
+
Spectre::Prompt.render(template: 'classification/intent/user', locals: { query: 'What is AI?' })
|
82
|
+
```
|
83
|
+
* This feature allows for better organization and scalability when dealing with multiple prompt categories and complex scenarios.
|
data/README.md
CHANGED
@@ -377,6 +377,37 @@ Spectre::Prompt.render(
|
|
377
377
|
- **`template`:** The path to the prompt template file (e.g., `rag/system`).
|
378
378
|
- **`locals`:** A hash of variables to be used inside the ERB template.
|
379
379
|
|
380
|
+
**Using Nested Templates for Prompts**
|
381
|
+
|
382
|
+
Spectre's `Prompt` class now supports rendering templates from nested directories. This allows you to better organize your prompt files in a structured folder hierarchy.
|
383
|
+
|
384
|
+
You can organize your prompt templates in subfolders. For instance, you can have the following structure:
|
385
|
+
|
386
|
+
```
|
387
|
+
app/
|
388
|
+
spectre/
|
389
|
+
prompts/
|
390
|
+
rag/
|
391
|
+
system.yml.erb
|
392
|
+
user.yml.erb
|
393
|
+
classification/
|
394
|
+
intent/
|
395
|
+
system.yml.erb
|
396
|
+
user.yml.erb
|
397
|
+
entity/
|
398
|
+
system.yml.erb
|
399
|
+
user.yml.erb
|
400
|
+
```
|
401
|
+
|
402
|
+
To render a prompt from a nested folder, simply pass the full path to the `template` argument:
|
403
|
+
|
404
|
+
```ruby
|
405
|
+
# Rendering from a nested folder
|
406
|
+
Spectre::Prompt.render(template: 'classification/intent/user', locals: { query: 'What is AI?' })
|
407
|
+
```
|
408
|
+
|
409
|
+
This allows for more flexibility when organizing your prompt files, particularly when dealing with complex scenarios or multiple prompt categories.
|
410
|
+
|
380
411
|
**Combining Completions with Prompts**
|
381
412
|
|
382
413
|
You can also combine completions and prompts like so:
|
data/lib/spectre/prompt.rb
CHANGED
@@ -5,107 +5,119 @@ require 'yaml'
|
|
5
5
|
|
6
6
|
module Spectre
|
7
7
|
class Prompt
|
8
|
-
|
9
|
-
|
10
|
-
# Render a prompt by reading and rendering the YAML template
|
11
|
-
#
|
12
|
-
# @param template [String] The path to the template file, formatted as 'type/prompt' (e.g., 'rag/system')
|
13
|
-
# @param locals [Hash] Variables to be passed to the template for rendering
|
14
|
-
#
|
15
|
-
# @return [String] Rendered prompt
|
16
|
-
def self.render(template:, locals: {})
|
17
|
-
type, prompt = split_template(template)
|
18
|
-
file_path = prompt_file_path(type, prompt)
|
19
|
-
|
20
|
-
raise "Prompt file not found: #{file_path}" unless File.exist?(file_path)
|
21
|
-
|
22
|
-
# Preprocess the locals before rendering the YAML file
|
23
|
-
preprocessed_locals = preprocess_locals(locals)
|
24
|
-
|
25
|
-
template_content = File.read(file_path)
|
26
|
-
erb_template = ERB.new(template_content)
|
27
|
-
|
28
|
-
context = Context.new(preprocessed_locals)
|
29
|
-
rendered_prompt = erb_template.result(context.get_binding)
|
30
|
-
|
31
|
-
# YAML.safe_load returns a hash, so fetch the correct part based on the prompt
|
32
|
-
parsed_yaml = YAML.safe_load(rendered_prompt)[prompt]
|
33
|
-
|
34
|
-
# Convert special characters back after YAML processing
|
35
|
-
convert_special_chars_back(parsed_yaml)
|
36
|
-
rescue Errno::ENOENT
|
37
|
-
raise "Template file not found at path: #{file_path}"
|
38
|
-
rescue Psych::SyntaxError => e
|
39
|
-
raise "YAML Syntax Error in file #{file_path}: #{e.message}"
|
40
|
-
rescue StandardError => e
|
41
|
-
raise "Error rendering prompt for template '#{template}': #{e.message}"
|
42
|
-
end
|
8
|
+
class << self
|
9
|
+
attr_reader :prompts_path
|
43
10
|
|
44
|
-
|
11
|
+
def prompts_path
|
12
|
+
@prompts_path ||= detect_prompts_path
|
13
|
+
end
|
45
14
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
15
|
+
# Render a prompt by reading and rendering the YAML template
|
16
|
+
#
|
17
|
+
# @param template [String] The path to the template file, formatted as 'folder1/folder2/prompt'
|
18
|
+
# @param locals [Hash] Variables to be passed to the template for rendering
|
19
|
+
#
|
20
|
+
# @return [String] Rendered prompt
|
21
|
+
def render(template:, locals: {})
|
22
|
+
path, prompt = split_template(template)
|
23
|
+
file_path = prompt_file_path(path, prompt)
|
24
|
+
|
25
|
+
raise "Prompt file not found: #{file_path}" unless File.exist?(file_path)
|
26
|
+
|
27
|
+
# Preprocess the locals before rendering the YAML file
|
28
|
+
preprocessed_locals = preprocess_locals(locals)
|
29
|
+
|
30
|
+
template_content = File.read(file_path)
|
31
|
+
erb_template = ERB.new(template_content)
|
32
|
+
|
33
|
+
context = Context.new(preprocessed_locals)
|
34
|
+
rendered_prompt = erb_template.result(context.get_binding)
|
35
|
+
|
36
|
+
# YAML.safe_load returns a hash, so fetch the correct part based on the prompt
|
37
|
+
parsed_yaml = YAML.safe_load(rendered_prompt)[prompt]
|
38
|
+
|
39
|
+
# Convert special characters back after YAML processing
|
40
|
+
convert_special_chars_back(parsed_yaml)
|
41
|
+
rescue Errno::ENOENT
|
42
|
+
raise "Template file not found at path: #{file_path}"
|
43
|
+
rescue Psych::SyntaxError => e
|
44
|
+
raise "YAML Syntax Error in file #{file_path}: #{e.message}"
|
45
|
+
rescue StandardError => e
|
46
|
+
raise "Error rendering prompt for template '#{template}': #{e.message}"
|
47
|
+
end
|
53
48
|
|
54
|
-
|
55
|
-
#
|
56
|
-
# @param type [String] Name of the prompt folder
|
57
|
-
# @param prompt [String] Type of prompt (e.g., 'system', 'user')
|
58
|
-
#
|
59
|
-
# @return [String] Full path to the template file
|
60
|
-
def self.prompt_file_path(type, prompt)
|
61
|
-
"#{PROMPTS_PATH}/#{type}/#{prompt}.yml.erb"
|
62
|
-
end
|
49
|
+
private
|
63
50
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# @return [Object] Processed value with special characters escaped
|
68
|
-
def self.preprocess_locals(value)
|
69
|
-
case value
|
70
|
-
when String
|
71
|
-
escape_special_chars(value)
|
72
|
-
when Hash
|
73
|
-
value.transform_values { |v| preprocess_locals(v) } # Recurse into hash values
|
74
|
-
when Array
|
75
|
-
value.map { |item| preprocess_locals(item) } # Recurse into array items
|
76
|
-
else
|
77
|
-
value
|
51
|
+
# Detects the appropriate path for prompt templates
|
52
|
+
def detect_prompts_path
|
53
|
+
File.join(Dir.pwd, 'app', 'spectre', 'prompts')
|
78
54
|
end
|
79
|
-
end
|
80
55
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
.gsub('"', '"')
|
90
|
-
.gsub("'", ''')
|
91
|
-
.gsub("\n", '\\n')
|
92
|
-
.gsub("\r", '\\r')
|
93
|
-
.gsub("\t", '\\t')
|
94
|
-
end
|
56
|
+
# Split the template parameter into path and prompt
|
57
|
+
#
|
58
|
+
# @param template [String] Template path in the format 'folder1/folder2/prompt'
|
59
|
+
# @return [Array<String, String>] An array containing the folder path and the prompt name
|
60
|
+
def split_template(template)
|
61
|
+
*path_parts, prompt = template.split('/')
|
62
|
+
[File.join(path_parts), prompt]
|
63
|
+
end
|
95
64
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
65
|
+
# Build the path to the desired prompt file
|
66
|
+
#
|
67
|
+
# @param path [String] Path to the prompt folder(s)
|
68
|
+
# @param prompt [String] Name of the prompt file (e.g., 'system', 'user')
|
69
|
+
#
|
70
|
+
# @return [String] Full path to the template file
|
71
|
+
def prompt_file_path(path, prompt)
|
72
|
+
File.join(prompts_path, path, "#{prompt}.yml.erb")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Preprocess locals recursively to escape special characters in strings
|
76
|
+
#
|
77
|
+
# @param value [Object] The value to process (string, array, hash, etc.)
|
78
|
+
# @return [Object] Processed value with special characters escaped
|
79
|
+
def preprocess_locals(value)
|
80
|
+
case value
|
81
|
+
when String
|
82
|
+
escape_special_chars(value)
|
83
|
+
when Hash
|
84
|
+
value.transform_values { |v| preprocess_locals(v) } # Recurse into hash values
|
85
|
+
when Array
|
86
|
+
value.map { |item| preprocess_locals(item) } # Recurse into array items
|
87
|
+
else
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Escape special characters in strings to avoid YAML parsing issues
|
93
|
+
#
|
94
|
+
# @param value [String] The string to process
|
95
|
+
# @return [String] The processed string with special characters escaped
|
96
|
+
def escape_special_chars(value)
|
97
|
+
value.gsub('&', '&')
|
98
|
+
.gsub('<', '<')
|
99
|
+
.gsub('>', '>')
|
100
|
+
.gsub('"', '"')
|
101
|
+
.gsub("'", ''')
|
102
|
+
.gsub("\n", '\\n')
|
103
|
+
.gsub("\r", '\\r')
|
104
|
+
.gsub("\t", '\\t')
|
105
|
+
end
|
106
|
+
|
107
|
+
# Convert special characters back to their original form after YAML processing
|
108
|
+
#
|
109
|
+
# @param value [String] The string to process
|
110
|
+
# @return [String] The processed string with original special characters restored
|
111
|
+
def convert_special_chars_back(value)
|
112
|
+
value.gsub('&', '&')
|
113
|
+
.gsub('<', '<')
|
114
|
+
.gsub('>', '>')
|
115
|
+
.gsub('"', '"')
|
116
|
+
.gsub(''', "'")
|
117
|
+
.gsub('\\n', "\n")
|
118
|
+
.gsub('\\r', "\r")
|
119
|
+
.gsub('\\t', "\t")
|
120
|
+
end
|
109
121
|
end
|
110
122
|
|
111
123
|
# Helper class to handle the binding for ERB template rendering
|
data/lib/spectre/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spectre_ai
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ilya Klapatok
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-10-
|
12
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rspec-rails
|