prompt_manager 0.1.0 → 0.2.0

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: 2772d50f835a71c26631b2109af906008fec84b62e7ccf435a188fb856415268
4
- data.tar.gz: c1aa3889826246a79df35ad74cd221c1374af161083a34f50d64ef09f5606ade
3
+ metadata.gz: dbcdfe0c23335596eaad240b45fdb1451cab1be2569e511d21ddaf58ea986128
4
+ data.tar.gz: ce09536239ef6d06c8e42a120d0fd6661f3e42b72a4e9bea6dc998be12024956
5
5
  SHA512:
6
- metadata.gz: c43cc115f2dfa8e847c499386b482d29f3454ca068b0d161d8c14350002d955461962e1009af0a932a92388eef39b134515bca6f694dabd41110150301339b56
7
- data.tar.gz: 55b0e4f827d1950f39ad87909954660cb5ae0f6da44a4de0b43aad637dc1d99d878d2cb1876ac4a001cd42c190e1160a76593c6f4cba3b8411c250bd42e97e35
6
+ metadata.gz: 1a603d11d09c89ba70e4577db65e0feb31246406dadb0b118717a51b1b4831d54dde782e90511a4d2e44733dd255c501296c97639b446d8f458903d391e3aa0a
7
+ data.tar.gz: bf48bf56a2e28ccde3ec5673ca9d54e5994ca4b7b71b35714177b2fcb8fbe07bf64521e5f4e9d8d1faf0ca4ec86f8ed98c5f879d532245e8978c200b319fee71
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-11-21
4
+
5
+ - **Breaking change to FileSystemAdapter config process**
6
+ - added list and path as extra methods in FileSystemAdapter
7
+
3
8
  ## [0.1.0] - 2023-11-16
4
9
 
5
10
  - Initial release using the FileSystemAdapter
data/README.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  Manage the parameterized prompts (text) used in generative AI (aka chatGPT, OpenAI, _et.al._) using storage adapters such as FileSystemAdapter, SqliteAdapter and ActiveRecordAdapter.
4
4
 
