prompt_manager 0.0.1 → 0.1.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 +1 -1
- data/README.md +31 -61
- data/Rakefile +1 -1
- data/examples/prompts_dir/todo.json +1 -0
- data/examples/prompts_dir/todo.txt +7 -0
- data/examples/simple.rb +89 -0
- data/lib/prompt_manager/prompt.rb +151 -0
- data/lib/prompt_manager/storage/active_record_adapter.rb +66 -0
- data/lib/prompt_manager/storage/file_system_adapter.rb +149 -0
- data/lib/prompt_manager/storage/sqlite_adapter.rb +65 -0
- data/lib/prompt_manager/storage.rb +7 -0
- data/lib/prompt_manager/version.rb +1 -1
- data/lib/prompt_manager.rb +5 -1
- metadata +58 -8
- data/lib/aip.rb +0 -402
- data/lib/temp.md +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2772d50f835a71c26631b2109af906008fec84b62e7ccf435a188fb856415268
|
4
|
+
data.tar.gz: c1aa3889826246a79df35ad74cd221c1374af161083a34f50d64ef09f5606ade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c43cc115f2dfa8e847c499386b482d29f3454ca068b0d161d8c14350002d955461962e1009af0a932a92388eef39b134515bca6f694dabd41110150301339b56
|
7
|
+
data.tar.gz: 55b0e4f827d1950f39ad87909954660cb5ae0f6da44a4de0b43aad637dc1d99d878d2cb1876ac4a001cd42c190e1160a76593c6f4cba3b8411c250bd42e97e35
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,99 +1,69 @@
|
|
1
1
|
# PromptManager
|
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
|
-
|
6
|
-
|
7
|
-
## AIP.RB Legacy Summary of Capability
|
8
|
-
|
9
|
-
This is just some source material for later documentation.
|
10
|
-
|
11
|
-
### README for aip.rb
|
12
|
-
|
13
|
-
#### Overview
|
14
|
-
|
15
|
-
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.
|
16
|
-
|
17
|
-
#### Features
|
18
|
-
|
19
|
-
- **Prompt Management**
|
20
|
-
- Users can select prompts from a saved collection with the help of command-line searching and filtering.
|
21
|
-
- Prompts can be edited by the user to better fit their specific context or requirement.
|
22
|
-
- Support for reading input from files to provide context for AI generation is included.
|
23
|
-
|
24
|
-
- **AI Integration**
|
25
|
-
- The script interacts with `mods`, a generative AI utilizing GPT-based models, to produce outputs from the prompts.
|
26
|
-
|
27
|
-
- **Output Handling**
|
28
|
-
- Generated content is saved to a specified file for record-keeping and further use.
|
29
|
-
|
30
|
-
- **Activity Logging**
|
31
|
-
- All actions, including prompt usage and AI output, are logged with timestamps for review and auditing purposes.
|
32
|
-
|
33
|
-
#### Dependencies
|
5
|
+
## Installation
|
34
6
|
|
35
|
-
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
36
8
|
|
37
|
-
|
38
|
-
- `mods`: an AI-powered CLI tool for generative AI interactions.
|
39
|
-
- `the_silver_searcher (ag)`: a code-searching tool similar to ack and used for searching prompts.
|
9
|
+
bundle add prompt_manager
|
40
10
|
|
41
|
-
|
11
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
42
12
|
|
43
|
-
|
13
|
+
gem install prompt_manager
|
44
14
|
|
45
|
-
|
46
|
-
- `-e, --edit`: Open the prompt text for editing before generation.
|
47
|
-
- `-f, --fuzzy`: Allows fuzzy matching for prompt selection.
|
48
|
-
- `-o, --output`: Sets the output file for the generated content.
|
15
|
+
## Usage
|
49
16
|
|
50
|
-
|
17
|
+
See [examples/simple.rb](examples.simple.rb)
|
51
18
|
|
52
|
-
|
19
|
+
## Overview
|
53
20
|
|
54
|
-
|
21
|
+
### Generative AI (gen-AI)
|
55
22
|
|
56
|
-
|
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.
|
57
24
|
|
58
|
-
The
|
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.
|
59
26
|
|
60
|
-
####
|
27
|
+
#### What does a keyword look like?
|
61
28
|
|
62
|
-
|
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.
|
63
30
|
|
64
|
-
|
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.
|
65
32
|
|
66
|
-
|
33
|
+
### Storage Adapters
|
67
34
|
|
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.
|
68
36
|
|
37
|
+
#### FileSystemAdapter
|
69
38
|
|
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.
|
70
40
|
|
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.)
|
71
42
|
|
72
|
-
|
43
|
+
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.)
|
73
44
|
|
74
|
-
|
45
|
+
#### SqliteAdapter
|
75
46
|
|
76
|
-
|
47
|
+
TODO: This may be the next adapter to be implemented.
|
77
48
|
|
78
|
-
|
49
|
+
#### ActiveRecordAdapter
|
79
50
|
|
80
|
-
|
51
|
+
TODO: Still looking for requirements on how to integrate with an existing `rails` app. Looking for ideas.
|
81
52
|
|
82
|
-
|
53
|
+
#### Other Potential Storage Adapters
|
83
54
|
|
84
|
-
|
55
|
+
There are many possibilities to example this plugin concept of the storage adapter. Here are some for consideration:
|
85
56
|
|
86
|
-
|
57
|
+
* RedisAdapter - Not sure; isn't redis more temporary oriented?
|
58
|
+
* ApiAdapter - use some end-point to CRUD a prompt
|
87
59
|
|
88
60
|
## Development
|
89
61
|
|
90
|
-
|
91
|
-
|
92
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
62
|
+
Looking for feedback and contributors to enhance the capability of prompt_manager.
|
93
63
|
|
94
64
|
## Contributing
|
95
65
|
|
96
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
66
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/MadBomber/prompt_manager.
|
97
67
|
|
98
68
|
## License
|
99
69
|
|
data/Rakefile
CHANGED
@@ -0,0 +1 @@
|
|
1
|
+
{"[LANGUAGE]":"ruby"}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# prompts_dir/todo.txt
|
2
|
+
# Desc: Let the robot fix the TODO items.
|
3
|
+
#
|
4
|
+
|
5
|
+
As an experienced [LANGUAGE] software engineer write some [LANGUAGE] source code. Consider the following [LANGUAGE] file. For each comment line that contains the word "[KEYWORD_AKA_TODO]" take the text that follows that word as a requirement to be implemented in [LANGUAGE]. Remove the "[KEYWORD_AKA_TODO]" word from the comment line. After the line insert the [LANGUAGE] code that implements the requirement.
|
6
|
+
|
7
|
+
__END__
|
data/examples/simple.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
# frozen_string_literal: true
|
4
|
+
# warn_indent: true
|
5
|
+
##########################################################
|
6
|
+
###
|
7
|
+
## File: simple.rb
|
8
|
+
## Desc: Simple demo of the PromptManager and FileStorageAdapter
|
9
|
+
## By: Dewayne VanHoozer (dvanhoozer@gmail.com)
|
10
|
+
##
|
11
|
+
#
|
12
|
+
|
13
|
+
|
14
|
+
require 'prompt_manager'
|
15
|
+
require 'prompt_manager/storage/file_system_adapter'
|
16
|
+
|
17
|
+
require 'amazing_print'
|
18
|
+
require 'pathname'
|
19
|
+
|
20
|
+
HERE = Pathname.new( __dir__ )
|
21
|
+
PROMPTS_DIR = HERE + "prompts_dir"
|
22
|
+
|
23
|
+
|
24
|
+
######################################################
|
25
|
+
# Main
|
26
|
+
|
27
|
+
at_exit do
|
28
|
+
puts
|
29
|
+
puts "Done."
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
|
33
|
+
# Configure the Storage Adapter to use
|
34
|
+
|
35
|
+
PromptManager::Prompt.storage_adapter =
|
36
|
+
PromptManager::Storage::FileSystemAdapter.new(
|
37
|
+
prompts_dir: PROMPTS_DIR
|
38
|
+
)
|
39
|
+
|
40
|
+
# Get a prompt
|
41
|
+
|
42
|
+
todo = PromptManager::Prompt.get(id: 'todo')
|
43
|
+
|
44
|
+
# This sequence simulates presenting each of the previously
|
45
|
+
# used values for each keyword to the user to accept or
|
46
|
+
# edit.
|
47
|
+
|
48
|
+
# ap todo.keywords
|
49
|
+
|
50
|
+
# This is a new keyword that was added after the current
|
51
|
+
# todo.json file was created. Simulate the user setting
|
52
|
+
# its value.
|
53
|
+
|
54
|
+
todo.parameters["[KEYWORD_AKA_TODO]"] = "TODO"
|
55
|
+
|
56
|
+
# When the parameter values change, the prompt must
|
57
|
+
# must be rebuilt using the build method.
|
58
|
+
|
59
|
+
todo.build
|
60
|
+
|
61
|
+
|
62
|
+
puts <<~EOS
|
63
|
+
|
64
|
+
Raw Text from Prompt File
|
65
|
+
includes all lines
|
66
|
+
=========================
|
67
|
+
EOS
|
68
|
+
|
69
|
+
puts todo.text
|
70
|
+
|
71
|
+
|
72
|
+
puts <<~EOS
|
73
|
+
|
74
|
+
Last Set of Parameters Used
|
75
|
+
Includes those recently added
|
76
|
+
=============================
|
77
|
+
EOS
|
78
|
+
|
79
|
+
ap todo.parameters
|
80
|
+
|
81
|
+
|
82
|
+
puts <<~EOS
|
83
|
+
|
84
|
+
Prompt Ready to Send to gen-AI
|
85
|
+
==============================
|
86
|
+
EOS
|
87
|
+
|
88
|
+
puts todo.to_s
|
89
|
+
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# prompt_manager/lib/prompt_manager/prompt.rb
|
2
|
+
|
3
|
+
# This class is responsible for managing prompts which can be utilized by
|
4
|
+
# generative AI processes. This includes creation, retrieval, storage management,
|
5
|
+
# as well as building prompts with replacement of parameterized values and
|
6
|
+
# comment removal. It communicates with a storage system through a storage
|
7
|
+
# adapter.
|
8
|
+
|
9
|
+
class PromptManager::Prompt
|
10
|
+
PARAMETER_REGEX = /(\[[A-Z _|]+\])/ # NOTE: old from aip.rb
|
11
|
+
@storage_adapter = nil
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_accessor :storage_adapter
|
15
|
+
|
16
|
+
alias_method :get, :new
|
17
|
+
|
18
|
+
def create(id:, text: "", parameters: {})
|
19
|
+
storage_adapter.save(
|
20
|
+
id: id,
|
21
|
+
text: text,
|
22
|
+
parameters: parameters
|
23
|
+
)
|
24
|
+
|
25
|
+
new(id: id)
|
26
|
+
end
|
27
|
+
|
28
|
+
def search(for_what)
|
29
|
+
storage_adapter.search(for_what)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# SMELL: Does the db (aka storage adapter) really need
|
34
|
+
# to be accessible by the main program?
|
35
|
+
attr_accessor :db, :id, :text, :parameters, :keywords
|
36
|
+
|
37
|
+
|
38
|
+
# Retrieve the specific prompt ID from the Storage system.
|
39
|
+
def initialize(
|
40
|
+
id: nil, # A String name for the prompt
|
41
|
+
context: [] # FIXME: Array of Strings or Pathname?
|
42
|
+
)
|
43
|
+
|
44
|
+
@id = id
|
45
|
+
@db = self.class.storage_adapter
|
46
|
+
|
47
|
+
validate_arguments(@id, @db)
|
48
|
+
|
49
|
+
@record = db.get(id: id)
|
50
|
+
@text = @record[:text]
|
51
|
+
@parameters = @record[:parameters]
|
52
|
+
@keywords = []
|
53
|
+
|
54
|
+
update_keywords
|
55
|
+
build
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
# Make sure the ID and DB are good-to-go
|
60
|
+
def validate_arguments(prompt_id, prompts_db)
|
61
|
+
raise ArgumentError, 'id cannot be blank' if prompt_id.nil? || id.strip.empty?
|
62
|
+
raise(ArgumentError, 'storage_adapter is not set') if prompts_db.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Return tje prompt text suitable for passing to a
|
67
|
+
# gen-AI process.
|
68
|
+
def to_s
|
69
|
+
@prompt
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# Save the prompt to the Storage system
|
74
|
+
def save
|
75
|
+
db.save(
|
76
|
+
id: id,
|
77
|
+
text: text,
|
78
|
+
parameters: parameters
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Delete this prompt from the Storage system
|
84
|
+
def delete
|
85
|
+
db.delete(id: id)
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
# Build the @prompt String by replacing the keywords
|
90
|
+
# with there parameterized values and removing all
|
91
|
+
# the comments.
|
92
|
+
#
|
93
|
+
def build
|
94
|
+
@prompt = text.gsub(PARAMETER_REGEX) do |match|
|
95
|
+
param_name = match
|
96
|
+
parameters[param_name] || match
|
97
|
+
end
|
98
|
+
|
99
|
+
remove_comments
|
100
|
+
end
|
101
|
+
|
102
|
+
######################################
|
103
|
+
private
|
104
|
+
|
105
|
+
def update_keywords
|
106
|
+
@keywords = @text.scan(PARAMETER_REGEX).flatten.uniq
|
107
|
+
keywords.each do |kw|
|
108
|
+
@parameters[kw] = "" unless @parameters.has_key?(kw)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
def remove_comments
|
114
|
+
lines = @prompt
|
115
|
+
.split("\n")
|
116
|
+
.reject{|a_line| a_line.strip.start_with?('#')}
|
117
|
+
|
118
|
+
# Remove empty lines at the start of the prompt
|
119
|
+
#
|
120
|
+
lines = lines.drop_while(&:empty?)
|
121
|
+
|
122
|
+
# Drop all the lines at __END__ and after
|
123
|
+
#
|
124
|
+
logical_end_inx = lines.index("__END__")
|
125
|
+
|
126
|
+
@prompt = if logical_end_inx
|
127
|
+
lines[0...logical_end_inx] # NOTE: ... means to not include last index
|
128
|
+
else
|
129
|
+
lines
|
130
|
+
end.join("\n")
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
# Handle storage errors
|
135
|
+
# SMELL: Just raise them or get out of their way and let the
|
136
|
+
# main program do tje job.
|
137
|
+
def handle_storage_error(error)
|
138
|
+
# Log the error message, notify, or take appropriate action
|
139
|
+
log_error("Storage operation failed: #{error.message}")
|
140
|
+
# Re-raise the error if necessary, or define recovery steps
|
141
|
+
raise error
|
142
|
+
end
|
143
|
+
|
144
|
+
|
145
|
+
# SMELL: should this gem log errors or is that a function of
|
146
|
+
# main program? I believe its the main program's job.
|
147
|
+
def log_error(message)
|
148
|
+
puts "ERROR: #{message}"
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
@@ -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)
|
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
|
+
|
data/lib/prompt_manager.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
|
+
# prompt_manager/lib/prompt_manager.rb
|
2
|
+
#
|
1
3
|
# frozen_string_literal: true
|
2
4
|
|
3
5
|
require_relative "prompt_manager/version"
|
6
|
+
require_relative "prompt_manager/storage"
|
7
|
+
require_relative "prompt_manager/prompt"
|
4
8
|
|
5
9
|
module PromptManager
|
6
10
|
class Error < StandardError; end
|
7
|
-
#
|
11
|
+
# TODO: Add some more module specific errors here
|
8
12
|
end
|
metadata
CHANGED
@@ -1,16 +1,60 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: prompt_manager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.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-
|
12
|
-
dependencies:
|
13
|
-
|
11
|
+
date: 2023-11-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: amazing_print
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: fakefs
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: "Manage the parameterized prompts (text) used in generative AI (aka chatGPT,
|
56
|
+
\npen AI, et.al.) using storage adapters such as FileSystemAdapter, \nSqliteAdapter
|
57
|
+
and ActiveRecordAdapter.\n"
|
14
58
|
email:
|
15
59
|
- dvanhoozer@gmail.com
|
16
60
|
executables: []
|
@@ -23,10 +67,16 @@ files:
|
|
23
67
|
- LICENSE.txt
|
24
68
|
- README.md
|
25
69
|
- Rakefile
|
26
|
-
-
|
70
|
+
- examples/prompts_dir/todo.json
|
71
|
+
- examples/prompts_dir/todo.txt
|
72
|
+
- examples/simple.rb
|
27
73
|
- lib/prompt_manager.rb
|
74
|
+
- lib/prompt_manager/prompt.rb
|
75
|
+
- lib/prompt_manager/storage.rb
|
76
|
+
- lib/prompt_manager/storage/active_record_adapter.rb
|
77
|
+
- lib/prompt_manager/storage/file_system_adapter.rb
|
78
|
+
- lib/prompt_manager/storage/sqlite_adapter.rb
|
28
79
|
- lib/prompt_manager/version.rb
|
29
|
-
- lib/temp.md
|
30
80
|
homepage: https://github.com/MadBomber/prompt_manager
|
31
81
|
licenses:
|
32
82
|
- MIT
|
@@ -50,8 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
100
|
- !ruby/object:Gem::Version
|
51
101
|
version: '0'
|
52
102
|
requirements: []
|
53
|
-
rubygems_version: 3.
|
103
|
+
rubygems_version: 3.4.22
|
54
104
|
signing_key:
|
55
105
|
specification_version: 4
|
56
|
-
summary: Manage prompts for use with
|
106
|
+
summary: Manage prompts for use with gen-AI processes
|
57
107
|
test_files: []
|
data/lib/aip.rb
DELETED
@@ -1,402 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# encoding: utf-8
|
3
|
-
# frozen_string_literal: true
|
4
|
-
# warn_indent: true
|
5
|
-
##########################################################
|
6
|
-
###
|
7
|
-
## File: aip.rb
|
8
|
-
## Desc: Use generative AI with saved parameterized prompts
|
9
|
-
## By: Dewayne VanHoozer (dvanhoozer@gmail.com)
|
10
|
-
##
|
11
|
-
## This program makes use of the gem word_wrap's
|
12
|
-
## CLI tool: ww
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
=begin
|
17
|
-
|
18
|
-
brew install fzf mods the_silver_searcher
|
19
|
-
|
20
|
-
fzf Command-line fuzzy finder written in Go
|
21
|
-
|__ https://github.com/junegunn/fzf
|
22
|
-
|
23
|
-
mods AI on the command-line
|
24
|
-
|__ https://github.com/charmbracelet/mods
|
25
|
-
|
26
|
-
the_silver_searcher Code-search similar to ack
|
27
|
-
|__ https://github.com/ggreer/the_silver_searcher
|
28
|
-
|
29
|
-
Program Summary
|
30
|
-
|
31
|
-
The program is a Ruby script that integrates with the `mods` CLI tool, which is built on a GPT-based generative AI model. This script is designed to make use of generative AI through a set of saved, parameterized prompts. Users can easily interact with the following features:
|
32
|
-
|
33
|
-
- **Prompt Selection**: Users have the ability to choose a prompt from a curated list. This selection process is streamlined by allowing users to search and filter prompts using keywords.
|
34
|
-
|
35
|
-
- **Prompt Editing**: There is functionality for a user to modify the text of an existing prompt, tailoring it to better meet their specific needs.
|
36
|
-
|
37
|
-
- **File Input**: The script can read in data from input files, providing the necessary context or information required for the AI to generate relevant content.
|
38
|
-
|
39
|
-
- **AI Integration**: Utilizing the `mods` GPT-based CLI tool, the script takes the chosen edited prompt to guide the AI in generating its output.
|
40
|
-
|
41
|
-
- **Output Management**: After the generative process, the resulting output is saved to a designated file, ensuring that the user has a record of the AI's creations.
|
42
|
-
|
43
|
-
- **Logging**: For tracking and accountability, the program records the details of each session, including the prompt used, the AI-generated output, and the precise timestamp when the generation occurred.
|
44
|
-
|
45
|
-
This robust tool is excellent for users who wish to harness the power of generative AI for creating content, with an efficient and user-friendly system for managing the creation process.
|
46
|
-
|
47
|
-
=end
|
48
|
-
|
49
|
-
#
|
50
|
-
# TODO: I think this script has reached the point where
|
51
|
-
# it is ready to become a proper gem.
|
52
|
-
#
|
53
|
-
|
54
|
-
require 'pathname'
|
55
|
-
HOME = Pathname.new( ENV['HOME'] )
|
56
|
-
|
57
|
-
|
58
|
-
MODS_MODEL = ENV['MODS_MODEL'] || 'gpt-4-1106-preview'
|
59
|
-
|
60
|
-
AI_CLI_PROGRAM = "mods"
|
61
|
-
ai_default_opts = "-m #{MODS_MODEL} --no-limit -f"
|
62
|
-
ai_options = ai_default_opts.dup
|
63
|
-
|
64
|
-
extra_inx = ARGV.index('--')
|
65
|
-
|
66
|
-
if extra_inx
|
67
|
-
ai_options += " " + ARGV[extra_inx+1..].join(' ')
|
68
|
-
ARGV.pop(ARGV.size - extra_inx)
|
69
|
-
end
|
70
|
-
|
71
|
-
AI_COMMAND = "#{AI_CLI_PROGRAM} #{ai_options} "
|
72
|
-
EDITOR = ENV['EDITOR']
|
73
|
-
PROMPT_DIR = HOME + ".prompts"
|
74
|
-
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
|
-
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
|
-
|
89
|
-
require 'amazing_print'
|
90
|
-
require 'json'
|
91
|
-
require 'readline' # TODO: or reline ??
|
92
|
-
require 'word_wrap'
|
93
|
-
require 'word_wrap/core_ext'
|
94
|
-
|
95
|
-
|
96
|
-
require 'debug_me'
|
97
|
-
include DebugMe
|
98
|
-
|
99
|
-
require 'cli_helper'
|
100
|
-
include CliHelper
|
101
|
-
|
102
|
-
configatron.version = '1.1.0'
|
103
|
-
|
104
|
-
AI_CLI_PROGRAM_HELP = `#{AI_CLI_PROGRAM} --help`
|
105
|
-
|
106
|
-
HELP = <<EOHELP
|
107
|
-
AI CLI Program
|
108
|
-
==============
|
109
|
-
|
110
|
-
The AI cli program being used is: #{AI_CLI_PROGRAM}
|
111
|
-
|
112
|
-
The defaul options to #{AI_CLI_PROGRAM} are:
|
113
|
-
"#{ai_default_opts}"
|
114
|
-
|
115
|
-
You can pass additional CLI options to #{AI_CLI_PROGRAM} like this:
|
116
|
-
"#{my_name} my options -- options for #{AI_CLI_PROGRAM}"
|
117
|
-
|
118
|
-
#{AI_CLI_PROGRAM_HELP}
|
119
|
-
|
120
|
-
EOHELP
|
121
|
-
|
122
|
-
cli_helper("Use generative AI with saved parameterized prompts") do |o|
|
123
|
-
|
124
|
-
o.string '-p', '--prompt', 'The prompt name', default: ""
|
125
|
-
o.bool '-e', '--edit', 'Edit the prompt text', default: false
|
126
|
-
o.bool '-f', '--fuzzy', 'Allow fuzzy matching', default: false
|
127
|
-
o.path '-o', '--output', 'The output file', default: Pathname.pwd + "temp.md"
|
128
|
-
end
|
129
|
-
|
130
|
-
|
131
|
-
AG_COMMAND = "ag --file-search-regex '\.txt$' e"
|
132
|
-
CD_COMMAND = "cd #{PROMPT_DIR}"
|
133
|
-
FIND_COMMAND = "find . -name '*.txt'"
|
134
|
-
|
135
|
-
FZF_OPTIONS = [
|
136
|
-
"--tabstop=2", # 2 soaces for a tab
|
137
|
-
"--header='Prompt contents below'",
|
138
|
-
"--header-first",
|
139
|
-
"--prompt='Search term: '",
|
140
|
-
'--delimiter :',
|
141
|
-
"--preview 'ww {1}'", # ww comes from the word_wrap gem
|
142
|
-
"--preview-window=down:50%:wrap"
|
143
|
-
].join(' ')
|
144
|
-
|
145
|
-
FZF_OPTIONS += "--exact" unless fuzzy?
|
146
|
-
|
147
|
-
FZF_COMMAND = "#{CD_COMMAND} ; #{FIND_COMMAND} | fzf #{FZF_OPTIONS}"
|
148
|
-
AG_FZF_COMMAND = "#{CD_COMMAND} ; #{AG_COMMAND} | fzf #{FZF_OPTIONS}"
|
149
|
-
|
150
|
-
# use `ag` ti build a list of text lines from each prompt
|
151
|
-
# use `fzf` to search through that list to select a prompt file
|
152
|
-
|
153
|
-
def ag_fzf = `#{AG_FZF_COMMAND}`.split(':')&.first&.strip&.gsub('.txt','')
|
154
|
-
|
155
|
-
|
156
|
-
configatron.input_files = get_pathnames_from( configatron.arguments, %w[.rb .txt .md])
|
157
|
-
|
158
|
-
|
159
|
-
# TODO: Make the use of the "-p" flag optional.
|
160
|
-
# I find myself many times forgetting to use it
|
161
|
-
# and this program rejecting it because
|
162
|
-
# "the file does not exist" thinging that it
|
163
|
-
# was an input file file rather than a prompt
|
164
|
-
# name.
|
165
|
-
|
166
|
-
if configatron.prompt.empty?
|
167
|
-
configatron.prompt = ag_fzf
|
168
|
-
end
|
169
|
-
|
170
|
-
unless edit?
|
171
|
-
if configatron.prompt.nil? || configatron.prompt.empty?
|
172
|
-
error "No prompt provided"
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
abort_if_errors
|
177
|
-
|
178
|
-
configatron.prompt_path = PROMPT_DIR + (configatron.prompt + PROMPT_EXTNAME)
|
179
|
-
configatron.defaults_path = PROMPT_DIR + (configatron.prompt + DEFAULTS_EXTNAME)
|
180
|
-
|
181
|
-
if !configatron.prompt_path.exist? && !edit?
|
182
|
-
error "This prompt does not exist: #{configatron.prompt}\n"
|
183
|
-
end
|
184
|
-
|
185
|
-
configatron.prompt_path = PROMPT_DIR + (configatron.prompt + PROMPT_EXTNAME)
|
186
|
-
configatron.defaults_path = PROMPT_DIR + (configatron.prompt + DEFAULTS_EXTNAME)
|
187
|
-
|
188
|
-
abort_if_errors
|
189
|
-
|
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:
|
195
|
-
|
196
|
-
PROMPT
|
197
|
-
end
|
198
|
-
|
199
|
-
`#{EDITOR} #{configatron.prompt_path}`
|
200
|
-
end
|
201
|
-
|
202
|
-
######################################################
|
203
|
-
# Local methods
|
204
|
-
|
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
|
-
|
222
|
-
unless x.nil?
|
223
|
-
array_of_strings = array_of_strings[..x-1]
|
224
|
-
end
|
225
|
-
|
226
|
-
array_of_strings
|
227
|
-
end
|
228
|
-
|
229
|
-
|
230
|
-
def print_header_comment(array_of_strings)
|
231
|
-
print "\n\n" if verbose?
|
232
|
-
|
233
|
-
x = 0
|
234
|
-
|
235
|
-
while array_of_strings[x].start_with?('#') do
|
236
|
-
puts array_of_strings[x]
|
237
|
-
x += 1
|
238
|
-
end
|
239
|
-
|
240
|
-
print "\n\n" if x>0 && verbose?
|
241
|
-
end
|
242
|
-
|
243
|
-
|
244
|
-
# Returns an Array of keywords or phrases that look like:
|
245
|
-
# [KEYWORD]
|
246
|
-
# [KEYWORD|KEYWORD]
|
247
|
-
# [KEY PHRASE]
|
248
|
-
# [KEY PHRASE | KEY PHRASE | KEY_WORD]
|
249
|
-
#
|
250
|
-
def extract_keywords_from(prompt_raw)
|
251
|
-
prompt_raw.scan(KEYWORD_REGEX).flatten.uniq
|
252
|
-
end
|
253
|
-
|
254
|
-
# get the replacements for the keywords
|
255
|
-
def replacements_for(keywords)
|
256
|
-
replacements = load_default_replacements
|
257
|
-
|
258
|
-
keywords.each do |kw|
|
259
|
-
default = replacements[kw]
|
260
|
-
print "#{kw} (#{default}) ? "
|
261
|
-
a_string = Readline.readline("\n> ", false)
|
262
|
-
replacements[kw] = a_string unless a_string.empty?
|
263
|
-
end
|
264
|
-
|
265
|
-
save_default_replacements(replacements)
|
266
|
-
|
267
|
-
replacements
|
268
|
-
end
|
269
|
-
|
270
|
-
|
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
|
-
|
279
|
-
|
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
|
-
|
286
|
-
# substitute the replacements for the keywords
|
287
|
-
def replace_keywords_with replacements, prompt_raw
|
288
|
-
prompt = prompt_raw.dup
|
289
|
-
|
290
|
-
replacements.each_pair do |keyword, replacement|
|
291
|
-
prompt.gsub!(keyword, replacement)
|
292
|
-
end
|
293
|
-
|
294
|
-
prompt
|
295
|
-
end
|
296
|
-
|
297
|
-
|
298
|
-
def log(prompt_path, prompt_raw, answer)
|
299
|
-
f = File.open(PROMPT_LOG, "ab")
|
300
|
-
|
301
|
-
f.write <<~EOS
|
302
|
-
=======================================
|
303
|
-
== #{Time.now}
|
304
|
-
== #{prompt_path}
|
305
|
-
|
306
|
-
PROMPT: #{prompt_raw}
|
307
|
-
|
308
|
-
RESULT:
|
309
|
-
#{answer}
|
310
|
-
|
311
|
-
EOS
|
312
|
-
end
|
313
|
-
|
314
|
-
|
315
|
-
######################################################
|
316
|
-
# Main
|
317
|
-
|
318
|
-
at_exit do
|
319
|
-
puts
|
320
|
-
puts "Done."
|
321
|
-
puts
|
322
|
-
end
|
323
|
-
|
324
|
-
ap configatron.to_h if debug?
|
325
|
-
|
326
|
-
configatron.prompt_raw = extract_raw_prompt
|
327
|
-
|
328
|
-
puts
|
329
|
-
puts "PROMPT:"
|
330
|
-
puts configatron.prompt_raw.wrap
|
331
|
-
puts
|
332
|
-
|
333
|
-
|
334
|
-
keywords = extract_keywords_from configatron.prompt_raw
|
335
|
-
replacements = replacements_for keywords
|
336
|
-
|
337
|
-
prompt = replace_keywords_with replacements, configatron.prompt_raw
|
338
|
-
ptompt = %Q{prompt}
|
339
|
-
|
340
|
-
command = AI_COMMAND + '"' + prompt + '"'
|
341
|
-
|
342
|
-
configatron.input_files.each do |input_file|
|
343
|
-
command += " < #{input_file}"
|
344
|
-
end
|
345
|
-
|
346
|
-
|
347
|
-
print "\n\n" if verbose? && !keywords.empty?
|
348
|
-
|
349
|
-
if verbose?
|
350
|
-
puts "="*42
|
351
|
-
puts command
|
352
|
-
puts "="*42
|
353
|
-
print "\n\n"
|
354
|
-
end
|
355
|
-
|
356
|
-
result = `#{command}`
|
357
|
-
|
358
|
-
configatron.output.write result
|
359
|
-
|
360
|
-
log configatron.prompt_path, prompt, result
|
361
|
-
|
362
|
-
|
363
|
-
__END__
|
364
|
-
|
365
|
-
To specify a history and autocomplete options with the readline method in Ruby using the readline gem, you can follow these steps:
|
366
|
-
|
367
|
-
1. **History** - To enable history functionality, create a Readline::HISTORY object:
|
368
|
-
```ruby
|
369
|
-
history = Readline::HISTORY
|
370
|
-
```
|
371
|
-
You can then use the `history` object to add and manipulate history entries.
|
372
|
-
|
373
|
-
2. **Autocomplete** - To enable autocomplete functionality, you need to provide a completion proc to `Readline.completion_proc`:
|
374
|
-
```ruby
|
375
|
-
Readline.completion_proc = proc { |input| ... }
|
376
|
-
```
|
377
|
-
You should replace `...` with the logic for determining the autocomplete options based on the input.
|
378
|
-
|
379
|
-
For example, you can define a method that provides autocomplete options based on a predefined array:
|
380
|
-
```ruby
|
381
|
-
def autocomplete_options(input)
|
382
|
-
available_options = ['apple', 'banana', 'cherry']
|
383
|
-
available_options.grep(/^#{Regexp.escape(input)}/)
|
384
|
-
end
|
385
|
-
|
386
|
-
Readline.completion_proc = proc { |input| autocomplete_options(input) }
|
387
|
-
```
|
388
|
-
|
389
|
-
In this example, the `autocomplete_options` method takes the user's input and uses the `grep` method to filter the available options based on the input prefix.
|
390
|
-
|
391
|
-
Remember to require the readline gem before using these features:
|
392
|
-
```ruby
|
393
|
-
require 'readline'
|
394
|
-
```
|
395
|
-
|
396
|
-
With the above steps in place, you can use the readline method in your code, and the specified history and autocomplete options will be available.
|
397
|
-
|
398
|
-
Note: Keep in mind that autocomplete options will only appear when tab is pressed while entering input.
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
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
|
-
|