prompt_manager 0.0.1 → 0.0.2

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: 0e3ba20b4c51adf033fc6124346bbb568be751fb3820000773dd51b496c6bc64
4
- data.tar.gz: 950e2dd314ac1eec127c6ea3887ddd727fc55b6b5bb5735cac32c175de4135e5
3
+ metadata.gz: 74f0d17c5e89ae990b91f2cc8632751ecb1664f1dfc248de9967828f088101b3
4
+ data.tar.gz: 47e60cd381bf459a34b1a6a6350cd0ca600125677535321d36e2d6d8395fcd2c
5
5
  SHA512:
6
- metadata.gz: 1dc004fae9eba3c212014102a89897a0a7301e3157efd6b662e75766e387b057110c58556f918c97dc729edf4968963245d86736a1b8e80795fc7258620eacc2
7
- data.tar.gz: ce46bbfa158650fabfa9741f3b3b9ab42e4acf6022d6dedbd62b377f9659dab72d9ed241ba575c489b6fabab6f550af03a5afa4fc9074303b866c96984f3d769
6
+ metadata.gz: e5033e585f72c925f0ca3461288d7429362a184567eb6713676ba213ac91e6dcced8ae48475346bf0a2b411890b143b7ad22c35ca4e8f3be0f4f4a08b9685db0
7
+ data.tar.gz: 72b4b277108805d56c7322c862eba84eff11379ba7c3efb116b9276de96ab8de77726c67b102a008bac5046fa697854ba3274bb6d0d15782d1474ae07d6a4945
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # PromptManager
2
2
 
3
- **Under Development**
3
+ **Under Development** Not ready for use.
4
+
5
+ I'm looking for some contributors to help define the API between the Prompt class and the Storage adapters. I'm focusing on the FileSystemAdapter since the majority of my work is on the command line.
4
6
 
5
7
  Extracting the prompt management functionality fro the aip.rb file into a new gem that will provide a generic management service for other programs.
6
8
 
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require "rake/testtask"
6
6
  Rake::TestTask.new(:test) do |t|
7
7
  t.libs << "test"
8
8
  t.libs << "lib"
9
- t.test_files = FileList["test/**/test_*.rb"]
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
10
  end
11
11
 
12
12
  task default: :test
@@ -13,6 +13,9 @@
13
13
  #
14
14
 
15
15
 
16
+ require_relative '../lib/prompt_manager'
17
+
18
+
16
19
  =begin
17
20
 
18
21
  brew install fzf mods the_silver_searcher
@@ -48,9 +51,11 @@ This robust tool is excellent for users who wish to harness the power of generat
48
51
 
49
52
  #
50
53
  # TODO: I think this script has reached the point where
51
- # it is ready to become a proper gem.
54
+ # it is ready to become a proper gem. This would
55
+ # be a different gem than prompt_manager.
52
56
  #
53
57
 
58
+
54
59
  require 'pathname'
55
60
  HOME = Pathname.new( ENV['HOME'] )
56
61
 
@@ -72,22 +77,10 @@ AI_COMMAND = "#{AI_CLI_PROGRAM} #{ai_options} "
72
77
  EDITOR = ENV['EDITOR']
73
78
  PROMPT_DIR = HOME + ".prompts"
74
79
  PROMPT_LOG = PROMPT_DIR + "_prompts.log"
75
- PROMPT_EXTNAME = ".txt"
76
- DEFAULTS_EXTNAME = ".json"
77
- # SEARCH_COMMAND = "ag -l"
78
- KEYWORD_REGEX = /(\[[A-Z _|]+\])/
79
80
 
80
- AVAILABLE_PROMPTS = PROMPT_DIR
81
- .children
82
- .select{|c| PROMPT_EXTNAME == c.extname}
83
- .map{|c| c.basename.to_s.split('.')[0]}
84
-
85
- AVAILABLE_PROMPTS_HELP = AVAILABLE_PROMPTS
86
- .map{|c| " * " + c}
87
- .join("\n")
88
81
 
