ndurner commited on
Commit
8c1fe21
1 Parent(s): a4de7ef

file-based settings storage

Browse files
Files changed (2) hide show
  1. app.py +14 -1
  2. settings_mgr.py +85 -0
app.py CHANGED
@@ -5,6 +5,7 @@ from anthropic import Anthropic
5
  import json
6
 
7
  from doc2json import process_docx
 
8
 
9
  dump_controls = False
10
  log_to_console = False
@@ -187,12 +188,14 @@ with gr.Blocks() as demo:
187
  with gr.Accordion("Settings"):
188
  api_key = gr.Textbox(label="Anthropic API Key", elem_id="api_key")
189
  model = gr.Dropdown(label="Model", value="claude-3-opus-20240229", allow_custom_value=True, elem_id="model",
190
- choices=["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307"])
191
  system_prompt = gr.TextArea("You are a helpful AI.", label="System Prompt", lines=3, max_lines=250, elem_id="system_prompt")
192
  temp = gr.Slider(0, 1, label="Temperature", elem_id="temp", value=1)
193
  max_tokens = gr.Slider(1, 4000, label="Max. Tokens", elem_id="max_tokens", value=800)
194
  save_button = gr.Button("Save Settings")
195
  load_button = gr.Button("Load Settings")
 
 
196
 
197
  load_button.click(load_settings, js="""
198
  () => {
@@ -216,6 +219,16 @@ with gr.Blocks() as demo:
216
  }
217
  """)
218
 
 
 
 
 
 
 
 
 
 
 
219
  chatbot = gr.Chatbot(
220
  [],
221
  elem_id="chatbot",
 
5
  import json
6
 
7
  from doc2json import process_docx
8
+ from settings_mgr import generate_download_settings_js, generate_upload_settings_js
9
 
10
  dump_controls = False
11
  log_to_console = False
 
188
  with gr.Accordion("Settings"):
189
  api_key = gr.Textbox(label="Anthropic API Key", elem_id="api_key")
190
  model = gr.Dropdown(label="Model", value="claude-3-opus-20240229", allow_custom_value=True, elem_id="model",
191
+ choices=["claude-3-opus-20240229", "claude-3-sonnet-20240229", "claude-3-haiku-20240307", "claude-2.1", "claude-2.0", "claude-instant-1.2"])
192
  system_prompt = gr.TextArea("You are a helpful AI.", label="System Prompt", lines=3, max_lines=250, elem_id="system_prompt")
193
  temp = gr.Slider(0, 1, label="Temperature", elem_id="temp", value=1)
194
  max_tokens = gr.Slider(1, 4000, label="Max. Tokens", elem_id="max_tokens", value=800)
195
  save_button = gr.Button("Save Settings")
196
  load_button = gr.Button("Load Settings")
197
+ dl_settings_button = gr.Button("Download Settings")
198
+ ul_settings_button = gr.Button("Upload Settings")
199
 
200
  load_button.click(load_settings, js="""
201
  () => {
 
219
  }
220
  """)
221
 
222
+ control_ids = [('api_key', '#api_key textarea'),
223
+ ('system_prompt', '#system_prompt textarea'),
224
+ ('temp', '#temp input'),
225
+ ('max_tokens', '#max_tokens input'),
226
+ ('model', '#model')]
227
+ controls = [api_key, system_prompt, temp, max_tokens, model]
228
+
229
+ dl_settings_button.click(None, controls, js=generate_download_settings_js("claude_chat_settings.bin", control_ids))
230
+ ul_settings_button.click(None, None, None, js=generate_upload_settings_js(control_ids))
231
+
232
  chatbot = gr.Chatbot(
233
  [],
234
  elem_id="chatbot",
settings_mgr.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ def generate_download_settings_js(dl_fn, control_ids):
2
+ js_code = """
3
+ async (""" + ", ".join([f'{ctrl[0]}' for ctrl in control_ids]) + """) => {
4
+ const password = prompt("Please enter a password for encryption", " ");
5
+ if (!password) {
6
+ alert("No password provided. Cancelling download.");
7
+ return;
8
+ }
9
+
10
+ let settings = {""" + ", ".join([f'"{ctrl[0]}": {ctrl[0]}' for ctrl in control_ids]) + """};
11
+ const settingsStr = JSON.stringify(settings);
12
+ const textEncoder = new TextEncoder();
13
+ const encodedSettings = textEncoder.encode(settingsStr);
14
+ const salt = crypto.getRandomValues(new Uint8Array(16));
15
+ const passwordBuffer = textEncoder.encode(password);
16
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBuffer, {name: 'PBKDF2'}, false, ['deriveKey']);
17
+ const key = await crypto.subtle.deriveKey(
18
+ {name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256'},
19
+ keyMaterial,
20
+ {name: 'AES-GCM', length: 256},
21
+ false,
22
+ ['encrypt']
23
+ );
24
+ const iv = crypto.getRandomValues(new Uint8Array(12));
25
+ const encryptedSettings = await crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, encodedSettings);
26
+ const blob = new Blob([salt, iv, new Uint8Array(encryptedSettings)], {type: 'application/octet-stream'});
27
+ const url = URL.createObjectURL(blob);
28
+ const a = document.createElement('a');
29
+ a.href = url;
30
+ a.download = '""" + dl_fn + """';
31
+ document.body.appendChild(a);
32
+ a.click();
33
+ document.body.removeChild(a);
34
+ URL.revokeObjectURL(url);
35
+ }"""
36
+ return js_code
37
+
38
+ def generate_upload_settings_js(control_ids):
39
+ js_code = """
40
+ async () => {
41
+ const input = document.createElement('input');
42
+ input.type = 'file';
43
+ input.onchange = async e => {
44
+ const file = e.target.files[0];
45
+ if (!file) {
46
+ alert("No file selected.");
47
+ return;
48
+ }
49
+
50
+ const password = prompt("Please enter the password for decryption", " ");
51
+ if (!password) {
52
+ alert("No password provided. Cancelling upload.");
53
+ return;
54
+ }
55
+
56
+ const arrayBuffer = await file.arrayBuffer();
57
+ const salt = arrayBuffer.slice(0, 16);
58
+ const iv = arrayBuffer.slice(16, 28);
59
+ const encryptedData = arrayBuffer.slice(28);
60
+ const textEncoder = new TextEncoder();
61
+ const passwordBuffer = textEncoder.encode(password);
62
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBuffer, {name: 'PBKDF2'}, false, ['deriveKey']);
63
+ const key = await crypto.subtle.deriveKey(
64
+ {name: 'PBKDF2', salt: salt, iterations: 100000, hash: 'SHA-256'},
65
+ keyMaterial,
66
+ {name: 'AES-GCM', length: 256},
67
+ false,
68
+ ['decrypt']
69
+ );
70
+
71
+ try {
72
+ const decryptedData = await crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, encryptedData);
73
+ const textDecoder = new TextDecoder();
74
+ const settingsStr = textDecoder.decode(decryptedData);
75
+ const settings = JSON.parse(settingsStr);
76
+ """ + "\n".join([f'document.querySelector("{ctrl[1]}").value = settings["{ctrl[0]}"];' for ctrl in control_ids]) + """
77
+ """ + "\n".join([f'document.querySelector("{ctrl[1]}").dispatchEvent(new InputEvent("input", {{ bubbles: true }}));' for ctrl in control_ids]) + """
78
+ } catch (err) {
79
+ alert("Failed to decrypt. Check your password and try again.");
80
+ console.error("Decryption failed:", err);
81
+ }
82
+ };
83
+ input.click();
84
+ }"""
85
+ return js_code