davidberenstein1957 HF staff commited on
Commit
a8cff7e
·
unverified ·
2 Parent(s): 4e7dde0 35aa65d

Merge pull request #3 from huggingface/feat/upgrade-gradio-flow

Browse files
Files changed (3) hide show
  1. .gitignore +1 -0
  2. README.md +1 -1
  3. app/app.py +152 -23
.gitignore CHANGED
@@ -160,3 +160,4 @@ cython_debug/
160
  # and can be added to the global gitignore or merged into this file. For a more nuclear
161
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
  #.idea/
 
 
160
  # and can be added to the global gitignore or merged into this file. For a more nuclear
161
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
  #.idea/
163
+ user_feedback
README.md CHANGED
@@ -4,7 +4,7 @@ emoji: 🚀
4
  colorFrom: blue
5
  colorTo: gray
6
  sdk: gradio
7
- sdk_version: 5.8.0
8
  app_file: app/app.py
9
  pinned: false
10
  ---
 
4
  colorFrom: blue
5
  colorTo: gray
6
  sdk: gradio
7
+ sdk_version: 5.10.0
8
  app_file: app/app.py
9
  pinned: false
10
  ---
app/app.py CHANGED
@@ -1,16 +1,18 @@
1
  import os
 
2
  import uuid
3
  from base64 import b64encode
4
  from datetime import datetime
5
  from mimetypes import guess_type
6
  from pathlib import Path
 
7
 
8
  import gradio as gr
 
 
9
  from huggingface_hub import InferenceClient
10
  from pandas import DataFrame
11
 