89
82
  require 'amazing_print'
90
- require 'json'
83
+ # require 'json'
91
84
  require 'readline' # TODO: or reline ??
92
85
  require 'word_wrap'
93
86
  require 'word_wrap/core_ext'
@@ -125,6 +118,7 @@ cli_helper("Use generative AI with saved parameterized prompts") do |o|
125
118
  o.bool '-e', '--edit', 'Edit the prompt text', default: false
126
119
  o.bool '-f', '--fuzzy', 'Allow fuzzy matching', default: false
127
120
  o.path '-o', '--output', 'The output file', default: Pathname.pwd + "temp.md"
121
+ o.string '-s', '--storage','How are the prompts stored', default: 'FileSystemAdapter'
128
122
  end
129
123
 
130
124
 
@@ -173,60 +167,84 @@ unless edit?
173
167
  end
174
168
  end
175
169
 
170
+ valid_storage = %w[
171
+ FileSystemAdapter
172
+ SqliteAdapter
173
+ ActiveRecordAdapter
174
+ ]
175
+
176
+ if valid_storange.include? configatron.storage
177
+ require_relative "../lib/storage/#{configatron.storage}"
178
+ STORAGE = "PromptManager::Storage::#{configatron.storage}".constantize
179
+ else
180
+ error "Unknow storage: #{configatron.storage}"
181
+ end
182
+
176
183
  abort_if_errors
177
184
 
178
- configatron.prompt_path = PROMPT_DIR + (configatron.prompt + PROMPT_EXTNAME)
179
- configatron.defaults_path = PROMPT_DIR + (configatron.prompt + DEFAULTS_EXTNAME)
185
+ # configatron.prompt_path = PROMPT_DIR + (configatron.prompt + PROMPT_EXTNAME)
186
+ # configatron.defaults_path = PROMPT_DIR + (configatron.prompt + DEFAULTS_EXTNAME)
180
187
 
181
- if !configatron.prompt_path.exist? && !edit?
182
- error "This prompt does not exist: #{configatron.prompt}\n"
188
+ begin
189
+ PromptManager::Prompt.storage_adapter = STORAGE.new
190
+ rescue StandardError => e
191
+ error "Unknown storage adapter: #{configatron.storage}\n#{e}"
183
192
  end
184
193
 
185
- configatron.prompt_path = PROMPT_DIR + (configatron.prompt + PROMPT_EXTNAME)
186
- configatron.defaults_path = PROMPT_DIR + (configatron.prompt + DEFAULTS_EXTNAME)
187
-
188
194
  abort_if_errors
189
195
 
190
- if edit?
191
- unless configatron.prompt_path.exist?
192
- configatron.prompt_path.write <<~PROMPT
193
- # #{configatron.prompt_path.relative_path_from(HOME)}
194
- # DESC:
196
+ begin
197
+ PROMPT = PromptManager::Prompt.get(id: configatron.prompt)
198
+ rescue Exception => e
199
+ error "Storage (#{configatron.storage}) does not have: #{configatron.prompt}\n#{e}"
200
+ end
195
201
 
196
- PROMPT
197
- end
202
+ abort_if_errors
198
203
 
199
- `#{EDITOR} #{configatron.prompt_path}`
200
- end
204
+ # TODO: This will have to change.
205
+ #
206
+ # if edit?
207
+ # unless configatron.prompt_path.exist?
208
+ # configatron.prompt_path.write <<~PROMPT
209
+ # # #{configatron.prompt_path.relative_path_from(HOME)}
210
+ # # DESC:
211
+ #
212
+ # PROMPT
213
+ # end
214
+ #
215
+ # `#{EDITOR} #{configatron.prompt_path}`
216
+ # end
201
217
 
202
218
  ######################################################
203
219
  # Local methods
204
220
 
