jedick commited on
Commit
0fc99d7
·
1 Parent(s): b16a131

Update feedback processing

Browse files
Files changed (2) hide show
  1. app.py +41 -38
  2. feedback.py +76 -11
app.py CHANGED
@@ -107,38 +107,45 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
107
  </table>
108
 
109
  """,
110
- elem_id="intro_table",
111
  )
112
 
113
  with gr.Row():
114
- with gr.Column():
115
- with gr.Accordion("More Info", open=False) as accordion:
116
  gr.Markdown(
117
- """#### Query Instructions
118
  - Page title is case sensitive; use underscores or spaces
119
  - Specify any number of days or up to 499 revisions behind
120
  - The closest available revision is retrieved
121
  - Only article introductions are downloaded
122
  """
123
  )
 
124
  gr.Markdown(
125
- """#### Confidence Key
126
  - **High:** heuristic = few-shot, judge agrees
127
  - **Moderate:** heuristic ≠ few-shot, judge decides
128
  - **Questionable:** heuristic = few-shot, judge vetoes
129
  """
130
  )
131
- random_btn = gr.Button(
132
- "Get Random Page Title", size="md", elem_id="random-button"
133
- )
134
- title_input = gr.Textbox(
135
- label="Wikipedia Page Title", placeholder="e.g., Albert Einstein", value=""
136
- )
137
- number_input = gr.Number(label="Number", value=50, minimum=0, precision=0)
138
- units_dropdown = gr.Dropdown(
139
- choices=["revisions", "days"], value="revisions", label="Units"
140
- )
141
- with gr.Column():
 
 
 
 
 
 
142
  submit_btn = gr.Button(
143
  "Fetch Revisions and Run Model", size="md", variant="primary"
144
  )
@@ -182,8 +189,8 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
182
  lines=1,
183
  interactive=False,
184
  )
185
- confidence = gr.Textbox(
186
- label="Confidence",
187
  lines=1,
188
  interactive=False,
189
  )
@@ -207,12 +214,12 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
207
  random_btn.click(
208
  fn=get_random_wikipedia_title,
209
  inputs=None,
210
- outputs=[title_input],
211
  )
212
 
213
  gr.on(
214
  # Press Enter in textbox or use button to submit
215
- triggers=[title_input.submit, submit_btn.click],
216
  # Clear the new_revision and new_timestamp values before proceeding.
217
  # The empty values will propagate to the other components (through function return values) if there is an error.
218
  fn=lambda: (gr.update(value=""), gr.update(value="")),
@@ -222,16 +229,16 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
222
  ).then(
223
  # Initialize Logfire context
224
  fn=start_parent_span,
225
- inputs=[title_input, number_input, units_dropdown],
226
  outputs=context,
227
  ).then(
228
  fn=fetch_current_revision,
229
- inputs=[title_input, context],
230
  outputs=[new_revision, new_timestamp],
231
  api_name=False,
232
  ).then(
233
  fn=fetch_previous_revision,
234
- inputs=[title_input, number_input, units_dropdown, new_revision, context],
235
  outputs=[old_revision, old_timestamp],
236
  api_name=False,
237
  ).then(
@@ -255,7 +262,7 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
255
  fewshot_rationale,
256
  context,
257
  ],
258
- outputs=[judge_noteworthy, noteworthy_text, judge_reasoning, confidence],
259
  api_name=False,
260
  )
261
 
@@ -282,7 +289,7 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
282
  fewshot_rationale,
283
  context,
284
  ],
285
- outputs=[judge_noteworthy, noteworthy_text, judge_reasoning, confidence],
286
  api_name=False,
287
  )
288
 
@@ -290,9 +297,9 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
290
  thumbs_up_btn.click(
291
  fn=save_feedback_agree,
292
  inputs=[
293
- title_input,
294
- number_input,
295
- units_dropdown,
296
  old_revision,
297
  new_revision,
298
  old_timestamp,
@@ -301,7 +308,7 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
301
  fewshot_rationale,
302
  judge_reasoning,
303
  noteworthy_text,
304
- confidence,
305
  heuristic_noteworthy,
306
  fewshot_noteworthy,
307
  judge_noteworthy,
@@ -312,9 +319,9 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
312
  thumbs_down_btn.click(
313
  fn=save_feedback_disagree,
314
  inputs=[
315
- title_input,
316
- number_input,
317
- units_dropdown,
318
  old_revision,
319
  new_revision,
320
  old_timestamp,
@@ -323,7 +330,7 @@ with gr.Blocks(title="Noteworthy Differences") as demo:
323
  fewshot_rationale,
324
  judge_reasoning,
325
  noteworthy_text,
326
- confidence,
327
  heuristic_noteworthy,
328
  fewshot_noteworthy,
329
  judge_noteworthy,
@@ -340,15 +347,11 @@ if __name__ == "__main__":
340
  head = '<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">'
341
  # Use CSS to style table
342
  css = """