12
- from feedback import save_feedback
13
-
14
  client = InferenceClient(
15
  token=os.getenv("HF_TOKEN"),
16
  model=(
@@ -30,7 +32,7 @@ def add_user_message(history, message):
30
  return history, gr.MultimodalTextbox(value=None, interactive=False)
31
 
32
 
33
- def _format_history_as_messages(history: list):
34
  messages = []
35
  current_role = None
36
  current_message_content = []
@@ -84,46 +86,152 @@ def _process_content(content) -> str | list[str]:
84
  return content
85
 
86
 
87
- def respond_system_message(history: list) -> list: # -> list:
88
- """Respond to the user message with a system message"""
89
- messages = _format_history_as_messages(history)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  response = client.chat.completions.create(
91
  messages=messages,
92
  max_tokens=2000,
93
  stream=False,
 
 
94
  )
95
  content = response.choices[0].message.content
96
- # TODO: Add a response to the user message
97
-
98
  message = gr.ChatMessage(role="assistant", content=content)
99
  history.append(message)
100
  return history
101
 
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
  def wrangle_like_data(x: gr.LikeData, history) -> DataFrame:
104
  """Wrangle conversations and liked data into a DataFrame"""
105
 
106
- liked_index = x.index[0]
 
 
 
107
 
108
  output_data = []
109
  for idx, message in enumerate(history):
 
 
110
  if idx == liked_index:
111
  message["metadata"] = {"title": "liked" if x.liked else "disliked"}
 
 
112
  rating = message["metadata"].get("title")
113
  if rating == "liked":
114
  message["rating"] = 1
115
  elif rating == "disliked":
116
  message["rating"] = -1
117
  else:
118
- message["rating"] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  output_data.append(
121
- dict([(k, v) for k, v in message.items() if k != "metadata"])
 
 
122
  )
123
 
124
  return history, DataFrame(data=output_data)
125
 
126
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  def submit_conversation(dataframe, session_id):
128
  """ "Submit the conversation to dataset repo"""
129
  if dataframe.empty:
@@ -142,7 +250,14 @@ def submit_conversation(dataframe, session_id):
142
  return (gr.Dataframe(value=None, interactive=False), [])
143
 
144
 
145
- with gr.Blocks() as demo:
 
 
 
 
 
 
 
146
  ##############################
147
  # Chatbot
148
  ##############################
@@ -154,8 +269,10 @@ with gr.Blocks() as demo:
154
 
155
  chatbot = gr.Chatbot(
156
  elem_id="chatbot",
 
157
  bubble_full_width=False,
158
  type="messages",
 
159
  )
160
 
161
  chat_input = gr.MultimodalTextbox(
@@ -166,21 +283,23 @@ with gr.Blocks() as demo:
166
  submit_btn=True,
167
  )
168
 
169
- chat_msg = chat_input.submit(
170
- fn=add_user_message, inputs=[chatbot, chat_input], outputs=[chatbot, chat_input]
171
- )
172
 
173
- bot_msg = chat_msg.then(
174
- respond_system_message, chatbot, chatbot, api_name="bot_response"
175
  )
176
 
177
- bot_msg.then(lambda: gr.Textbox(interactive=True), None, [chat_input])
178
-
179
  ##############################
180
  # Deal with feedback
181
  ##############################
182
 
183
- dataframe = gr.DataFrame()
 
 
 
 
 
 
184
 
185
  chatbot.like(
186
  fn=wrangle_like_data,
@@ -189,9 +308,19 @@ with gr.Blocks() as demo:
189
  like_user_message=False,
190
  )
191
 
192
- gr.Button(
193
- value="Submit conversation",
194
- ).click(
 
 
 
 
 
 
 
 
 
 
195
  fn=submit_conversation,
196
  inputs=[dataframe, session_id],
197
  outputs=[dataframe, chatbot],
 
1
  import os
2
+ import random
3
  import uuid
4
  from base64 import b64encode
5
  from datetime import datetime
6
  from mimetypes import guess_type
7
  from pathlib import Path
8
+ from typing import Optional
9
 
10
  import gradio as gr
11
+ from feedback import save_feedback
12
+ from gradio.components.chatbot import Option
13
  from huggingface_hub import InferenceClient
14
  from pandas import DataFrame
15
 
 
 
16
  client = InferenceClient(
17
  token=os.getenv("HF_TOKEN"),
18
  model=(
 
32
  return history, gr.MultimodalTextbox(value=None, interactive=False)
33
 
34
 
35
+ def format_history_as_messages(history: list):
36
  messages = []
37
  current_role = None
38
  current_message_content = []
 
86
  return content
87
 
88
 
89
+ def add_fake_like_data(history: list, session_id: str, liked: bool = False) -> None:
90
+ data = {
91
+ "index": len(history) - 1,
92
+ "value": history[-1],
93
+ "liked": True,
94
+ }
95
+ _, dataframe = wrangle_like_data(
96
+ gr.LikeData(target=None, data=data), history.copy()
97
+ )
98
+ submit_conversation(dataframe, session_id)
99
+
100
+
101
+ def respond_system_message(
102
+ history: list, temperature: Optional[float] = None, seed: Optional[int] = None
103
+ ) -> list: # -> list:
104
+ """Respond to the user message with a system message
105
+
106
+ Return the history with the new message"""
107
+ messages = format_history_as_messages(history)
108
  response = client.chat.completions.create(
109
  messages=messages,
110
  max_tokens=2000,
111
  stream=False,
112
+ seed=seed,
113
+ temperature=temperature,
114
  )
115
  content = response.choices[0].message.content
 
 
116
  message = gr.ChatMessage(role="assistant", content=content)
117
  history.append(message)
118
  return history
119
 
120
 
121
+ def update_dataframe(dataframe: DataFrame, history: list) -> DataFrame:
122
+ """Update the dataframe with the new message"""
123
+ data = {
124
+ "index": 9999,
125
+ "value": None,
126
+ "liked": False,
127
+ }
128
+ _, dataframe = wrangle_like_data(
129
+ gr.LikeData(target=None, data=data), history.copy()
130
+ )
131
+ return dataframe
132
+
133
+
134
  def wrangle_like_data(x: gr.LikeData, history) -> DataFrame:
135
  """Wrangle conversations and liked data into a DataFrame"""
136
 
137
+ if isinstance(x.index, int):
138
+ liked_index = x.index
139
+ else:
140
+ liked_index = x.index[0]
141
 
142
  output_data = []
143
  for idx, message in enumerate(history):
144
+ if isinstance(message, gr.ChatMessage):
145
+ message = message.__dict__
146
  if idx == liked_index:
147
  message["metadata"] = {"title": "liked" if x.liked else "disliked"}
148
+ if not isinstance(message["metadata"], dict):
149
+ message["metadata"] = message["metadata"].__dict__
150
  rating = message["metadata"].get("title")
151
  if rating == "liked":
152
  message["rating"] = 1
153
  elif rating == "disliked":
154
  message["rating"] = -1
155
  else:
156
+ message["rating"] = 0
157
+
158
+ message["chosen"] = ""
159
+ message["rejected"] = ""
160
+ if message["options"]:
161
+ for option in message["options"]:
162
+ if not isinstance(option, dict):
163
+ option = option.__dict__
164
+ message[option["label"]] = option["value"]
165
+ else:
166
+ if message["rating"] == 1:
167
+ message["chosen"] = message["content"]
168
+ elif message["rating"] == -1:
169
+ message["rejected"] = message["content"]
170
 
171
  output_data.append(
172
+ dict(
173
+ [(k, v) for k, v in message.items() if k not in ["metadata", "options"]]
174
+ )
175
  )
176
 
177
  return history, DataFrame(data=output_data)
178
 
179
 
180
+ def wrangle_edit_data(
181
+ x: gr.EditData, history: list, dataframe: DataFrame, session_id: str
182
+ ) -> list:
183
+ """Edit the conversation and add negative feedback if assistant message is edited, otherwise regenerate the message
184
+
185
+ Return the history with the new message"""
186
+ if isinstance(x.index, int):
187
+ index = x.index
188
+ else:
189
+ index = x.index[0]
190
+
191
+ original_message = gr.ChatMessage(
192
+ role="assistant", content=dataframe.iloc[index]["content"]
193
+ ).__dict__
194
+
195
+ if history[index]["role"] == "user":
196
+ # Add feedback on original and corrected message
197
+ add_fake_like_data(history[: index + 2], session_id, liked=True)
198
+ add_fake_like_data(history[: index + 1] + [original_message], session_id)
199
+ history = respond_system_message(
200
+ history[: index + 1],
201
+ temperature=random.randint(1, 100) / 100,
202
+ seed=random.randint(0, 1000000),
203
+ )
204
+ return history
205
+ else:
206
+ # Add feedback on original and corrected message
207
+ add_fake_like_data(history[: index + 1], session_id, liked=True)
208
+ add_fake_like_data(history[:index] + [original_message], session_id)
209
+ history = history[: index + 1]
210
+ # add chosen and rejected options
211
+ history[-1]["options"] = [
212
+ Option(label="chosen", value=x.value),
213
+ Option(label="rejected", value=original_message["content"]),
214
+ ]
215
+ return history
216
+
217
+
218
+ def wrangle_retry_data(
219
+ x: gr.RetryData, history: list, dataframe: DataFrame, session_id: str
220
+ ) -> list:
221
+ """Respond to the user message with a system message and add negative feedback on the original message
222
+
223
+ Return the history with the new message"""
224
+ add_fake_like_data(history, session_id)
225
+
226
+ # Return the history without a new message
227
+ history = respond_system_message(
228
+ history[:-1],
229
+ temperature=random.randint(1, 100) / 100,
230
+ seed=random.randint(0, 1000000),
231
+ )
232
+ return history, update_dataframe(dataframe, history)
233
+
234
+
235
  def submit_conversation(dataframe, session_id):
236
  """ "Submit the conversation to dataset repo"""
237
  if dataframe.empty:
 
250
  return (gr.Dataframe(value=None, interactive=False), [])
251
 
252
 
253
+ css = """
254
+ .options {
255
+ display: none !important;
256
+ }
257
+ """
258
+
259
+
260
+ with gr.Blocks(css=css) as demo:
261
  ##############################
262
  # Chatbot
263
  ##############################
 
269
 
270
  chatbot = gr.Chatbot(
271
  elem_id="chatbot",
272
+ editable="all",
273
  bubble_full_width=False,
274
  type="messages",
275
+ feedback_options=["Like", "Dislike"],
276
  )
277
 
278
  chat_input = gr.MultimodalTextbox(
 
283
  submit_btn=True,
284
  )
285
 
286
+ dataframe = gr.Dataframe(wrap=True)
 
 
287
 
288
+ submit_btn = gr.Button(
289
+ value="Submit conversation",
290
  )
291
 
 
 
292
  ##############################
293
  # Deal with feedback
294
  ##############################
295
 
296
+ chat_input.submit(
297
+ fn=add_user_message,
298
+ inputs=[chatbot, chat_input],
299
+ outputs=[chatbot, chat_input],
300
+ ).then(respond_system_message, chatbot, chatbot, api_name="bot_response").then(
301
+ lambda: gr.Textbox(interactive=True), None, [chat_input]
302
+ ).then(update_dataframe, inputs=[dataframe, chatbot], outputs=[dataframe])
303
 
304
  chatbot.like(
305
  fn=wrangle_like_data,
 
308
  like_user_message=False,
309
  )
310
 
311
+ chatbot.retry(
312
+ fn=wrangle_retry_data,
313
+ inputs=[chatbot, dataframe, session_id],
314
+ outputs=[chatbot, dataframe],
315
+ )
316
+
317
+ chatbot.edit(
318
+ fn=wrangle_edit_data,
319
+ inputs=[chatbot, dataframe, session_id],
320
+ outputs=[chatbot],
321
+ ).then(update_dataframe, inputs=[dataframe, chatbot], outputs=[dataframe])
322
+
323
+ submit_btn.click(
324
  fn=submit_conversation,
325
  inputs=[dataframe, session_id],
326
  outputs=[dataframe, chatbot],