205
- def extract_raw_prompt
206
- array_of_strings = ignore_after_end
207
- print_header_comment(array_of_strings)
208
-
209
- array_of_strings.reject do |a_line|
210
- a_line.chomp.strip.start_with?('#')
211
- end
212
- .join("\n")
213
- end
214
-
215
-
216
- def ignore_after_end
217
- array_of_strings = configatron.prompt_path.readlines
218
- .map{|a_line| a_line.chomp.strip}
219
-
220
- x = array_of_strings.index("__END__")
221
+ # def extract_raw_prompt
222
+ # array_of_strings = ignore_after_end
223
+ # print_header_comment(array_of_strings)
224
+ #
225
+ # array_of_strings.reject do |a_line|
226
+ # a_line.chomp.strip.start_with?('#')
227
+ # end
228
+ # .join("\n")
229
+ # end
221
230
 
222
- unless x.nil?
223
- array_of_strings = array_of_strings[..x-1]
224
- end
225
231
 
226
- array_of_strings
227
- end
232
+ # def ignore_after_end
233
+ # array_of_strings = configatron.prompt_path.readlines
234
+ # .map{|a_line| a_line.chomp.strip}
235
+ #
236
+ # x = array_of_strings.index("__END__")
237
+ #
238
+ # unless x.nil?
239
+ # array_of_strings = array_of_strings[..x-1]
240
+ # end
241
+ #
242
+ # array_of_strings
243
+ # end
228
244
 
229
245
 
246
+ # SMELL: This is based upon a convention that not everyone
247
+ # may want to follow.
230
248
  def print_header_comment(array_of_strings)
231
249
  print "\n\n" if verbose?
232
250
 
@@ -247,9 +265,9 @@ end
247
265
  # [KEY PHRASE]
248
266
  # [KEY PHRASE | KEY PHRASE | KEY_WORD]
249
267
  #
250
- def extract_keywords_from(prompt_raw)
251
- prompt_raw.scan(KEYWORD_REGEX).flatten.uniq
252
- end
268
+ # def extract_keywords_from(prompt_raw)
269
+ # prompt_raw.scan(KEYWORD_REGEX).flatten.uniq
270
+ # end
253
271
 
254
272
  # get the replacements for the keywords
255
273
  def replacements_for(keywords)
@@ -268,31 +286,32 @@ def replacements_for(keywords)
268
286
  end
269
287
 
270
288
 
271
- def load_default_replacements
272
- if configatron.defaults_path.exist?
273
- JSON.parse(configatron.defaults_path.read)
274
- else
275
- {}
276
- end
277
- end
278
-
289
+ # def load_default_replacements
290
+ # if configatron.defaults_path.exist?
291
+ # JSON.parse(configatron.defaults_path.read)
292
+ # else
293
+ # {}
294
+ # end
295
+ # end
279
296
 
280
- def save_default_replacements(a_hash)
281
- return if a_hash.empty?
282
- defaults = a_hash.to_json
283
- configatron.defaults_path.write defaults
284
- end
285
297
 
286
- # substitute the replacements for the keywords
287
- def replace_keywords_with replacements, prompt_raw
288
- prompt = prompt_raw.dup
298
+ # def save_default_replacements(a_hash)
299
+ # return if a_hash.empty?
300
+ # defaults = a_hash.to_json
301
+ # configatron.defaults_path.write defaults
302
+ # end
289
303
 
290
- replacements.each_pair do |keyword, replacement|
291
- prompt.gsub!(keyword, replacement)
292
- end
293
304
 
294
- prompt
295
- end
305
+ # substitute the replacements for the keywords
306
+ # def replace_keywords_with replacements, prompt_raw
307
+ # prompt = prompt_raw.dup
308
+ #
309
+ # replacements.each_pair do |keyword, replacement|
310
+ # prompt.gsub!(keyword, replacement)
311
+ # end
312
+ #
313
+ # prompt
314
+ # end
296
315
 
297
316
 
298
317
  def log(prompt_path, prompt_raw, answer)
@@ -323,16 +342,16 @@ end
323
342
 
324
343
  ap configatron.to_h if debug?
325
344
 
326
- configatron.prompt_raw = extract_raw_prompt
345
+ # configatron.prompt_raw = extract_raw_prompt
327
346
 
