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 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