prompt_manager 0.2.1 → 0.3.0

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: 6c96927dad3be1d7e793e3edd5d3df3e3a12999fcec57ad01d3d773d9b40e674
4
- data.tar.gz: 4aa47ce704540fa933dbdf30d3afddbb3704bd541b4dab1f7a751e0e461f3c1e
3
+ metadata.gz: 6785a25ba5b2c7e8bacf3bdba572eced1c95bdab2f83144a4ec32f0ae359eeb1
4
+ data.tar.gz: fd21f559d660899ec94765b7ef3f3fd6a4579ac625a44af428ab7771ba934713
5
5
  SHA512:
6
- metadata.gz: '0857d6d0532c0c1293799e760933e5bf2ee1a82f8cfdce6dd649b187f724e31935bf959b04c8058a90877525751cf99deaa4fd51a9cc64d0259a3d4b1e40de38'
7
- data.tar.gz: 59d2e1cabed3a87766092f56eae6ed7e7d30a2c0d0b3e3dc2efd93fe9ce79d97ce7ee5af3d1beae6896aed3a1754649dc7a7f3bbdec1e0566015640d8f7f2128
6
+ metadata.gz: 4373edc2de2e838a606ab748a81a73bc15242f9bac45343ce5d89fa2cd13fd57b228bf1f34e1a870e31640ff7f957382d44b65bcf0fa7360d004b95854965dae
7
+ data.tar.gz: e1b04a1643de8de6a24618d5e2737241b17580922436bf6f3fa73e9caf394f9122426721eb3a44b37f227bd2eba21b0e20fad150a2b98265d707be033a7c4887
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] = 2023-11-28
4
+
5
+ - **Breaking change** The value of the parameters Hash for a keyword is now an Array instead of a single value. The last value in the Array is always the most recent value used for the given keyword. This was done to support the use of a Readline::History object editing in the [aia](https://github.com/MadBomber/aia) CLI tool
6
+
3
7
  ## [0.2.0] - 2023-11-21
4
8
 
5
9
  - **Breaking change to FileSystemAdapter config process**
data/README.md CHANGED
@@ -2,7 +2,8 @@
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.
5
+ **Breaking Change** in version 0.3.0 - The value of the parameters Hash for a keyword is now an Array instead of a single value. The last value in the Array is always the most recent value used for the given keyword. This was done to support the use of a Readline::History object editing in the [aia](https://github.com/MadBomber/aia) CLI tool
6
+
6
7
 
7
8
  <!-- Tocer[start]: Auto-generated, don't remove. -->
8
9
 
@@ -19,6 +20,8 @@ Manage the parameterized prompts (text) used in generative AI (aka chatGPT, Open
19
20
  - [prompts_dir](#prompts_dir)
20
21
  - [search_proc](#search_proc)
21
22
  - [File Extensions](#file-extensions)
23
+ - [Example Prompt Text File](#example-prompt-text-file)
24
+ - [Example Prompt Parameters JSON File](#example-prompt-parameters-json-file)
22
25
  - [Extra Functionality](#extra-functionality)
23
26
  - [SqliteAdapter](#sqliteadapter)
24
27
  - [ActiveRecordAdapter](#activerecordadapter)
@@ -118,6 +121,36 @@ Currently the `FileSystemAdapter` only supports a JSON serializer for its parame
118
121
 
119
122
  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
123
 
124
+ ##### Example Prompt Text File
125
+
126
+ ```text
127
+ # ~/.prompts/joke.txt
128
+ # Desc: Tell some jokes
129
+
130
+ Tell me a few [KIND] jokes about [SUBJECT]
131
+ ```
132
+
133
+ Note the command lines at the top. This is a convention I use. It is not part of the software. I find it helpful in documenting the prompt.
134
+
135
+ ##### Example Prompt Parameters JSON File
136
+
137
+ ```json
138
+ {
139
+ "[KIND]": [
140
+ "pun",
141
+ "family friendly"
142
+ ],
143
+ "[SUBJECT]": [
144
+ "parrot",
145
+ "garbage man",
146
+ "snowman",
147
+ "weather girl"
148
+ ]
149
+ }
150
+ ```
151
+
152
+ The last value in the keyword's Array is the most recent value used for that keyword. This is a functionality established since v0.3.0. Its purpose is to provide a history of values from which a user can select to repeat a previous value or to select ta previous value and edit it into something new.
153
+
121
154
  ##### Extra Functionality
122
155
 
123
156
  The `FileSystemAdapter` adds two new methods for use by the `Prompt` class:
@@ -108,7 +108,7 @@ class PromptManager::Prompt
108
108
  def build
109
109
  @prompt = text.gsub(PARAMETER_REGEX) do |match|
110
110
  param_name = match
111
- parameters[param_name] || match
111
+ parameters[param_name].last || match
112
112
  end
113
113
 
114
114
  remove_comments
@@ -120,7 +120,7 @@ class PromptManager::Prompt
120
120
  def update_keywords
121
121
  @keywords = @text.scan(PARAMETER_REGEX).flatten.uniq
122
122
  keywords.each do |kw|
123
- @parameters[kw] = "" unless @parameters.has_key?(kw)
123
+ @parameters[kw] = [] unless @parameters.has_key?(kw)
124
124
  end
125
125
  end
126
126
 
@@ -63,4 +63,99 @@ class PromptManager::Storage::ActiveRecordAdapter
63
63
  def find_prompt(prompt_id)
64
64
  model_class.find_by(id: prompt_id) || raise('Prompt not found')
65
65
  end
66
- end
66
+ end
67
+
68
+
69
+ __END__
70
+
71
+ # prompt_manager/lib/prompt_manager/storage/active_record_adapter.rb
72
+
73
+ require 'active_record'
74
+
75
+ module PromptManager
76
+ module Storage
77
+ class ActiveRecordAdapter
78
+
79
+ # Define models for ActiveRecord
80
+ class Prompt < ActiveRecord::Base
81
+ validates :unique_id, presence: true
82
+ validates :text, presence: true
83
+ end
84
+
85
+ class PromptParameter < ActiveRecord::Base
86
+ belongs_to :prompt
87
+ validates :key, presence: true
88
+ serialize :value
89
+ end
90
+
91
+ def initialize
92
+ unless ActiveRecord::Base.connected?
93
+ raise ArgumentError, "ActiveRecord is not connected"
94
+ end
95
+ end
96
+
97
+ def get(id:)
98
+ prompt = Prompt.find_by(unique_id: id)
99
+ return nil unless prompt
100
+
101
+ parameters = prompt.prompt_parameters.index_by(&:key)
102
+
103
+ {
104
+ id: prompt.unique_id,
105
+ text: prompt.text,
106
+ parameters: parameters.transform_values(&:value)
107
+ }
108
+ end
109
+
110
+ def save(id:, text: "", parameters: {})
111
+ prompt = Prompt.find_or_initialize_by(unique_id: id)
112
+ prompt.text = text
113
+ prompt.save!
114
+
115
+ parameters.each do |key, value|
116
+ parameter = PromptParameter.find_or_initialize_by(prompt: prompt, key: key)
117
+ parameter.value = value
118
+ parameter.save!
119
+ end
120
+ end
121
+
122
+ def delete(id:)
123
+ prompt = Prompt.find_by(unique_id: id)
124
+ return unless prompt
125
+
126
+ prompt.prompt_parameters.destroy_all
127
+ prompt.destroy
128
+ end
129
+
130
+ def search(for_what)
131
+ query = '%' + for_what.downcase + '%'
132
+ Prompt.where('LOWER(text) LIKE ?', query).pluck(:unique_id)
133
+ end
134
+
135
+ def list(*)
136
+ Prompt.pluck(:unique_id)
137
+ end
138
+
139
+ private
140
+
141
+ # This is an example of how the database connection setup could look like,
142
+ # but it should be handled externally in the actual application setup.
143
+ def self.setup_database_connection
144
+ ActiveRecord::Base.establish_connection(
145
+ adapter: 'sqlite3',
146
+ database: 'prompts.db'
147
+ )
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ # After this, you would need to create a database migration to generate the required tables.
154
+ # Additionally, you have to establish an ActiveRecord connection before using this adapter,
155
+ # typically in the environment setup of your application.
156
+
157
+ # Keep in mind you need to create migrations for both the Prompt and PromptParameter models,
158
+ # and manage the database schema using ActiveRecord migrations. This adapter assumes that the
159
+ # database structure is already in place and follows the schema inferred by the models in the adapter.
160
+
161
+
@@ -61,5 +61,96 @@ class PromptManager::Storage::SqliteAdapter
61
61
  end
62
62
 
63
63
 
64
+ __END__
65
+
66
+
67
+ require 'sequel'
68
+ require 'pathname'
69
+ require 'json'
70
+
71
+ module PromptManager
72
+ module Storage
73
+ class SqliteAdapter
74
+ DEFAULT_DB_FILE = 'prompts.sqlite3'.freeze
75
+
76
+ class << self
77
+ attr_accessor :db_file, :db
78
+
79
+ def config
80
+ if block_given?
81
+ yield self
82
+ else
83
+ raise ArgumentError, 'No block given to config'
84
+ end
85
+
86
+ self.db = Sequel.sqlite(db_file || DEFAULT_DB_FILE) # Use provided db_file or default
87
+ create_tables unless db.table_exists?(:prompts)
88
+ end
89
+
90
+ # Define the necessary tables within the SQLite database if they don't exist
91
+ def create_tables
92
+ db.create_table :prompts do
93
+ primary_key :id
94
+ String :prompt_id, unique: true, null: false
95
+ Text :text
96
+ Json :parameters
97
+
98
+ index :prompt_id
99
+ end
100
+ end
101
+ end
102
+
103
+ def initialize
104
+ @db = self.class.db
105
+ end
106
+
107
+ def get(id:)
108
+ validate_id(id)
109
+ result = @db[:prompts].where(prompt_id: id).first
110
+ raise ArgumentError, 'Prompt not found' unless result
111
+
112
+ {
113
+ id: result[:prompt_id],
114
+ text: result[:text],
115
+ parameters: result[:parameters]
116
+ }
117
+ end
118
+
119
+ def save(id:, text: '', parameters: {})
120
+ validate_id(id)
121
+ rec = @db[:prompts].where(prompt_id: id).first
122
+ if rec
123
+ @db[:prompts].where(prompt_id: id).update(text: text, parameters: Sequel.pg_json(parameters))
124
+ else
125
+ @db[:prompts].insert(prompt_id: id, text: text, parameters: Sequel.pg_json(parameters))
126
+ end
127
+ end
128
+
129
+ def delete(id:)
130
+ validate_id(id)
131
+ @db[:prompts].where(prompt_id: id).delete
132
+ end
133
+
134
+ # Return an Array of prompt IDs
135
+ def list()
136
+ @db[:prompts].select_map(:prompt_id)
137
+ end
138
+
139
+ def search(for_what)
140
+ search_term = for_what.downcase
141
+ @db[:prompts].where(Sequel.ilike(:text, "%#{search_term}%")).select_map(:prompt_id)
142
+ end
143
+
144
+ private
145
+
146
+ # Validate that the ID contains good characters.
147
+ def validate_id(id)
148
+ raise ArgumentError, "Invalid ID format id: #{id}" unless id =~ /^[a-zA-Z0-9\-\/_]+$/
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+
64
155
 
65
156
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PromptManager
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.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.2.1
4
+ version: 0.3.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-23 00:00:00.000000000 Z
11
+ date: 2023-11-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: amazing_print