328
347
  puts
329
348
  puts "PROMPT:"
330
- puts configatron.prompt_raw.wrap
349
+ puts prompt.raw_text.wrap
331
350
  puts
332
351
 
333
352
 
334
- keywords = extract_keywords_from configatron.prompt_raw
335
- replacements = replacements_for keywords
353
+ keywords = PROMPT.parameters.keys
354
+ replacements = PROMPT.parameters
336
355
 
337
356
  prompt = replace_keywords_with replacements, configatron.prompt_raw
338
357
  ptompt = %Q{prompt}
@@ -0,0 +1 @@
1
+ {"language":"ruby"}
@@ -0,0 +1,8 @@
1
+ # .prompts/todo.txt
2
+ # Desc: replace NotImpleted.needs_to "prompt_string" with the
3
+ # results of sending the prompt_string to an LLM
4
+ #
5
+
6
+ As an experienced [LANGUAGE] software engineer write some [LANGUAGE] source code. Consider the following [LANGUAGE] file. For each comment line that contains the word "todo" take the text that follows that word as a requirement to be implemented in [LANGUAGE]. Remove the 'todo' word from the comment line. After the line insert the [LANGUAGE] code that implements the requirement.
7
+
8
+ __END__
@@ -0,0 +1,153 @@
1
+ # prompt_manager/lib/prompt_manager/prompt.rb
2
+
3
+ # TODO: Consider an ActiveModel ??
4
+
5
+ class PromptManager::Prompt
6
+ PARAMETER_REGEX = /\[([A-Z _]+)\]/.freeze
7
+ # KEYWORD_REGEX = /(\[[A-Z _|]+\])/ # NOTE: old from aip.rb
8
+ @storage_adapter = nil
9
+
10
+ class << self
11
+ attr_accessor :storage_adapter
12
+
13
+ alias_method :get, :new
14
+
15
+ def create(id:, text: "", parameters: {})
16
+ storage_adapter.save(
17
+ id: id,
18
+ text: text,
19
+ parameters: parameters
20
+ )
21
+
22
+ new(id: id)
23
+ end
24
+
25
+ def search(for_what)
26
+ storage_adapter.search(for_what)
27
+ end
28
+ end
29
+
30
+ attr_accessor :db, :id, :text, :parameters
31
+
32
+ # FIXME: Assumes that the prompt ID exists in storage,
33
+ # wo how do we create a new one?
34
+ def initialize(
35
+ id: nil, # A String name for the prompt
36
+ context: [] # FIXME: Array of Strings or Pathname?
37
+ )
38
+
39
+ raise ArgumentError, 'id cannot be blank' if id.nil? || id.strip.empty?
40
+
41
+ @id = id
42
+ @db = self.class.storage_adapter
43
+
44
+ raise(ArgumentError, 'storage_adapter is not set') if db.nil?
45
+
46
+ @record = db.get(id: id)
47
+ @text = @record[:text]
48
+ @parameters = @record[:parameters]
49
+
50
+ @prompt = interpolate_parameters
51
+ end
52
+
53
+ # Displays the prompt text after parameter interpolation.
54
+ def to_s
55
+ @prompt
56
+ end
57
+
58
+ def save
59
+ db.save(
60
+ id: id,
61
+ text: text,
62
+ parameters: parameters
63
+ )
64
+ end
65
+
66
+
67
+ def delete
68
+ db.delete(id: id)
69
+ end
70
+
71
+
72
+ ######################################
73
+ private
74
+
75
+ # Converts keys in the hash to lowercase symbols for easy parameter replacement.
76
+ def symbolize_and_downcase_keys(hash)
77
+ hash.map { |key, value| [key.to_s.downcase.to_sym, value] }.to_h
78
+ end
79
+
80
+ # Interpolate the parameters within the prompt.
81
+ def interpolate_parameters
82
+ text.gsub(PARAMETER_REGEX) do |match|
83
+ param_name = match[1..-2].downcase
84
+ parameters[param_name] || match
85
+ end
86
+ end
87
+
88
+
89
+ # TODO: Implement and integrate ignore_after_end and apply the logic within initialize.
90
+
91
+ # TODO: Implement and integrate extract_raw_prompt and apply the logic within initialize.
92
+
93
+ # TODO: Implement a better error handling strategy for the storage methods (save, search, get).
94
+
95
+ # TODO: Refactor class to support more explicit and semantic configuration and setup.
96
+
97
+ # TODO: Consider adding a method to refresh the parameters and re-interpolate the prompt text.
98
+
99
+ # TODO: Check the responsibility of the save method; should it deal with the parameters directly or leave it to storage?
100
+
101
+ # TODO: Check overall consistency and readability of the code.
102
+ end
103
+
104
+ # Usage of the fixed class would change as follows:
105
+ # Assuming Storage is a defined class that manages storing and retrieving prompts.
106
+ # storage_instance = Storage.new(...)
107
+ # PromptManager::Prompt.storage_adapter = storage_instance
108
+
109
+ # prompt = PromptManager::Prompt.new(id: 'my_prompt_id')
110
+ # puts prompt.to_s
111
+ # Expected output would depend on the parameters stored with 'my_prompt_id'
112
+
113
+ __END__
114
+
115
+ def extract_raw_prompt
116
+ array_of_strings = ignore_after_end
117
+ print_header_comment(array_of_strings)
118
+
119
+ array_of_strings.reject do |a_line|
120
+ a_line.chomp.strip.start_with?('#')
121
+ end
122
+ .join("\n")
123
+ end
124
+
125
+
126
+
127
+ def ignore_after_end
128
+ array_of_strings = configatron.prompt_path.readlines
129
+ .map{|a_line| a_line.chomp.strip}
130
+
131
+ x = array_of_strings.index("__END__")
132
+
133
+ unless x.nil?
134
+ array_of_strings = array_of_strings[..x-1]
135
+ end
136
+
137
+ array_of_strings
138
+ end
139
+
140
+
141
+
142
+
143
+
144
+
145
+ # Usage example:
146
+ prompt_text = "Hello, [NAME]! You are logged in as [ROLE]."
147
+ parameter_hash = { 'NAME' => 'Alice', 'ROLE' => 'Admin' }
148
+ file_paths = ['path/to/first_context', 'path/to/second_context']
149
+
150
+ prompt = Prompt.new(prompt_text, parameter_hash, *file_paths)
151
+ puts prompt.show
152
+ # Expected output: Hello, Alice! You are logged in as Admin.
153
+
@@ -0,0 +1,66 @@
1
+ # prompt_manager/lib/prompt_manager/storage/active_record_adapter.rb
2
+
3
+ require 'active_record'
4
+
5
+ # TODO: Will need a database.yml file
6
+ # will need to know the column names that coorespond
7
+ # with the things that the Prompt class wants.
8
+
9
+ class PromptManager::Storage::ActiveRecordAdapter
10
+ attr_reader :model_class
11
+
12
+ def initialize(model_class)
13
+ @model_class = model_class
14
+ end
15
+
16
+
17
+ def prompt_text(prompt_id)
18
+ prompt = find_prompt(prompt_id)
19
+ prompt.text
20
+ end
21
+
22
+
23
+ def parameter_values(prompt_id)
24
+ prompt = find_prompt(prompt_id)
25
+ JSON.parse(prompt.params, symbolize_names: true)
26
+ end
27
+
28
+
29
+ def save(prompt_id, prompt_text, parameter_values)
30
+ prompt = model_class.find_or_initialize_by(id: prompt_id)
31
+ prompt.text = prompt_text
32
+ prompt.params = parameter_values.to_json
33
+ prompt.save!
34
+ end
35
+
36
+
37
+ def delete(prompt_id)
38
+ prompt = find_prompt(prompt_id)
39
+ prompt.destroy
40
+ end
41
+
42
+
43
+ def search(for_what)
44
+ # TODO: search through all prompts. Return an Array of
45
+ # prompt_id where the text of the prompt contains
46
+ # for_what is being searched.
47
+
48
+ []
49
+ end
50
+
51
+
52
+ class << self
53
+ def config
54
+ # TODO: establish a connection to the database
55
+ # maybe define the prompts table and its
56
+ # columns of interest.
57
+ end
58
+ end
59
+
60
+ ###############################################
61
+ private
62
+
63
+ def find_prompt(prompt_id)
64
+ model_class.find_by(id: prompt_id) || raise('Prompt not found')
65
+ end
66
+ end
@@ -0,0 +1,149 @@
1
+ # prompt_manager/lib/prompt_manager/storage/file_system_adapter.rb
2
+
3
+
4
+ require 'json'
5
+
6
+ class PromptManager::Storage::FileSystemAdapter
7
+ PARAMS_EXTENSION = '.json'.freeze
8
+ PROMPT_EXTENSION = '.txt'.freeze
9
+
10
+ attr_reader :prompts_dir
11
+
12
+ def initialize(
13
+ prompts_dir: '.prompts',
14
+ search_proc: nil # Example: ->(q) {`ag -l #{q}`}
15
+ )
16
+
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"
20
+ end
21
+
22
+ @prompts_dir = prompts_dir
23
+ @search_proc = search_proc
24
+ end
25
+
26
+
27
+ def get(id:)
28
+ validate_id(id)
29
+
30
+ {
31
+ id: id,
32
+ text: prompt_text(id),
33
+ parameters: parameter_values(id)
34
+ }
35
+ end
36
+
37
+
38
+ # Retrieve prompt text by its id
39
+ def prompt_text(prompt_id)
40
+ read_file(file_path(prompt_id, PROMPT_EXTENSION))
41
+ end
42
+
43
+
44
+ # Retrieve parameter values by its id
45
+ def parameter_values(prompt_id)
46
+ json_content = read_file(file_path(prompt_id, PARAMS_EXTENSION))
47
+ deserialize(json_content)
48
+ end
49
+
50
+
51
+ # Save prompt text and parameter values to corresponding files
52
+ def save(
53
+ id:,
54
+ text: "",
55
+ parameters: {}
56
+ )
57
+ validate_id(id)
58
+
59
+ prompt_filepath = file_path(id, PROMPT_EXTENSION)
60
+ params_filepath = file_path(id, PARAMS_EXTENSION)
61
+
62
+ write_with_error_handling(prompt_filepath, text)
63
+ write_with_error_handling(params_filepath, serialize(parameters))
64
+ end
65
+
66
+
67
+ # Delete prompted text and parameter values files
68
+ def delete(id:)
69
+ validate_id(id)
70
+
71
+ prompt_filepath = file_path(id, PROMPT_EXTENSION)
72
+ params_filepath = file_path(id, PARAMS_EXTENSION)
73
+
74
+ delete_with_error_handling(prompt_filepath)
75
+ delete_with_error_handling(params_filepath)
76
+ end
77
+
78
+
79
+ def search(for_what)
80
+ if @search_proc
81
+ @search_proc.call(for_what)
82
+ else
83
+ search_prompts(for_what)
84
+ end
85
+ end
86
+
87
+
88
+ ##########################################
89
+ private
90
+
91
+ def validate_id(id)
92
+ raise ArgumentError, 'Invalid ID format' unless id =~ /^[a-zA-Z0-9\-_]+$/
93
+ end
94
+
95
+
96
+ def write_with_error_handling(file_path, content)
97
+ begin
98
+ File.write(file_path, content)
99
+ rescue IOError => e
100
+ raise "Failed to write to file: #{e.message}"
101
+ end
102
+ end
103
+
104
+
105
+ def delete_with_error_handling(file_path)
106
+ begin
107
+ FileUtils.rm_f(file_path)
108
+ rescue IOError => e
109
+ raise "Failed to delete file: #{e.message}"
110
+ end
111
+ end
112
+
113
+
114
+ def file_path(id, extension)
115
+ File.join(@prompts_dir, "#{id}#{extension}")
116
+ end
117
+
118
+
119
+ def read_file(full_path)
120
+ raise IOError, 'File does not exist' unless File.exist?(full_path)
121
+ File.read(full_path)
122
+ end
123
+
124
+
125
+ 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
136
+ end
137
+ end.uniq
138
+ end
139
+
140
+
141
+ def serialize(data)
142
+ data.to_json
143
+ end
144
+
145
+
146
+ def deserialize(data)
147
+ JSON.parse(data, symbolize_names: true)
148
+ end
149
+ end
@@ -0,0 +1,65 @@
1
+ # prompt_manager/lib/prompt_manager/storage/sqlite_adapter.rb
2
+
3
+ require 'json'
4
+ require 'sqlite3'
5
+
6
+ class PromptManager::Storage::SqliteAdapter
7
+ def initialize(db_path = 'prompts.db')
8
+ @db = SQLite3::Database.new(db_path)
9
+ create_tables
10
+ end
11
+
12
+ def save_prompt(prompt_id, text, params)
13
+ @db.execute("INSERT INTO prompts (id, text, params) VALUES (?, ?, ?)",
14
+ [prompt_id, text, params.to_json])
15
+ end
16
+
17
+ def prompt_text(prompt_id)
18
+ result = @db.get_first_value("SELECT text FROM prompts WHERE id = ?", [prompt_id])
19
+ raise 'Prompt not found' if result.nil?
20
+ result
21
+ end
22
+
23
+ def parameter_values(prompt_id)
24
+ json_content = @db.get_first_value("SELECT params FROM prompts WHERE id = ?", [prompt_id])
25
+ raise 'Parameters not found' if json_content.nil?
26
+ JSON.parse(json_content, symbolize_names: true)
27
+ end
28
+
29
+ def save(prompt_id, prompt_text, parameter_values)
30
+ @db.execute(
31
+ 'REPLACE INTO prompts (id, text, params) VALUES (?, ?, ?)',
32
+ [prompt_id, prompt_text, parameter_values.to_json]
33
+ )
34
+ end
35
+
36
+ def delete(prompt_id)
37
+ @db.execute('DELETE FROM prompts WHERE id = ?', [prompt_id])
38
+ end
39
+
40
+
41
+ def search(for_what)
42
+ # TODO: search through all prompts. Return an Array of
43
+ # prompt_id where the text of the prompt contains
44
+ # for_what is being searched.
45
+
46
+ []
47
+ end
48
+
49
+ ###################################################
50
+ private
51
+
52
+ def create_tables
53
+ @db.execute <<-SQL
54
+ CREATE TABLE IF NOT EXISTS prompts (
55
+ id TEXT PRIMARY KEY,
56
+ text TEXT NOT NULL,
57
+ params TEXT NOT NULL
58
+ );
59
+ SQL
60
+ end
61
+ end
62
+
63
+
64
+
65
+
@@ -0,0 +1,7 @@
1
+ # prompt_manager/lib/prompt_manager/storage.rb
2
+
3
+ module PromptManager
4
+ module Storage
5
+ end
6
+ end
7
+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PromptManager
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
@@ -1,8 +1,14 @@
1
+ # prompt_manager/lib/prompt_manager.rb
2
+ #
1
3
  # frozen_string_literal: true
