spectre_ai 1.1.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|