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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5cf3f5f31178a8456ccbb32bf9d68560efa5873751d3b3ca95c71921734b5da0
4
- data.tar.gz: 9e96ac4a9eb3bb69dca886cff6570450f6380100c6df2df789f1c0bc0b7327f6
3
+ metadata.gz: c7c3acf59b77ad62e0095fb7f91aa0491a50f25d197f94c788ad5ce2bbefbf6f
4
+ data.tar.gz: 48b4a9dcda9a013a6dde32ca5008a7dd33a49f4d4678952b8fcbf2089d2ccca6
5
5
  SHA512:
6
- metadata.gz: 23dc62f85aa5d69faa16036349560dcf0a97f214f636598387921ba4fa63b48863a69db2e60518d05aeae383a1b27ba8a82ff03bf40ec9fa46b5c7fa059ce546
7
- data.tar.gz: 12203cbdfc16fb9e4983362eee4a700f33ba220516ad75663f0bfafc57a2b503c164fb7e7efc90913f3cbb73c6f62d589c9a39b4fdeda0e22fa1decbfc14732c
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:
@@ -5,107 +5,119 @@ require 'yaml'
5
5
 
6
6
  module Spectre
7
7
  class Prompt
8
- PROMPTS_PATH = File.join(Dir.pwd, 'app', 'spectre', 'prompts')
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
- private
11
+ def prompts_path
12
+ @prompts_path ||= detect_prompts_path
13
+ end
45
14
 
46
- # Split the template parameter into type and prompt
47
- #
48
- # @param template [String] Template path in the format 'type/prompt' (e.g., 'rag/system')
49
- # @return [Array<String, String>] An array containing the type and prompt
50
- def self.split_template(template)
51
- template.split('/')
52
- end
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
- # Build the path to the desired prompt file
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
- # Preprocess locals recursively to escape special characters in strings
65
- #
66
- # @param value [Object] The value to process (string, array, hash, etc.)
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
- # Escape special characters in strings to avoid YAML parsing issues
82
- #
83
- # @param value [String] The string to process
84
- # @return [String] The processed string with special characters escaped
85
- def self.escape_special_chars(value)
86
- value.gsub('&', '&amp;')
87
- .gsub('<', '&lt;')
88
- .gsub('>', '&gt;')
89
- .gsub('"', '&quot;')
90
- .gsub("'", '&#39;')
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
- # Convert special characters back to their original form after YAML processing
97
- #
98
- # @param value [String] The string to process
99
- # @return [String] The processed string with original special characters restored
100
- def self.convert_special_chars_back(value)
101
- value.gsub('&amp;', '&')
102
- .gsub('&lt;', '<')
103
- .gsub('&gt;', '>')
104
- .gsub('&quot;', '"')
105
- .gsub('&#39;', "'")
106
- .gsub('\\n', "\n")
107
- .gsub('\\r', "\r")
108
- .gsub('\\t', "\t")
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('&', '&amp;')
98
+ .gsub('<', '&lt;')
99
+ .gsub('>', '&gt;')
100
+ .gsub('"', '&quot;')
101
+ .gsub("'", '&#39;')
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('&amp;', '&')
113
+ .gsub('&lt;', '<')
114
+ .gsub('&gt;', '>')
115
+ .gsub('&quot;', '"')
116
+ .gsub('&#39;', "'")
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Spectre # :nodoc:all
4
- VERSION = "1.1.0"
4
+ VERSION = "1.1.1"
5
5
  end
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.0
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-07 00:00:00.000000000 Z
12
+ date: 2024-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec-rails