5
+ **Breaking Change** in version 0.2.0 for `FileSystemAdapter` configuration. See [Configuration](#configuration) to see how the new `config` block works.
6
+
7
+ <!-- Tocer[start]: Auto-generated, don't remove. -->
8
+
9
+ ## Table of Contents
10
+
11
+ - [Installation](#installation)
12
+ - [Usage](#usage)
13
+ - [Overview](#overview)
14
+ - [Generative AI (gen-AI)](#generative-ai-gen-ai)
15
+ - [What does a keyword look like?](#what-does-a-keyword-look-like)
16
+ - [Storage Adapters](#storage-adapters)
17
+ - [FileSystemAdapter](#filesystemadapter)
18
+ - [Configuration](#configuration)
19
+ - [prompts_dir](#prompts_dir)
20
+ - [search_proc](#search_proc)
21
+ - [File Extensions](#file-extensions)
22
+ - [Extra Functionality](#extra-functionality)
23
+ - [SqliteAdapter](#sqliteadapter)
24
+ - [ActiveRecordAdapter](#activerecordadapter)
25
+ - [Other Potential Storage Adapters](#other-potential-storage-adapters)
26
+ - [Development](#development)
27
+ - [Contributing](#contributing)
28
+ - [License](#license)
29
+
30
+ <!-- Tocer[finish]: Auto-generated, don't remove. -->
31
+
5
32
  ## Installation
6
33
 
7
34
  Install the gem and add to the application's Gemfile by executing:
@@ -14,34 +41,92 @@ If bundler is not being used to manage dependencies, install the gem by executin
14
41
 
15
42
  ## Usage
16
43
 
17
- See [examples/simple.rb](examples.simple.rb)
44
+ See [examples/simple.rb](examples/simple.rb)
18
45
 
19
46
  ## Overview
20
47
 
21
48
  ### Generative AI (gen-AI)
22
49
 
23
- Gen-AI deals with the conversion (some would say execution) of a human natural language text (the "prompt") into somthing else using what are known as large language models (LLM) such as those available from OpenAI. A parameterized prompt is one in whcih there are embedded keywords (parameters) which are place holders for other text to be inserted into the prompt.
50
+ Gen-AI deals with the conversion (some would say execution) of a human natural language text (the "prompt") into somthing else using what are known as large language models (LLM) such as those available from OpenAI. A parameterized prompt is one in which there are embedded keywords (parameters) which are place holders for other text to be inserted into the prompt.
24
51
 
25
- The prompt_manager uses a regurlar expression to identify these keywords within the prompt. It uses the keywords as keys in a `parameters` Hash which is stored with the prompt text in a serialized form - for example as JSON.
52
+ The prompt_manager uses a regular expression to identify these keywords within the prompt. It uses the keywords as keys in a `parameters` Hash which is stored with the prompt text in a serialized form - for example as JSON.
26
53
 
27
54
  #### What does a keyword look like?
28
55
 
29
- The current hard-coded REGEX for a [KEYWORD] identifies any all [UPPERCASE_TEXT] enclosed in squal brackes as a keyword. [KEY WORDS CAN ALSO HAVE SPACES] as well as the underscore character.
56
+ The current hard-coded REGEX for a [KEYWORD] identifies any all [UPPERCASE_TEXT] enclosed in square brackets as a keyword. [KEYWORDS CAN ALSO HAVE SPACES] as well as the underscore character.
30
57
 
31
- This is just the initial convention adopted by prompt_manager. It is intended that this REGEX be configurable so that the promp_manager can be used with other conventions.
58
+ This is just the initial convention adopted by prompt_manager. It is intended that this REGEX be configurable so that the prompt_manager can be used with other conventions.
32
59
 
33
60
  ### Storage Adapters
34
61
 
35
- A storage adapter is a class instance that ties the `PromptManager::Prompt` class to a storage facility that holds the actual prompts. Currentlyt there are 3 storage adapters planned for implementation.
62
+ A storage adapter is a class instance that ties the `PromptManager::Prompt` class to a storage facility that holds the actual prompts. Currently there are 3 storage adapters planned for implementation.
63
+
64
+ The `PromptManager::Prompt` to support a small set of methods. A storage adapter can provide "extra" class or instance methods that can be used through the Prompt class. See the `test/prompt_manager/prompt_test.rb` for guidance on creating a new storage adapter.
36
65
 
37
66
  #### FileSystemAdapter
38
67
 
39
- This is the first storage adapter developed. It saves prompts as text files within the file system inside a designated `prompts_dir` (directory) such as `~/,prompts` or where it makes the most sense to you. Another example would be to have your directory on a shared file system so that others can use the same prompts.
68
+ This is the first storage adapter developed. It saves prompts as text files within the file system inside a designated `prompts_dir` (directory) such as `~/.prompts` or where it makes the most sense to you. Another example would be to have your directory on a shared file system so that others can use the same prompts.
40
69
 
41
- The `promp ID` is the basename of the text file. For example `todo.txt` is the file for the prompt ID `todo` (see the examples directory.)
70
+ The `prompt ID` is the basename of the text file. For example `todo.txt` is the file for the prompt ID `todo` (see the examples directory.)
42
71
 
43
72
  The parameters for the `todo` prompt ID are saved in the same directory as `todo.txt` in a JSON file named `todo.json` (also in the examples directory.)
44
73
 
74
+ ##### Configuration
75
+
76
+ Use a `config` block toe establish the configuration for the class.
77
+
78
+ ```ruby
79
+ PromptManager::Storage::FileSystemAdapter.config do |o|
80
+ o.prompts_dir = "path/to/prompts_directory"
81
+ o.search_proc = -> (q) { "ag -l #{q} #{prompts_dir} | reformat" }
82
+ o.prompt_extension = '.txt' # default
83
+ o.params_extension = '.json' # the default
84
+ end
85
+ ```
86
+
87
+ The `config` block returns `self` so that means you can do this to setup the storage adapter with the Prompt class:
88
+
89
+ ```ruby
90
+ PromptManager::Prompt
91
+ .storage_adapter =
92
+ PromptManager::Storage::FileSystemAdapter
93
+ .config do |config|
94
+ config.prompts_dir = 'path/to/prompts_dir'
95
+ end.new
96
+ ```
97
+
98
+ ###### prompts_dir
99
+
100
+ This is either a `String` or a `Pathname` object. All file paths are maintained in the class as `Pathname` objects. If you provide a `String` it will be converted. Relative paths will be converted to absolute paths.
101
+
102
+ An `ArgumentError` will be raised when `prompts_dir` does not exist or if it is not a directory.
103
+
104
+ ###### search_proc
105
+
106
+ The default for `search_proc` is nil. In this case the search will be preformed by a default `search` method which is basically reading all the prompt files to see which ones contain the search term. There are faster ways to do this kind of thing using CLI=based utilities.
107
+
108
+ TODO: add a example to the examples directory on how to integrate with command line utilities.
109
+
110
+ ###### File Extensions
111
+
112
+ These two configuration options are `String` objects that must start with a period "." utherwise an `ArgumentError` will be raised.
113
+
114
+ * prompt_extension - default: '.txt'
115
+ * params_extension - default: '.json'
116
+
117
+ Currently the `FileSystemAdapter` only supports a JSON serializer for its parameters Hash. Using any other values for these extensions will cause problems.
118
+
119
+ They exist so that there is a platform on to which other storage adapters can be built or serializers added. This is not currently on the roadmap.
120
+
121
+ ##### Extra Functionality
122
+
123
+ The `FileSystemAdapter` adds two new methods for use by the `Prompt` class:
124
+ * list - returns an Array of prompt IDs
125
+ * path and path(prompt_id) - returns a `Pathname` object to the prompt file
126
+
127
+ Use the `path(prompt_id)` form against the `Prompt` class
128
+ Use `prompt.path` when you have an instance of a `Prompt`
129
+
45
130
  #### SqliteAdapter
46
131
 
47
132
  TODO: This may be the next adapter to be implemented.
data/Rakefile CHANGED
@@ -1,5 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ begin
4
+ require "tocer/rake/register"
5
+ rescue LoadError => error
6
+ puts error.message
7
+ end
8
+
9
+ Tocer::Rake::Register.call
10
+
11
+
3
12
  require "bundler/gem_tasks"
4
13
  require "rake/testtask"
5
14
 
@@ -0,0 +1,4 @@
1
+ # prompt_manager/examples/prompts_dir/toy/8-ball.txt
2
+ # Desc: In the late 1940s the toy "Magic 8 Ball" came to market
3
+
4
+ As a magic 8-ball, provide me with a terse answer to what you suspect that I am thinking about.
data/examples/simple.rb CHANGED
@@ -9,7 +9,9 @@
9
9
  ## By: Dewayne VanHoozer (dvanhoozer@gmail.com)
10
10
  ##
11
11
  #
12
-
12
+ # TODO: Add `list` to get an Array of prompt IDs
13
+ # TODO: Add `path` to get a path to the prompt file
14
+ #
13
15
 
14
16
  require 'prompt_manager'
15
17
  require 'prompt_manager/storage/file_system_adapter'
@@ -31,11 +33,14 @@ at_exit do
31
33
  end
32
34
 
33
35
  # Configure the Storage Adapter to use
36
+ PromptManager::Storage::FileSystemAdapter.config do |config|
37
+ config.prompts_dir = PROMPTS_DIR
38
+ # config.search_proc = nil # default
39
+ # config.prompt_extension = '.txt' # default
40
+ # config.parms+_extension = '.json' # default
41
+ end
34
42
 
35
- PromptManager::Prompt.storage_adapter =
36
- PromptManager::Storage::FileSystemAdapter.new(
37
- prompts_dir: PROMPTS_DIR
38
- )
43
+ PromptManager::Prompt.storage_adapter = PromptManager::Storage::FileSystemAdapter.new
39
44
 
40
45
  # Get a prompt
41
46
 
@@ -87,3 +92,51 @@ EOS
87
92
 
88
93
  puts todo.to_s
89
94
 
95
+ puts <<~EOS
96
+
97
+ When using the FileSystemAdapter for prompt storage you can have within
98
+ the prompts_dir you can have many sub-directories. These sub-directories
99
+ act like categories. The prompt ID is composed for the sub-directory name,
100
+ a "/" character and then the normal prompt ID. For example "toy/8-ball"
101
+
102
+ EOS
103
+
104
+ magic = PromptManager::Prompt.get( id: 'toy/8-ball' )
105
+
106
+ puts "The magic PROMPT is:"
107
+ puts magic
108
+ puts
109
+ puts "Remember if you want to see the full text of the prompt file:"
110
+ puts magic.text
111
+
112
+ puts "="*64
113
+
114
+ puts <<~EOS
115
+
116
+ The FileSystemAdapter also adds two new methods to the Prompt class:
117
+
118
+ list - provides an Array of pompt IDs
119
+ path(prompt_id) - Returns a Pathname object to the prompt file
120
+
121
+ EOS
122
+
123
+ puts "List of prompts available"
124
+ puts "========================="
125
+
126
+ puts PromptManager::Prompt.list
127
+
128
+ puts <<~EOS
129
+
130
+ And the path to the "toy/8-ball" prompt file is:
131
+
132
+ #{magic.path}
133
+
134
+ Use "your_prompt.path" for when you want to do something with the
135
+ the prompt file like send it to a text editor.
136
+
137
+ Your can also use the class method if you supply a prompt_id
138
+ like this:
139
+
140
+ EOS
141
+
142
+ puts PromptManager::Prompt.path('toy/8-ball')
@@ -25,9 +25,24 @@ class PromptManager::Prompt
25
25
  new(id: id)
26
26
  end
27
27
 
28
+
28
29
  def search(for_what)
29
30
  storage_adapter.search(for_what)
30
31
  end
32
+
33
+
34
+ def method_missing(method_name, *args, &block)
35
+ if storage_adapter.respond_to?(method_name)
36
+ storage_adapter.send(method_name, *args, &block)
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+
43
+ def respond_to_missing?(method_name, include_private = false)
44
+ storage_adapter.respond_to?(method_name, include_private) || super
45
+ end
31
46
  end
32
47
 
33
48
  # SMELL: Does the db (aka storage adapter) really need
@@ -142,6 +157,23 @@ class PromptManager::Prompt
142
157
  end
143
158
 
144
159
 
160
+ # Let the storage adapter instance take a crake at
161
+ # these unknown methods. Don't care what the args
162
+ # are, just pass the prompt's ID.
163
+ def method_missing(method_name, *args, &block)
164
+ if db.respond_to?(method_name)
165
+ db.send(method_name, id, &block)
166
+ else
167
+ super
168
+ end
169
+ end
170
+
171
+
172
+ def respond_to_missing?(method_name, include_private = false)
173
+ db.respond_to?(method_name, include_private) || super
174
+ end
175
+
176
+
145
177
  # SMELL: should this gem log errors or is that a function of
146
178
  # main program? I believe its the main program's job.
147
179
  def log_error(message)
@@ -1,26 +1,151 @@
1
1
  # prompt_manager/lib/prompt_manager/storage/file_system_adapter.rb
2
2
 
3
-
4
- require 'json'
3
+ # Use the local (or remote) file system as a place to
4
+ # store and access prompts.
5
+ #
6
+ # Adds two additional methods to the Promp class:
7
+ # list - returns Array of prompt IDs
8
+ # path = returns a Pathname object to the prompt's text file
9
+ # path(prompt_id) - same as path on the prompt instance
10
+ #
11
+ # Allows sub-directories of the prompts_dir to be
12
+ # used like categories. For example the prompt_id "toy/magic"
13
+ # is found in the `magic.txt` file inside the `toy` sub-directory
14
+ # of the prompts_dir.
15
+ #
16
+ # There can man be many layers of categories (sub-directories)
17
+ #
18
+
19
+ require 'json' # basic serialization of parameters
20
+ require 'pathname'
5
21
 
6
22
  class PromptManager::Storage::FileSystemAdapter
7
- PARAMS_EXTENSION = '.json'.freeze
8
- PROMPT_EXTENSION = '.txt'.freeze
23
+ SEARCH_PROC = nil # placeholder
24
+ PARAMS_EXTENSION = '.json'.freeze
25
+ PROMPT_EXTENSION = '.txt'.freeze
26
+ PROMPT_ID_FORMAT = /^[a-zA-Z0-9\-\/_]+$/
27
+
28
+ class << self
29
+ attr_accessor :prompts_dir, :search_proc,
30
+ :params_extension, :prompt_extension
31
+
32
+ def config
33
+ if block_given?
34
+ yield self
35
+ validate_configuration
36
+ else
37
+ raise ArgumentError, "No block given to config"
38
+ end
9
39
 
10
- attr_reader :prompts_dir
40
+ self
41
+ end
11
42
 
12
- def initialize(
13
- prompts_dir: '.prompts',
14
- search_proc: nil # Example: ->(q) {`ag -l #{q}`}
15
- )
43
+ # Expansion methods on the Prompt class specific to
44
+ # this storage adapter.
45
+
46
+ # Ignore the incoming prompt_id
47
+ def list(prompt_id = nil)
48
+ new.list
49
+ end
50
+
51
+
52
+ def path(prompt_id)
53
+ new.path(prompt_id)
54
+ end
55
+
56
+ #################################################
57
+ private
58
+
59
+ def validate_configuration
60
+ validate_prompts_dir
61
+ validate_search_proc
62
+ validate_prompt_extension
63
+ validate_params_extension
64
+ end
65
+
66
+
67
+ def validate_prompts_dir
68
+ # This is a work around for a Ruby scope issue where the
69
+ # class getter/setter method is becoming confused with a
70
+ # local variable when anything other than plain 'ol get and
71
+ # set are used.'This error is in both Ruby v3.2.2 and
72
+ # v3.3.0-preview3.
73
+ #
74
+ prompts_dir_local = self.prompts_dir
75
+
76
+ unless prompts_dir_local.is_a?(Pathname)
77
+ prompts_dir_local = Pathname.new(prompts_dir_local) unless prompts_dir_local.nil?
78
+ end
16
79
 
17
- # validate that prompts_dir exist and is in fact a directory.
18
- unless Dir.exist?(prompts_dir)
19
- raise "Directory #{prompts_dir} does not exist or is not a directory"
80
+ prompts_dir_local = prompts_dir_local.expand_path
81
+
82
+ raise(ArgumentError, "prompts_dir: #{prompts_dir_local}") unless prompts_dir_local.exist? && prompts_dir_local.directory?
83
+
84
+ self.prompts_dir = prompts_dir_local
85
+ end
86
+
87
+
88
+ def validate_search_proc
89
+ search_proc_local = self.search_proc
90
+
91
+ if search_proc_local.nil?
92
+ search_proc_local = SEARCH_PROC
93
+ else
94
+ raise(ArgumentError, "search_proc invalid; does not respond to call") unless search_proc_local.respond_to?(:call)
95
+ end
96
+
97
+ self.search_proc = search_proc_local
98
+ end
99
+
100
+
101
+ def validate_prompt_extension
102
+ prompt_extension_local = self.prompt_extension
103
+
104
+ if prompt_extension_local.nil?
105
+ prompt_extension_local = PROMPT_EXTENSION
106
+ else
107
+ unless prompt_extension_local.is_a?(String) &&
108
+ prompt_extension_local.start_with?('.')
109
+ raise(ArgumentError, "Invalid prompt_extension: #{prompt_extension_local}")
110
+ end
111
+ end
112
+
113
+ self.prompt_extension = prompt_extension_local
20
114
  end
21
115
 
22
- @prompts_dir = prompts_dir
23
- @search_proc = search_proc
116
+
117
+ def validate_params_extension
118
+ params_extension_local = self.params_extension
119
+
120
+ if params_extension_local.nil?
121
+ params_extension_local = PARAMS_EXTENSION
122
+ else
123
+ unless params_extension_local.is_a?(String) &&
124
+ params_extension_local.start_with?('.')
125
+ raise(ArgumentError, "Invalid params_extension: #{params_extension_local}")
126
+ end
127
+ end
128
+
129
+ self.params_extension = params_extension_local
130
+ end
131
+ end
132
+
133
+
134
+ ##################################################
135
+ ###
136
+ ## Instance
137
+ #
138
+
139
+ def prompts_dir = self.class.prompts_dir
140
+ def search_proc = self.class.search_proc
141
+ def prompt_extension = self.class.prompt_extension
142
+ def params_extension = self.class.params_extension
143
+
144
+
145
+ def initialize
146
+ # NOTE: validate because main program may have made
147
+ # changes outside of the config block
148
+ self.class.send(:validate_configuration) # send gets around private designations of a method
24
149
  end
25
150
 
26
151
 
@@ -37,14 +162,20 @@ class PromptManager::Storage::FileSystemAdapter
37
162
 
38
163
  # Retrieve prompt text by its id
39
164
  def prompt_text(prompt_id)
40
- read_file(file_path(prompt_id, PROMPT_EXTENSION))
165
+ read_file(file_path(prompt_id, prompt_extension))
41
166
  end
42
167
 
43
168
 
44
169
  # Retrieve parameter values by its id
45
170
  def parameter_values(prompt_id)
46
- json_content = read_file(file_path(prompt_id, PARAMS_EXTENSION))
47
- deserialize(json_content)
171
+ params_path = file_path(prompt_id, params_extension)
172
+
173
+ if params_path.exist?
174
+ parms_content = read_file(params_path)
175
+ deserialize(parms_content)
176
+ else
177
+ {}
178
+ end
48
179
  end
49
180
 
50
181
 
@@ -56,8 +187,8 @@ class PromptManager::Storage::FileSystemAdapter
56
187
  )
57
188
  validate_id(id)
58
189
 
59
- prompt_filepath = file_path(id, PROMPT_EXTENSION)
60
- params_filepath = file_path(id, PARAMS_EXTENSION)
190
+ prompt_filepath = file_path(id, prompt_extension)
191
+ params_filepath = file_path(id, params_extension)
61
192
 
62
193
  write_with_error_handling(prompt_filepath, text)
63
194
  write_with_error_handling(params_filepath, serialize(parameters))
@@ -68,8 +199,8 @@ class PromptManager::Storage::FileSystemAdapter
68
199
  def delete(id:)
69
200
  validate_id(id)
70
201
 
71
- prompt_filepath = file_path(id, PROMPT_EXTENSION)
72
- params_filepath = file_path(id, PARAMS_EXTENSION)
202
+ prompt_filepath = file_path(id, prompt_extension)
203
+ params_filepath = file_path(id, params_extension)
73
204
 
74
205
  delete_with_error_handling(prompt_filepath)
75
206
  delete_with_error_handling(params_filepath)
@@ -85,26 +216,54 @@ class PromptManager::Storage::FileSystemAdapter
85
216
  end
86
217
 
87
218
 
219
+ # Return an Array of prompt IDs
220
+ def list(*)
221
+ prompt_ids = []
222
+
223
+ Pathname.glob(prompts_dir.join("**/*#{prompt_extension}")).each do |file_path|
224
+ prompt_id = file_path.relative_path_from(prompts_dir).to_s.gsub(prompt_extension, '')
225
+ prompt_ids << prompt_id
226
+ end
227
+
228
+ prompt_ids
229
+ end
230
+
231
+
232
+ # Returns a Pathname object for a prompt ID text file
233
+ # However, it is possible that the file does not exist.
234
+ def path(id)
235
+ validate_id(id)
236
+ file_path(id, prompt_extension)
237
+ end
238
+
88
239
  ##########################################
89
240
  private
90
241
 
242
+ # Validate that the ID contains good characters.
91
243
  def validate_id(id)
92
- raise ArgumentError, 'Invalid ID format' unless id =~ /^[a-zA-Z0-9\-_]+$/
244
+ raise ArgumentError, "Invalid ID format id: #{id}" unless id =~ PROMPT_ID_FORMAT
245
+ end
246
+
247
+
248
+ # Verify that the ID actually exists
249
+ def verify_id(id)
250
+ file_path(id, prompt_extension).exist?
93
251
  end
94
252
 
95
253
 
96
254
  def write_with_error_handling(file_path, content)
97
255
  begin
98
- File.write(file_path, content)
256
+ file_path.write content
99
257
  rescue IOError => e
100
258
  raise "Failed to write to file: #{e.message}"
101
259
  end
102
260
  end
103
261
 
104
262
 
263
+ # file_path (Pathname)
105
264
  def delete_with_error_handling(file_path)
106
265
  begin
107
- FileUtils.rm_f(file_path)
266
+ file_path.delete
108
267
  rescue IOError => e
109
268
  raise "Failed to delete file: #{e.message}"
110
269
  end
@@ -112,7 +271,7 @@ class PromptManager::Storage::FileSystemAdapter
112
271
 
113
272
 
114
273
  def file_path(id, extension)
115
- File.join(@prompts_dir, "#{id}#{extension}")
274
+ prompts_dir + "#{id}#{extension}"
116
275
  end
117
276
 
118
277
 
@@ -123,21 +282,22 @@ class PromptManager::Storage::FileSystemAdapter
123
282
 
124
283
 
125
284
  def search_prompts(search_term)
126
- query_term = search_term.downcase
127
-
128
- Dir.glob(File.join(@prompts_dir, "*#{PROMPT_EXTENSION}")).each_with_object([]) do |file_path, ids|
129
- File.open(file_path) do |file|
130
- file.each_line do |line|
131
- if line.downcase.include?(query_term)
132
- ids << File.basename(file_path, PROMPT_EXTENSION)
133
- next
134
- end
135
- end
285
+ prompt_ids = []
286
+
287
+ Pathname.glob(prompts_dir.join("**/*#{prompt_extension}")).each do |prompt_path|
288
+ if prompt_path.read.downcase.include?(search_term)
289
+ prompt_id = prompt_path.relative_path_from(prompts_dir).to_s.gsub(prompt_extension, '')
290
+ prompt_ids << prompt_id
136
291
  end
137
- end.uniq
292
+ end
293
+
294
+ prompt_ids
295
+
138
296
  end
139
297
 
140
298
 
299
+ # TODO: Should the serializer be generic?
300
+
141
301
  def serialize(data)
142
302
  data.to_json
143
303
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PromptManager
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prompt_manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dewayne VanHoozer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-19 00:00:00.000000000 Z
11
+ date: 2023-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: amazing_print
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: fakefs
28
+ name: debug_me
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -52,8 +52,22 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: tocer
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: "Manage the parameterized prompts (text) used in generative AI (aka chatGPT,
56
- \npen AI, et.al.) using storage adapters such as FileSystemAdapter, \nSqliteAdapter
70
+ \nOpenAI, et.al.) using storage adapters such as FileSystemAdapter, \nSqliteAdapter
57
71
  and ActiveRecordAdapter.\n"
58
72
  email:
59
73
  - dvanhoozer@gmail.com
@@ -69,6 +83,7 @@ files:
69
83
  - Rakefile
70
84
  - examples/prompts_dir/todo.json
71
85
  - examples/prompts_dir/todo.txt
86
+ - examples/prompts_dir/toy/8-ball.txt
72
87
  - examples/simple.rb
73
88
  - lib/prompt_manager.rb
74
89
  - lib/prompt_manager/prompt.rb