2
4
 
5
+ require 'debug_me'
6
+ include DebugMe
7
+
3
8
  require_relative "prompt_manager/version"
9
+ require_relative "prompt_manager/storage"
10
+ require_relative "prompt_manager/prompt"
4
11
 
5
12
  module PromptManager
6
13
  class Error < StandardError; end
7
- # Your code goes here...
8
14
  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.0.1
4
+ version: 0.0.2
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-16 00:00:00.000000000 Z
11
+ date: 2023-11-18 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Manage parameterized prompt text for use with GPT/LLM
14
14
  email:
@@ -23,10 +23,16 @@ files:
23
23
  - LICENSE.txt
24
24
  - README.md
25
25
  - Rakefile
26
- - lib/aip.rb
26
+ - examples/aip.rb
27
+ - examples/prompts_dir/todo.json
28
+ - examples/prompts_dir/todo.txt
27
29
  - lib/prompt_manager.rb
30
+ - lib/prompt_manager/prompt.rb
31
+ - lib/prompt_manager/storage.rb
32
+ - lib/prompt_manager/storage/active_record_adapter.rb
33
+ - lib/prompt_manager/storage/file_system_adapter.rb
34
+ - lib/prompt_manager/storage/sqlite_adapter.rb
28
35
  - lib/prompt_manager/version.rb
