ndurner commited on
Commit
575c087
·
1 Parent(s): 7a53e3e

Mistral adaptation

Browse files
Files changed (4) hide show
  1. .gitignore +1 -0
  2. README.md +5 -5
  3. app.py +86 -170
  4. requirements.txt +1 -1
.gitignore CHANGED
@@ -1,2 +1,3 @@
1
  __pycache__/*
2
  .DS_Store
 
 
1
  __pycache__/*
2
  .DS_Store
3
+ .venv
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: OAI Chat
3
  emoji: 🤖
4
  colorFrom: yellow
5
  colorTo: gray
@@ -10,16 +10,16 @@ pinned: false
10
  license: mit
11
  ---
12
 
13
- # OAI Chat
14
 
15
  Chat interface based on OpenAI transformer models. \
16
  Features:
17
- * Image upload (support for vision via gpt-4-vision)
18
  * Word file (DOCX) upload
19
- * PDF file support (via image rendering & GPT-4V)
20
  * Plaintext file upload
21
  * chat history download
22
  * file download
23
  * example: download an ICS calendar file the model has created for you
24
  * streaming chat
25
- * image generation (via DALL-E 3)
 
1
  ---
2
+ title: Mistral Chat
3
  emoji: 🤖
4
  colorFrom: yellow
5
  colorTo: gray
 
10
  license: mit
11
  ---
12
 
13
+ # Mistral Chat
14
 
15
  Chat interface based on OpenAI transformer models. \
16
  Features:
17
+ * Image upload (support for vision via Pixtral)
18
  * Word file (DOCX) upload
19
+ * PDF file support (via image rendering & Pixtral)
20
  * Plaintext file upload
21
  * chat history download
22
  * file download
23
  * example: download an ICS calendar file the model has created for you
24
  * streaming chat
25
+
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import gradio as gr
2
  import base64
3
- import os
4
- from openai import OpenAI
5
  import json
6
  import fitz
7
  from PIL import Image
@@ -80,10 +80,7 @@ def process_pdf_img(pdf_fn: str):
80
  })
81
  message_parts.append({
82
  "type": "image_url",
83
- "image_url": {
84
- "url": image_url,
85
- "detail": "high"
86
- }
87
  })
88
 
89
  pdf.close()
@@ -114,158 +111,62 @@ def encode_file(fn: str) -> list:
114
  content = str(content)
115
 
116
  if isImage:
117
- user_msg_parts.append({"type": "image_url",
118
- "image_url":{"url": content}})
119
  else:
120
  user_msg_parts.append({"type": "text", "text": content})
121
 
122
  return user_msg_parts
123
 
124
- def undo(history):
125
- history.pop()
126
- return history
 
 
127
 
128
- def dump(history):
129
- return str(history)
130
 
131
- def load_settings():
132
- # Dummy Python function, actual loading is done in JS
133
- pass
134
 
135
- def save_settings(acc, sec, prompt, temp, tokens, model):
136
- # Dummy Python function, actual saving is done in JS
137
- pass
 
 
 
138
 
139
- def process_values_js():
140
- return """
141
- () => {
142
- return ["oai_key", "system_prompt", "seed"];
143
- }
144
- """
145
 
146
- def bot(message, history, oai_key, system_prompt, seed, temperature, max_tokens, model):
147
- try:
148
- client = OpenAI(
149
- api_key=oai_key
150
- )
151
 
152
- if model == "whisper":
153
- result = ""
154
- whisper_prompt = system_prompt
155
- for human, assi in history:
156
- if human is not None:
157
- if type(human) is tuple:
158
- pass
159
- else:
160
- whisper_prompt += f"\n{human}"
161
- if assi is not None:
162
- whisper_prompt += f"\n{assi}"
163
-
164
- if message["text"]:
165
- whisper_prompt += message["text"]
166
- if message.files:
167
- for file in message.files:
168
- audio_fn = os.path.basename(file.path)
169
- with open(file.path, "rb") as f:
170
- transcription = client.audio.transcriptions.create(
171
- model="whisper-1",
172
- prompt=whisper_prompt,
173
- file=f,
174
- response_format="text"
175
- )
176
- whisper_prompt += f"\n{transcription}"
177
- result += f"\n``` transcript {audio_fn}\n {transcription}\n```"
178
-
179
- yield result
180
-
181
- elif model == "dall-e-3":
182
- response = client.images.generate(
183
- model=model,
184
- prompt=message["text"],
185
- size="1792x1024",
186
- quality="hd",
187
- n=1,
188
- )
189
- yield gr.Image(response.data[0].url)
190
- else:
191
- seed_i = None
192
- if seed:
193
- seed_i = int(seed)
194
 
195
- if log_to_console:
196
- print(f"bot history: {str(history)}")
197
 
198
- history_openai_format = []
199
- user_msg_parts = []
 
 
 
 
200
 
201
- if system_prompt:
202
- if not (model == "o1-mini" or model == "o1-preview"):
203
- role = "system"
204
- else:
205
- role = "user"
206
- history_openai_format.append({"role": role, "content": system_prompt})
207
-
208
- for human, assi in history:
209
- if human is not None:
210
- if type(human) is tuple:
211
- user_msg_parts.extend(encode_file(human[0]))
212
- else:
213
- user_msg_parts.append({"type": "text", "text": human})
214
-
215
- if assi is not None:
216
- if user_msg_parts:
217
- history_openai_format.append({"role": "user", "content": user_msg_parts})
218
- user_msg_parts = []
219
-
220
- history_openai_format.append({"role": "assistant", "content": assi})
221
-
222
- if message["text"]:
223
- user_msg_parts.append({"type": "text", "text": message["text"]})
224
- if message["files"]:
225
- for file in message["files"]:
226
- user_msg_parts.extend(encode_file(file))
227
- history_openai_format.append({"role": "user", "content": user_msg_parts})
228
- user_msg_parts = []
229
-
230
- if log_to_console:
231
- print(f"br_prompt: {str(history_openai_format)}")
232
-
233
- if model == "o1-preview" or model == "o1-mini":
234
- response = client.chat.completions.create(
235
- model=model,
236
- messages= history_openai_format,
237
- seed=seed_i,
238
- )
239
-
240
- yield response.choices[0].message.content
241
-
242
- if log_to_console:
243
- print(f"usage: {response.usage}")
244
- else:
245
- response = client.chat.completions.create(
246
- model=model,
247
- messages= history_openai_format,
248
- temperature=temperature,
249
- seed=seed_i,
250
- max_tokens=max_tokens,
251
- stream=True,
252
- stream_options={"include_usage": True}
253
- )
254
-
255
- partial_response=""
256
- for chunk in response:
257
- if chunk.choices:
258
- txt = ""
259
- for choice in chunk.choices:
260
- cont = choice.delta.content
261
- if cont:
262
- txt += cont
263
-
264
- partial_response += txt
265
- yield partial_response
266
-
267
- if chunk.usage and log_to_console:
268
- print(f"usage: {chunk.usage}")
269
 
270
  if log_to_console:
271
  print(f"br_result: {str(history)}")
@@ -273,6 +174,21 @@ def bot(message, history, oai_key, system_prompt, seed, temperature, max_tokens,
273
  except Exception as e:
274
  raise gr.Error(f"Error: {str(e)}")
275
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
276
  def import_history(history, file):
277
  with open(file.name, mode="rb") as f:
278
  content = f.read()
@@ -294,23 +210,23 @@ def import_history(history, file):
294
  # Assume it's an old format with only history data
295
  history = import_data
296
 
297
- return history, system_prompt.value # Return system prompt value to be set in the UI
298
 
299
  with gr.Blocks(delete_cache=(86400, 86400)) as demo:
300
- gr.Markdown("# OAI Chat (Nils' Version™️)")
301
  with gr.Accordion("Startup"):
302
  gr.Markdown("""Use of this interface permitted under the terms and conditions of the
303
- [MIT license](https://github.com/ndurner/oai_chat/blob/main/LICENSE).
304
- Third party terms and conditions apply, particularly
305
- those of the LLM vendor (OpenAI) and hosting provider (Hugging Face). This app and the AI models may make mistakes, so verify any outputs.""")
306
-
307
- oai_key = gr.Textbox(label="OpenAI API Key", elem_id="oai_key")
308
- model = gr.Dropdown(label="Model", value="gpt-4-turbo", allow_custom_value=True, elem_id="model",
309
- choices=["gpt-4-turbo", "gpt-4o-2024-05-13", "o1-mini", "o1-preview", "chatgpt-4o-latest", "gpt-4o", "gpt-4o-mini", "gpt-4-turbo-preview", "gpt-4-1106-preview", "gpt-4", "gpt-4-vision-preview", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-1106", "whisper", "dall-e-3"])
310
- system_prompt = gr.TextArea("You are a helpful yet diligent AI assistant. Answer faithfully and factually correct. Respond with 'I do not know' if uncertain.", label="System Prompt", lines=3, max_lines=250, elem_id="system_prompt")
311
  seed = gr.Textbox(label="Seed", elem_id="seed")
312
- temp = gr.Slider(0, 2, label="Temperature", elem_id="temp", value=1)
313
- max_tokens = gr.Slider(1, 16384, label="Max. Tokens", elem_id="max_tokens", value=800)
314
  save_button = gr.Button("Save Settings")
315
  load_button = gr.Button("Load Settings")
316
  dl_settings_button = gr.Button("Download Settings")
@@ -318,7 +234,7 @@ with gr.Blocks(delete_cache=(86400, 86400)) as demo:
318
 
319
  load_button.click(load_settings, js="""
320
  () => {
321
- let elems = ['#oai_key textarea', '#system_prompt textarea', '#seed textarea', '#temp input', '#max_tokens input', '#model'];
322
  elems.forEach(elem => {
323
  let item = document.querySelector(elem);
324
  let event = new InputEvent('input', { bubbles: true });
@@ -328,9 +244,9 @@ with gr.Blocks(delete_cache=(86400, 86400)) as demo:
328
  }
329
  """)
330
 
331
- save_button.click(save_settings, [oai_key, system_prompt, seed, temp, max_tokens, model], js="""
332
- (oai, sys, seed, temp, ntok, model) => {
333
- localStorage.setItem('oai_key', oai);
334
  localStorage.setItem('system_prompt', sys);
335
  localStorage.setItem('seed', seed);
336
  localStorage.setItem('temp', document.querySelector('#temp input').value);
@@ -339,18 +255,18 @@ with gr.Blocks(delete_cache=(86400, 86400)) as demo:
339
  }
340
  """)
341
 
342
- control_ids = [('oai_key', '#oai_key textarea'),
343
- ('system_prompt', '#system_prompt textarea'),
344
- ('seed', '#seed textarea'),
345
- ('temp', '#temp input'),
346
- ('max_tokens', '#max_tokens input'),
347
- ('model', '#model')]
348
- controls = [oai_key, system_prompt, seed, temp, max_tokens, model]
349
 
350
- dl_settings_button.click(None, controls, js=generate_download_settings_js("oai_chat_settings.bin", control_ids))
351
  ul_settings_button.click(None, None, None, js=generate_upload_settings_js(control_ids))
352
 
353
- chat = gr.ChatInterface(fn=bot, multimodal=True, additional_inputs=controls, autofocus = False)
354
  chat.textbox.file_count = "multiple"
355
  chatbot = chat.chatbot
356
  chatbot.show_copy_button = True
@@ -362,7 +278,7 @@ with gr.Blocks(delete_cache=(86400, 86400)) as demo:
362
  txt_dmp = gr.Textbox("Dump")
363
  dmp_btn.click(dump, inputs=[chatbot], outputs=[txt_dmp])
364
 
365
- with gr.Accordion("Import/Export", open = False):
366
  import_button = gr.UploadButton("History Import")
367
  export_button = gr.Button("History Export")
368
  export_button.click(lambda: None, [chatbot, system_prompt], js="""
 
1
  import gradio as gr
2
  import base64
3
+ import os
4
+ from mistralai import Mistral
5
  import json
6
  import fitz
7
  from PIL import Image
 
80
  })
81
  message_parts.append({
82
  "type": "image_url",
83
+ "image_url": image_url
 
 
 
84
  })
85
 
86
  pdf.close()
 
111
  content = str(content)
112
 
113
  if isImage:
114
+ user_msg_parts.append({"type": "image_url", "image_url": content})
 
115
  else:
116
  user_msg_parts.append({"type": "text", "text": content})
117
 
118
  return user_msg_parts
119
 
120
+ def bot(message, history, mistral_key, system_prompt, seed, temperature, max_tokens, model):
121
+ try:
122
+ client = Mistral(
123
+ api_key=mistral_key
124
+ )
125
 
126
+ history_mistral_format = []
127
+ user_msg_parts = []
128
 
129
+ if system_prompt:
130
+ history_mistral_format.append({"role": "system", "content": system_prompt})
 
131
 
132
+ for human, assi in history:
133
+ if human is not None:
134
+ if type(human) is tuple:
135
+ user_msg_parts.extend(encode_file(human[0]))
136
+ else:
137
+ user_msg_parts.append({"type": "text", "text": human})
138
 
139
+ if assi is not None:
140
+ if user_msg_parts:
141
+ history_mistral_format.append({"role": "user", "content": user_msg_parts})
142
+ user_msg_parts = []
 
 
143
 
144
+ history_mistral_format.append({"role": "assistant", "content": assi})
 
 
 
 
145
 
146
+ if message["text"]:
147
+ user_msg_parts.append({"type": "text", "text": message["text"]})
148
+ if message["files"]:
149
+ for file in message["files"]:
150
+ user_msg_parts.extend(encode_file(file))
151
+ history_mistral_format.append({"role": "user", "content": user_msg_parts})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
+ if log_to_console:
154
+ print(f"br_prompt: {str(history_mistral_format)}")
155
 
156
+ response = client.chat.stream(
157
+ model=model,
158
+ messages=history_mistral_format,
159
+ temperature=temperature,
160
+ max_tokens=max_tokens
161
+ )
162
 
163
+ partial_response = ""
164
+ for chunk in response:
165
+ if chunk.data.choices:
166
+ txt = chunk.data.choices[0].delta.content
167
+ if txt:
168
+ partial_response += txt
169
+ yield partial_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  if log_to_console:
172
  print(f"br_result: {str(history)}")
 
174
  except Exception as e:
175
  raise gr.Error(f"Error: {str(e)}")
176
 
177
+ def undo(history):
178
+ history.pop()
179
+ return history
180
+
181
+ def dump(history):
182
+ return str(history)
183
+
184
+ def load_settings():
185
+ # Dummy Python function, actual loading is done in JS
186
+ pass
187
+
188
+ def save_settings(acc, sec, prompt, temp, tokens, model):
189
+ # Dummy Python function, actual saving is done in JS
190
+ pass
191
+
192
  def import_history(history, file):
193
  with open(file.name, mode="rb") as f:
194
  content = f.read()
 
210
  # Assume it's an old format with only history data
211
  history = import_data
212
 
213
+ return history, system_prompt.value
214
 
215
  with gr.Blocks(delete_cache=(86400, 86400)) as demo:
216
+ gr.Markdown("# Mistral Chat")
217
  with gr.Accordion("Startup"):
218
  gr.Markdown("""Use of this interface permitted under the terms and conditions of the
219
+ [MIT license](https://github.com/ndurner/mistral_chat/blob/main/LICENSE).
220
+ Third party terms and conditions apply. This app and the AI models may make mistakes, so verify any outputs.""")
221
+
222
+ mistral_key = gr.Textbox(label="Mistral API Key", elem_id="mistral_key")
223
+ model = gr.Dropdown(label="Model", value="pixtral-large-latest", allow_custom_value=True, elem_id="model",
224
+ choices=["pixtral-large-latest", "mistral-large-latest", "pixtral-12b-2409"])
225
+ system_prompt = gr.TextArea("You are a helpful yet diligent AI assistant. Answer faithfully and factually correct. Respond with 'I do not know' if uncertain.",
226
+ label="System Prompt", lines=3, max_lines=250, elem_id="system_prompt")
227
  seed = gr.Textbox(label="Seed", elem_id="seed")
228
+ temp = gr.Slider(0, 1, label="Temperature", elem_id="temp", value=0.7)
229
+ max_tokens = gr.Slider(1, 4096, label="Max. Tokens", elem_id="max_tokens", value=800)
230
  save_button = gr.Button("Save Settings")
231
  load_button = gr.Button("Load Settings")
232
  dl_settings_button = gr.Button("Download Settings")
 
234
 
235
  load_button.click(load_settings, js="""
236
  () => {
237
+ let elems = ['#mistral_key textarea', '#system_prompt textarea', '#seed textarea', '#temp input', '#max_tokens input', '#model'];
238
  elems.forEach(elem => {
239
  let item = document.querySelector(elem);
240
  let event = new InputEvent('input', { bubbles: true });
 
244
  }
245
  """)
246
 
247
+ save_button.click(save_settings, [mistral_key, system_prompt, seed, temp, max_tokens, model], js="""
248
+ (key, sys, seed, temp, ntok, model) => {
249
+ localStorage.setItem('mistral_key', key);
250
  localStorage.setItem('system_prompt', sys);
251
  localStorage.setItem('seed', seed);
252
  localStorage.setItem('temp', document.querySelector('#temp input').value);
 
255
  }
256
  """)
257
 
258
+ control_ids = [('mistral_key', '#mistral_key textarea'),
259
+ ('system_prompt', '#system_prompt textarea'),
260
+ ('seed', '#seed textarea'),
261
+ ('temp', '#temp input'),
262
+ ('max_tokens', '#max_tokens input'),
263
+ ('model', '#model')]
264
+ controls = [mistral_key, system_prompt, seed, temp, max_tokens, model]
265
 
266
+ dl_settings_button.click(None, controls, js=generate_download_settings_js("mistral_chat_settings.bin", control_ids))
267
  ul_settings_button.click(None, None, None, js=generate_upload_settings_js(control_ids))
268
 
269
+ chat = gr.ChatInterface(fn=bot, multimodal=True, additional_inputs=controls, autofocus=False)
270
  chat.textbox.file_count = "multiple"
271
  chatbot = chat.chatbot
272
  chatbot.show_copy_button = True
 
278
  txt_dmp = gr.Textbox("Dump")
279
  dmp_btn.click(dump, inputs=[chatbot], outputs=[txt_dmp])
280
 
281
+ with gr.Accordion("Import/Export", open=False):
282
  import_button = gr.UploadButton("History Import")
283
  export_button = gr.Button("History Export")
284
  export_button.click(lambda: None, [chatbot, system_prompt], js="""
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
  gradio == 5.1
2
- openai >= 1.0.0
3
  lxml
4
  PyMuPDF
 
1
  gradio == 5.1
2
+ mistralai
3
  lxml
4
  PyMuPDF