343
- #intro_table {background-color: #eff6ff}
344
  table, tr, td {
345
  border: none; /* Removes all borders */
346
  border-collapse: collapse; /* Ensures no gaps between cells */
347
  }
348
-
349
- #random-button {
350
- margin-top: auto; /* Pushes the button to the bottom */
351
- }
352
  """
353
 
354
  demo.launch(theme=theme, head=head, css=css)
 
107
  </table>
108
 
109
  """,
110
+ elem_id="intro-table",
111
  )
112
 
113
  with gr.Row():
114
+ with gr.Column(scale=1):
115
+ with gr.Accordion("Query Instructions", open=False) as accordion:
116
  gr.Markdown(
117
+ """
118
  - Page title is case sensitive; use underscores or spaces
119
  - Specify any number of days or up to 499 revisions behind
120
  - The closest available revision is retrieved
121
  - Only article introductions are downloaded
122
  """
123
  )
124
+ with gr.Accordion("Confidence Scores", open=False) as accordion:
125
  gr.Markdown(
126
+ """
127
  - **High:** heuristic = few-shot, judge agrees
128
  - **Moderate:** heuristic ≠ few-shot, judge decides
129
  - **Questionable:** heuristic = few-shot, judge vetoes
130
  """
131
  )
132
+ with gr.Column(scale=3):
133
+ with gr.Row():
134
+ page_title = gr.Textbox(
135
+ label="Wikipedia Page Title",
136
+ placeholder="e.g., Albert Einstein",
137
+ value="",
138
+ )
139
+ number_behind = gr.Number(
140
+ label="Number Behind", value=50, minimum=0, precision=0
141
+ )
142
+ units_behind = gr.Dropdown(
143
+ choices=["revisions", "days"],
144
+ value="revisions",
145
+ label="Units Behind",
146
+ )
147
+ with gr.Column(scale=1):
148
+ random_btn = gr.Button("Get Random Page Title", size="md")
149
  submit_btn = gr.Button(
150
  "Fetch Revisions and Run Model", size="md", variant="primary"
151
  )
 
189
  lines=1,
190
  interactive=False,
191
  )
192
+ confidence_score = gr.Textbox(
193
+ label="Confidence Score",
194
  lines=1,
195
  interactive=False,
196
  )
 
214
  random_btn.click(
215
  fn=get_random_wikipedia_title,
216
  inputs=None,
217
+ outputs=[page_title],
218
  )
219
 
220
  gr.on(
221
  # Press Enter in textbox or use button to submit
222
+ triggers=[page_title.submit, submit_btn.click],
223
  # Clear the new_revision and new_timestamp values before proceeding.
224
  # The empty values will propagate to the other components (through function return values) if there is an error.
225
  fn=lambda: (gr.update(value=""), gr.update(value="")),
 
229
  ).then(
230
  # Initialize Logfire context
231
  fn=start_parent_span,
232
+ inputs=[page_title, number_behind, units_behind],
233
  outputs=context,
234
  ).then(
235
  fn=fetch_current_revision,
236
+ inputs=[page_title, context],
237
  outputs=[new_revision, new_timestamp],
238
  api_name=False,
239
  ).then(
240
  fn=fetch_previous_revision,
241
+ inputs=[page_title, number_behind, units_behind, new_revision, context],
242
  outputs=[old_revision, old_timestamp],
243
  api_name=False,
244
  ).then(
 
262
  fewshot_rationale,
263
  context,
264
  ],