29
- - lib/temp.md
30
36
  homepage: https://github.com/MadBomber/prompt_manager
31
37
  licenses:
32
38
  - MIT
@@ -50,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
56
  - !ruby/object:Gem::Version
51
57
  version: '0'
52
58
  requirements: []
53
- rubygems_version: 3.5.0.dev
59
+ rubygems_version: 3.4.22
54
60
  signing_key:
55
61
  specification_version: 4
56
62
  summary: Manage prompts for use with chatGPT LLMs
data/lib/temp.md DELETED
@@ -1,57 +0,0 @@
1
- ### README for aip.rb Ruby Script
2
-
3
- #### Overview
4
-
5
- The `aip.rb` Ruby script is a command-line interface (CLI) tool designed to leverage generative AI with saved parameterized prompts. It integrates with the `mods` command-line tool that uses a GPT-based model to generate responses based on user-provided prompts. The script offers an array of features that make interacting with AI models more convenient and streamlined.
6
-
7
- #### Features
8
-
9
- - **Prompt Management**
10
- - Users can select prompts from a saved collection with the help of command-line searching and filtering.
11
- - Prompts can be edited by the user to better fit their specific context or requirement.
12
- - Support for reading input from files to provide context for AI generation is included.
13
-
14
- - **AI Integration**
15
- - The script interacts with `mods`, a generative AI utilizing GPT-based models, to produce outputs from the prompts.
16
-
17
- - **Output Handling**
18
- - Generated content is saved to a specified file for record-keeping and further use.
19
-
20
- - **Activity Logging**
21
- - All actions, including prompt usage and AI output, are logged with timestamps for review and auditing purposes.
22
-
23
- #### Dependencies
24
-
25
- The script requires the installation of the following command-line tools:
26
-
27
- - `fzf`: a powerful command-line fuzzy finder.
28
- - `mods`: an AI-powered CLI tool for generative AI interactions.
29
- - `the_silver_searcher (ag)`: a code-searching tool similar to ack and used for searching prompts.
30
-
31
- #### Usage
32
-
33
- The `aip.rb` script offers a set of command-line options to guide the interaction with AI:
34
-
35
- - `-p, --prompt`: Specify the prompt name to be used.
36
- - `-e, --edit`: Open the prompt text for editing before generation.
37
- - `-f, --fuzzy`: Allows fuzzy matching for prompt selection.
38
- - `-o, --output`: Sets the output file for the generated content.
39
-
40
- Additional flags and options can be passed to the `mods` tool by appending them after a `--` separator.
41
-
42
- #### Installation
43
-
44
- Before using the script, one must ensure the required command-line tools (`fzf`, `mods`, and `the_silver_searcher`) are installed, and the Ruby environment is correctly set up with the necessary gems.
45
-
46
- #### Development Notes
47
-
48
- The author suggests that the script has matured enough to be converted into a Ruby gem for easier distribution and installation.
49
-
50
- #### Getting Help
51
-
52
- For help with using the CLI tool or further understanding the `mods` command, users can refer to the AI CLI Program help section included in the script or by invoking the `--help` flag.
53
-
54
- #### Conclusion
55
-
56
- The `aip.rb` script is designed to offer a user-friendly and flexible approach to integrating generative AI into content creation processes. It streamlines the interactions and management of AI-generated content by providing prompt management, AI integration, and logging capabilities, packaged inside a simple command-line utility.
57
-