forked from theubie/complex_memory
-
Notifications
You must be signed in to change notification settings - Fork 0
/
script.py
302 lines (228 loc) · 10.6 KB
/
script.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
import gradio as gr
import os
import yaml
from modules import shared
# import modules.shared as shared
import modules.chat as chat
import pickle
from modules.extensions import apply_extensions
from modules.text_generation import encode, get_max_prompt_length
from modules.chat import generate_chat_prompt
# Initialize the current character
character = shared.settings["character"]
# Initialize the list of keyword/memory pairs with a default pair
pairs = [{"keywords": "new keyword(s)", "memory": "new memory", "always": False},
{"keywords": "debug", "memory": "This is debug data.", "always": False}]
memory_settings = {"position": "Before Context"}
# our select
memory_select = None
def custom_generate_chat_prompt(user_input, state, **kwargs):
global pairs
global memory_settings
# create out memory rows
context_injection = []
for pair in pairs:
if pair["always"]:
# Always inject it.
context_injection.append(pair["memory"])
else:
# Check to see if keywords are present.
keywords = pair["keywords"].lower().split(",")
user_input_lower = user_input.lower()
for keyword in keywords:
if keyword.strip() in user_input_lower:
# keyword is present in user_input
context_injection.append(pair["memory"])
break # exit the loop if a match is found
# Add the context_injection
context_injection_string = ('\n'.join(context_injection)).strip()
if memory_settings["position"] == "Before Context":
state["context"] = f"{context_injection_string}\n{state['context']}\n"
elif memory_settings["position"] == "After Context":
state["context"] = f"{state['context']}\n{context_injection_string}\n"
return generate_chat_prompt(user_input, state, **kwargs)
def save_pairs():
global pairs, character
if character is not None and character != "None":
filename = f"characters/{character}.yaml"
else:
filename = "extensions/complex_memory/saved_memories.yaml"
# read the current character file
if os.path.exists(filename):
with open(filename, 'r') as f:
# Load the YAML data from the file into a Python dictionary
data = yaml.load(f, Loader=yaml.Loader)
else:
data = {}
# update the character file to include or update the memory
data["memory"] = pairs
# write the character file again
with open(filename, 'w') as f:
yaml.dump(data, f, indent=2)
# with open(f"extensions/complex_memory/{filename}", 'wb') as f:
# pickle.dump(pairs, f)
def load_pairs():
global pairs, character
filename = ""
# check to see if old pickle file exists, and if so, load that.
if character is not None and character != "None":
filename = f"{character}_saved_memories.pkl"
if os.path.exists(f"extensions/complex_memory/{filename}"):
print(f"Found old pickle file. Loading old pickle file {filename}")
with open(f"extensions/complex_memory/{filename}", 'rb') as f:
print("Getting memory.")
pairs = pickle.load(f)
print(f"pairs: {pairs}")
print(f"Removing old pickle file {filename}")
os.remove(f"extensions/complex_memory/{filename}")
print("Saving data into character file.")
save_pairs()
print("Conversion complete.")
return # we are done here.
# load the character file and get the memory from it, if it exists.
try:
if character is not None and character != "None":
filename = f"characters/{character}.yaml"
else:
filename = "extensions/complex_memory/saved_memories.yaml"
# read the current character file
with open(filename, 'r') as f:
# Load the YAML data from the file into a Python dictionary
data = yaml.load(f, Loader=yaml.Loader)
if "memory" in data:
pairs = data["memory"]
else:
print(f"Unable to find memories in {filename}. Using default.")
pairs = [{"keywords": "new keyword(s)", "memory": "new memory", "always": False}]
except FileNotFoundError:
print(
f"--Unable to load complex memories for character {character}. filename: {filename}. Using defaults.")
pairs = [{"keywords": "new keyword(s)", "memory": "new memory", "always": False}]
# Make sure old loaded data is updated
for pair in pairs:
if "always" not in pair:
pair["always"] = False
def save_settings():
global memory_settings
filename = "extensions/complex_memory/settings.yaml"
with open(filename, 'w') as f:
yaml.dump(memory_settings, f, indent=2)
def load_settings():
global memory_settings
filename = "extensions/complex_memory/settings.yaml"
try:
with open(filename, 'r') as f:
# Load the YAML data from the file into a Python dictionary
data = yaml.load(f, Loader=yaml.Loader)
if data:
memory_settings = data
except FileNotFoundError:
memory_settings = {"position": "Before Context"}
return memory_settings["position"]
def load_character_complex_memory_hijack(character_menu):
global character
# load the character like normal
# result = chat.load_character(character_menu, name1, name2)
character = character_menu
# Our code
load_pairs()
# return the result of normal load character
# return result
def pairs_loaded():
global pairs
select = gr.Dropdown.update(choices=[pair["keywords"] for pair in pairs], value=pairs[-1]['keywords'])
return select
def setup():
load_settings()
def ui():
global pairs
global memory_select
# And we need to load any saved memories for the default character
load_pairs()
# Function to update the list of pairs
def update_pairs(keywords, memory, always, memory_select):
for pair in pairs:
if pair["keywords"] == memory_select:
pair["keywords"] = keywords
pair["memory"] = memory
pair["always"] = always
break
# save the changes
save_pairs()
select = gr.Dropdown.update(choices=[pair["keywords"] for pair in pairs], value=keywords)
return select
# Function to update the UI based on the currently selected pair
def update_ui(keyword_value):
for pair in pairs:
if pair["keywords"] == keyword_value:
keywords = gr.Textbox.update(value=pair["keywords"])
memory = gr.Textbox.update(value=pair["memory"])
always = gr.Checkbox.update(value=pair["always"])
return [keywords, memory, always]
# Didn't find it, so return nothing and update nothing.
return
with gr.Accordion("", open=True):
t_m = gr.Tab("Memory", elem_id="complex_memory_tab_memory")
with t_m:
# Dropdown menu to select the current pair
memory_select = gr.Dropdown(choices=[pair["keywords"] for pair in pairs], label="Select Memory",
elem_id="complext_memory_memory_select", multiselect=False)
# Textbox to edit the keywords for the current pair
keywords = gr.Textbox(lines=1, max_lines=3, label="Keywords", placeholder="Keyword, Keyword, Keyword, ...")
# Textbox to edit the memory for the current pair
memory = gr.Textbox(lines=3, max_lines=7, label="Memory")
# Checkbox to select if memory is always on
always = gr.Checkbox(label="Always active")
# make the call back for the memory_select now that the text boxes exist.
memory_select.change(update_ui, memory_select, [keywords, memory, always])
keywords.submit(update_pairs, [keywords, memory, always, memory_select], memory_select)
keywords.blur(update_pairs, [keywords, memory, always, memory_select], memory_select)
memory.change(update_pairs, [keywords, memory, always, memory_select], None)
always.change(update_pairs, [keywords, memory, always, memory_select], None)
# Button to add a new pair
add_button = gr.Button("add")
add_button.click(add_pair, None, memory_select)
# Button to remove the current pair
remove_button = gr.Button("remove")
remove_button.click(remove_pair, memory_select, memory_select).then(update_ui, memory_select,
[keywords, memory])
t_s = gr.Tab("Settings", elem_id="complex_memory_tab_settings")
with t_s:
position = gr.Radio(["Before Context", "After Context"],
value=memory_settings["position"],
label="Memory Position in Prompt")
position.change(update_settings, position, None)
# We need to hijack load_character in order to load our memories based on characters.
if 'character_menu' in shared.gradio:
shared.gradio['character_menu'].change(
load_character_complex_memory_hijack,
[shared.gradio['character_menu']],
None).then(
chat.redraw_html, shared.reload_inputs, shared.gradio['display']).then(pairs_loaded, None, memory_select)
# Return the UI elements wrapped in a Gradio column
# return c
def update_settings(position):
global memory_settings
memory_settings["position"] = position
save_settings()
def add_pair():
global pairs
found = False
for pair in pairs:
if pair["keywords"] == "new keyword(s)":
found = True
break
if not found:
pairs.append({"keywords": "new keyword(s)", "memory": "new memory", "always": False})
select = gr.Dropdown.update(choices=[pair["keywords"] for pair in pairs], value=pairs[-1]['keywords'])
return select
def remove_pair(keyword):
global pairs
for pair in pairs:
if pair['keywords'] == keyword:
pairs.remove(pair)
break
if not pairs:
pairs = [{"keywords": "new keyword(s)", "memory": "new memory", "always": False}]
select = gr.Dropdown.update(choices=[pair["keywords"] for pair in pairs], value=pairs[-1]['keywords'])
return select