265
+ outputs=[judge_noteworthy, noteworthy_text, judge_reasoning, confidence_score],
266
  api_name=False,
267
  )
268
 
 
289
  fewshot_rationale,
290
  context,
291
  ],
292
+ outputs=[judge_noteworthy, noteworthy_text, judge_reasoning, confidence_score],
293
  api_name=False,
294
  )
295
 
 
297
  thumbs_up_btn.click(
298
  fn=save_feedback_agree,
299
  inputs=[
300
+ page_title,
301
+ number_behind,
302
+ units_behind,
303
  old_revision,
304
  new_revision,
305
  old_timestamp,
 
308
  fewshot_rationale,
309
  judge_reasoning,
310
  noteworthy_text,
311
+ confidence_score,
312
  heuristic_noteworthy,
313
  fewshot_noteworthy,
314
  judge_noteworthy,
 
319
  thumbs_down_btn.click(
320
  fn=save_feedback_disagree,
321
  inputs=[
322
+ page_title,
323
+ number_behind,
324
+ units_behind,
325
  old_revision,
326
  new_revision,
327
  old_timestamp,
 
330
  fewshot_rationale,
331
  judge_reasoning,
332
  noteworthy_text,
333
+ confidence_score,
334
  heuristic_noteworthy,
335
  fewshot_noteworthy,
336
  judge_noteworthy,
 
347
  head = '<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" rel="stylesheet">'
348
  # Use CSS to style table
349
  css = """
350
+ #intro-table {background-color: #eff6ff}
351
  table, tr, td {
352
  border: none; /* Removes all borders */
353
  border-collapse: collapse; /* Ensures no gaps between cells */
354
  }
 
 
 
 
355
  """
356
 
357
  demo.launch(theme=theme, head=head, css=css)
feedback.py CHANGED
@@ -6,6 +6,7 @@ import gradio as gr
6
  import logfire
7
  import json
8
  import os
 
9
 
10
  # Load API keys
11
  load_dotenv()
@@ -42,9 +43,9 @@ def save_feedback(*args, feedback_value: str) -> None:
42
  """
43
  # Assign dict keys to positional arguments
44
  keys = [
45
- "title_input",
46
- "number_input",
47
- "units_dropdown",
48
  "old_revision",
49
  "new_revision",
50
  "old_timestamp",
@@ -53,21 +54,85 @@ def save_feedback(*args, feedback_value: str) -> None:
53
  "fewshot_rationale",
54
  "judge_reasoning",
55
  "noteworthy_text",
56
- "confidence",
57
  "heuristic_noteworthy",
58
  "fewshot_noteworthy",
59
  "judge_noteworthy",
60
  ]
61
  feedback_dict = dict(zip(keys, args))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  # Add feedback value
63
  feedback_dict["feedback"] = feedback_value
64
- # Save feedback to file
65
- feedback_file = f"train-{datetime.now().isoformat()}.json"
66
- feedback_path = USER_FEEDBACK_DIR / feedback_file
67
- with feedback_path.open("a") as f:
68
- f.write(json.dumps(feedback_dict))
69
- f.write("\n")
70
- gr.Success(f"Saved feedback: <strong>{feedback_value}</strong>", duration=5)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
 
73
  @logfire.instrument("Save feedback: agree")
 
6
  import logfire
7
  import json
8
  import os
9
+ import re
10
 
11
  # Load API keys
12
  load_dotenv()
 
43
  """
44
  # Assign dict keys to positional arguments
45
  keys = [
46
+ "page_title",
47
+ "number_behind",
48
+ "units_behind",
49
  "old_revision",
50
  "new_revision",
51
  "old_timestamp",
 
54
  "fewshot_rationale",
55
  "judge_reasoning",
56
  "noteworthy_text",
57
+ "confidence_score",
58
  "heuristic_noteworthy",
59
  "fewshot_noteworthy",
60
  "judge_noteworthy",
61
  ]
62
  feedback_dict = dict(zip(keys, args))
63
+
64
+ # Data cleanup
65
+ # Split dictionary in two
66
+ keys_start = {"page_title", "number_behind", "units_behind"}
67
+ dict_start = {k: v for k, v in feedback_dict.items() if k in keys_start}
68
+ dict_end = {k: v for k, v in feedback_dict.items() if k not in keys_start}
69
+ # Normalize timestamp and extract revisions behind from display string
70
+ raw_new_timestamp = dict_end.get("new_timestamp", "")
71
+ # Extract ISO timestamp (e.g. 2013-12-09T04:48:24Z)
72
+ ts_match = re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", raw_new_timestamp)
73
+ if ts_match:
74
+ dict_end["new_timestamp"] = ts_match.group(0)
75
+ # Do the same for old timestamp
76
+ raw_old_timestamp = dict_end.get("old_timestamp", "")
77
+ ts_match = re.search(r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z", raw_old_timestamp)
78
+ if ts_match:
79
+ dict_end["old_timestamp"] = ts_match.group(0)
80
+ # Extract revisions behind (e.g. "35 revisions behind")
81
+ rev_match = re.search(r"(\d+)\s+revisions?\s+behind", raw_old_timestamp)
82
+ if rev_match:
83
+ dict_start["revisions_behind"] = int(rev_match.group(1))
84
+ else:
85
+ dict_start["revisions_behind"] = None
86
+ # Merge the start and end dictionaries
87
+ feedback_dict = dict_start | dict_end
88
+
89
  # Add feedback value
90
  feedback_dict["feedback"] = feedback_value
91
+ do_save = True
92
+ feedback_action = "Saved"
93
+
94
+ # Check for duplicate feedback against the most recent feedback file
95
+ feedback_files = sorted(
96
+ USER_FEEDBACK_DIR.glob("*.json"),
97
+ key=lambda p: p.stat().st_mtime,
98
+ reverse=True,
99
+ )
100
+ if feedback_files:
101
+ latest_feedback_path = feedback_files[0]
102
+ with latest_feedback_path.open("r") as latest_f:
103
+ # Files are written as JSON Lines, but currently contain a single line
104
+ latest_line = latest_f.readline().strip()
105
+ if latest_line:
106
+ latest_feedback = json.loads(latest_line)
107
+ # Pop the last feedback value
108
+ old_feedback = latest_feedback.pop("feedback", None)
109
+ # Use the same feedback value (for detecting resubmission)
110
+ latest_feedback["feedback"] = feedback_value
111
+ if latest_feedback == feedback_dict:
112
+ # The user resubmitted feedback: remove the previous feedback
113
+ latest_feedback_path.unlink()
114
+ if feedback_dict["feedback"] == old_feedback:
115
+ # The user submitted the same feedback: Issue a warning
116
+ gr.Warning(
117
+ f"Removed feedback: <strong>{old_feedback}</strong>",
118
+ duration=5,
119
+ )
120
+ do_save = False
121
+ else:
122
+ # The user changed the feedback: Proceed to update the feedback
123
+ feedback_action = "Updated"
124
+
125
+ if do_save:
126
+ # Save feedback to file
127
+ feedback_file = f"train-{datetime.now().isoformat()}.json"
128
+ feedback_path = USER_FEEDBACK_DIR / feedback_file
129
+ with feedback_path.open("a") as f:
130
+ f.write(json.dumps(feedback_dict))
131
+ f.write("\n")
132
+ if feedback_action == "Updated":
133
+ gr.Info(f"Updated feedback: <strong>{feedback_value}</strong>", duration=5)
134
+ else:
135
+ gr.Success(f"Saved feedback: <strong>{feedback_value}</strong>", duration=5)
136
 
137
 
138
  @logfire.instrument("Save feedback: agree")