prompt_manager 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +34 -1
- data/lib/prompt_manager/prompt.rb +2 -2
- data/lib/prompt_manager/storage/active_record_adapter.rb +96 -1
- data/lib/prompt_manager/storage/sqlite_adapter.rb +91 -0
- data/lib/prompt_manager/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6785a25ba5b2c7e8bacf3bdba572eced1c95bdab2f83144a4ec32f0ae359eeb1
|
4
|
+
data.tar.gz: fd21f559d660899ec94765b7ef3f3fd6a4579ac625a44af428ab7771ba934713
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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] =
|
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
|
|
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.
|
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-
|
11
|
+
date: 2023-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: amazing_print
|