omerehrlich commited on
Commit
cea1ff8
·
1 Parent(s): 5fda3e7

By-section 'All edit types' cell click: one combined summary of all edits, not per-label

Browse files
api.py CHANGED
@@ -558,6 +558,12 @@ async def download_demo_csv(article: str) -> FileResponse:
558
  class SummarizePayload(BaseModel):
559
  article: str
560
  label: int
 
 
 
 
 
 
561
  period: str # one of "Y" | "2Q" | "Q" — matches the frontend Period type
562
  # e.g. "2018", "2018Q3", "2018H1". Omit / send null to summarize across
563
  # every period (the "Summarize all periods" affordance in Over-Time).
@@ -784,6 +790,16 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
784
  section-group) cell using a fast OpenAI model. Used by interactive chart
785
  drill-downs.
786
  """
 
 
 
 
 
 
 
 
 
 
787
  # Client-supplied path: the dataset is in the user's browser, not cached
788
  # on the server. The frontend has already filtered rows by (label, period,
789
  # sections) and sent the relevant explanation snippets. Skip the server
@@ -824,11 +840,19 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
824
  if client_snippets is not None:
825
  snippets = client_snippets
826
  # We don't know the unmatched count on the client path; use the count
827
- # the client reported.
 
 
828
  match_count_for_response = matched_count or len(snippets)
 
829
  else:
830
- # Filter to rows that contain the requested label.
831
- mask = df["Labels"].apply(lambda v: payload.label in _parse_labels_field(v))
 
 
 
 
 
832
 
833
  # Never summarize hidden structural sections, even if the union-find
834
  # grouped_idx lumped one in with a visible section the user selected.
@@ -863,12 +887,21 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
863
  ),
864
  }
865
 
866
- # Pull the relevant Explanation snippet for each matched row.
 
 
867
  snippets = []
 
868
  for _, row in matched.iterrows():
869
- text = _explanation_for_label(row.get("Explanation"), payload.label)
870
- if text:
871
- snippets.append(text)
 
 
 
 
 
 
872
 
873
  if not snippets:
874
  return {
@@ -913,46 +946,100 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
913
  if payload.period_key
914
  else "across the article's full revision history"
915
  )
916
- user_prompt = (
917
- f"You are summarizing Wikipedia revision edits for the article "
918
- f"'{payload.article}'.\n\n"
919
- f"Below are explanations from {len(snippets_for_prompt)} edits "
920
- f"classified as '{label_name}' (label {payload.label}) {period_note}, "
921
- f"{section_note}.\n\n"
922
- f"Definition of '{label_name}':\n{label_definition}\n\n"
923
- f"Write a tight, scannable summary as 3–6 short bullets that "
924
- f"synthesize the patterns. Each bullet must:\n"
925
- f"- Start with '- '.\n"
926
- f"- Be one sentence, ideally under 20 words.\n"
927
- f"- Cover a distinct theme or grouping — do NOT restate the same "
928
- f"point twice and do NOT enumerate every edit.\n"
929
- f"- Lead with the substance, not filler (avoid openers like "
930
- f"'This edit…' or 'The article…').\n\n"
931
- f"Scope rules — read carefully:\n"
932
- f"- Only discuss evidence that directly satisfies the definition of "
933
- f"'{label_name}' given above. Do NOT mention edits or details that "
934
- f"appeared in the explanations but fall outside this definition "
935
- f"(e.g. for an 'Academic References Added' summary, do not mention "
936
- f"named-reference tags, formatting cleanup, or non-academic links "
937
- f"the definition is specifically about new academic papers with "
938
- f"identifiers like DOI/PMID/PMC/arXiv/ISSN).\n"
939
- f"- If something feels noteworthy but doesn't match the definition, "
940
- f"omit it. It will be covered by a different label's summary.\n\n"
941
- f"Formatting requirements:\n"
942
- f"- Use Markdown **bold** (double asterisks) only around concrete "
943
- f"evidence that directly satisfies the definition above — e.g. the "
944
- f"specific technical terms, references, wikilinks, numeric values, "
945
- f"or researcher names mentioned in the explanations. Bold only the "
946
- f"evidence phrase itself, not the surrounding prose. Do not bold "
947
- f"anything that isn't itself an instance of the label.\n"
948
- f"- After the bullets, add a separate final line beginning with "
949
- f"'Bottom line: ' that gives a one-sentence takeaway about what "
950
- f"these edits collectively did to the article during this period.\n"
951
- f"- Do not output any prose outside the bullets and the Bottom line "
952
- f"line. No headings, no intro sentence, no closing remark.\n\n"
953
- f"Explanations:\n"
954
- + "\n\n".join(f"- {s}" for s in snippets_for_prompt)
955
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
956
 
957
  try:
958
  import openai
@@ -978,10 +1065,14 @@ async def summarize_edits(payload: SummarizePayload) -> Dict[str, Any]:
978
  return {
979
  "summary": summary_text.strip(),
980
  "edit_count": match_count_for_response,
981
- "explanation_count": len(snippets),
 
 
 
982
  "truncated": truncated,
983
  "label": payload.label,
984
- "label_name": label_name,
 
985
  "period_key": payload.period_key,
986
  }
987
 
 
558
  class SummarizePayload(BaseModel):
559
  article: str
560
  label: int
561
+ # Combined multi-label summary: when set (length > 1), rows are matched if
562
+ # they carry ANY of these labels, and every covered label's explanation
563
+ # snippet is summarized together into a single write-up. Sent by the
564
+ # by-section heatmap's "All scientific edit types" cell clicks. `label`
565
+ # still holds labels[0] for backward-compatible single-label callers.
566
+ labels: Optional[List[int]] = None
567
  period: str # one of "Y" | "2Q" | "Q" — matches the frontend Period type
568
  # e.g. "2018", "2018Q3", "2018H1". Omit / send null to summarize across
569
  # every period (the "Summarize all periods" affordance in Over-Time).
 
790
  section-group) cell using a fast OpenAI model. Used by interactive chart
791
  drill-downs.
792
  """
793
+ # Normalize the label set. A combined request (by-section "All edit types"
794
+ # cell) carries multiple labels; a single-label request carries just one.
795
+ # `combined_labels` is the deduped, sorted set we match/summarize over;
796
+ # `is_combined` gates the multi-label prompt and response labelling.
797
+ if payload.labels:
798
+ combined_labels = sorted({int(l) for l in payload.labels})
799
+ else:
800
+ combined_labels = [int(payload.label)]
801
+ is_combined = len(combined_labels) > 1
802
+
803
  # Client-supplied path: the dataset is in the user's browser, not cached
804
  # on the server. The frontend has already filtered rows by (label, period,
805
  # sections) and sent the relevant explanation snippets. Skip the server
 
840
  if client_snippets is not None:
841
  snippets = client_snippets
842
  # We don't know the unmatched count on the client path; use the count
843
+ # the client reported. On this path each reported snippet is one
844
+ # non-empty explanation, so the "with explanation text" count is just
845
+ # the snippet count.
846
  match_count_for_response = matched_count or len(snippets)
847
+ rows_with_expl = len(snippets)
848
  else:
849
+ # Filter to rows that contain any of the requested label(s). For a
850
+ # single-label request this is just `payload.label`; for a combined
851
+ # request it's any of the covered labels.
852
+ label_set = set(combined_labels)
853
+ mask = df["Labels"].apply(
854
+ lambda v: bool(label_set.intersection(_parse_labels_field(v)))
855
+ )
856
 
857
  # Never summarize hidden structural sections, even if the union-find
858
  # grouped_idx lumped one in with a visible section the user selected.
 
887
  ),
888
  }
889
 
890
+ # Pull the relevant Explanation snippet(s) for each matched row. For a
891
+ # combined request, gather every covered label's snippet the row
892
+ # carries so the summary spans all edit types in the cell.
893
  snippets = []
894
+ rows_with_expl = 0
895
  for _, row in matched.iterrows():
896
+ raw_expl = row.get("Explanation")
897
+ row_had_snippet = False
898
+ for lbl in combined_labels:
899
+ text = _explanation_for_label(raw_expl, lbl)
900
+ if text:
901
+ snippets.append(text)
902
+ row_had_snippet = True
903
+ if row_had_snippet:
904
+ rows_with_expl += 1
905
 
906
  if not snippets:
907
  return {
 
946
  if payload.period_key
947
  else "across the article's full revision history"
948
  )
949
+
950
+ if is_combined:
951
+ # Combined summary: one write-up spanning every scientific edit type
952
+ # present in the cell. The scope is the union of the covered labels'
953
+ # definitions rather than a single one, so the model synthesizes
954
+ # across edit types instead of being told to omit "other labels".
955
+ covered_names = [
956
+ label_name_map.get(l, f"Label {l}") for l in combined_labels
957
+ ]
958
+ definitions_block = "\n\n".join(
959
+ f"'{label_name_map.get(l, f'Label {l}')}' (label {l}):\n"
960
+ f"{_label_definition(l, payload.article)}"
961
+ for l in combined_labels
962
+ )
963
+ response_label_name = "All scientific edit types"
964
+ user_prompt = (
965
+ f"You are summarizing Wikipedia revision edits for the article "
966
+ f"'{payload.article}'.\n\n"
967
+ f"Below are explanations from {len(snippets_for_prompt)} edit "
968
+ f"observations {period_note}, {section_note}. They span every "
969
+ f"scientific edit type present in this selection: "
970
+ f"{', '.join(covered_names)}.\n\n"
971
+ f"Definitions of the covered edit types:\n{definitions_block}\n\n"
972
+ f"Write a tight, scannable summary as 3–6 short bullets that "
973
+ f"synthesize what changed across ALL of these edit types together. "
974
+ f"Each bullet must:\n"
975
+ f"- Start with '- '.\n"
976
+ f"- Be one sentence, ideally under 20 words.\n"
977
+ f"- Cover a distinct theme or grouping — do NOT restate the same "
978
+ f"point twice and do NOT enumerate every edit.\n"
979
+ f"- Lead with the substance, not filler (avoid openers like "
980
+ f"'This edit…' or 'The article…').\n\n"
981
+ f"Scope rules read carefully:\n"
982
+ f"- Summarize the edits as a whole. Group related changes across "
983
+ f"edit types; you do not need one bullet per edit type.\n"
984
+ f"- Only discuss evidence that satisfies at least one of the "
985
+ f"definitions above. Ignore anything in the explanations that "
986
+ f"falls outside all of them.\n\n"
987
+ f"Formatting requirements:\n"
988
+ f"- Use Markdown **bold** (double asterisks) only around concrete "
989
+ f"evidence — e.g. the specific technical terms, references, "
990
+ f"wikilinks, numeric values, or researcher names mentioned in the "
991
+ f"explanations. Bold only the evidence phrase itself, not the "
992
+ f"surrounding prose.\n"
993
+ f"- After the bullets, add a separate final line beginning with "
994
+ f"'Bottom line: ' that gives a one-sentence takeaway about what "
995
+ f"these edits collectively did to the article during this period.\n"
996
+ f"- Do not output any prose outside the bullets and the Bottom "
997
+ f"line line. No headings, no intro sentence, no closing remark.\n\n"
998
+ f"Explanations:\n"
999
+ + "\n\n".join(f"- {s}" for s in snippets_for_prompt)
1000
+ )
1001
+ else:
1002
+ response_label_name = label_name
1003
+ user_prompt = (
1004
+ f"You are summarizing Wikipedia revision edits for the article "
1005
+ f"'{payload.article}'.\n\n"
1006
+ f"Below are explanations from {len(snippets_for_prompt)} edits "
1007
+ f"classified as '{label_name}' (label {payload.label}) {period_note}, "
1008
+ f"{section_note}.\n\n"
1009
+ f"Definition of '{label_name}':\n{label_definition}\n\n"
1010
+ f"Write a tight, scannable summary as 3–6 short bullets that "
1011
+ f"synthesize the patterns. Each bullet must:\n"
1012
+ f"- Start with '- '.\n"
1013
+ f"- Be one sentence, ideally under 20 words.\n"
1014
+ f"- Cover a distinct theme or grouping — do NOT restate the same "
1015
+ f"point twice and do NOT enumerate every edit.\n"
1016
+ f"- Lead with the substance, not filler (avoid openers like "
1017
+ f"'This edit…' or 'The article…').\n\n"
1018
+ f"Scope rules — read carefully:\n"
1019
+ f"- Only discuss evidence that directly satisfies the definition of "
1020
+ f"'{label_name}' given above. Do NOT mention edits or details that "
1021
+ f"appeared in the explanations but fall outside this definition "
1022
+ f"(e.g. for an 'Academic References Added' summary, do not mention "
1023
+ f"named-reference tags, formatting cleanup, or non-academic links — "
1024
+ f"the definition is specifically about new academic papers with "
1025
+ f"identifiers like DOI/PMID/PMC/arXiv/ISSN).\n"
1026
+ f"- If something feels noteworthy but doesn't match the definition, "
1027
+ f"omit it. It will be covered by a different label's summary.\n\n"
1028
+ f"Formatting requirements:\n"
1029
+ f"- Use Markdown **bold** (double asterisks) only around concrete "
1030
+ f"evidence that directly satisfies the definition above — e.g. the "
1031
+ f"specific technical terms, references, wikilinks, numeric values, "
1032
+ f"or researcher names mentioned in the explanations. Bold only the "
1033
+ f"evidence phrase itself, not the surrounding prose. Do not bold "
1034
+ f"anything that isn't itself an instance of the label.\n"
1035
+ f"- After the bullets, add a separate final line beginning with "
1036
+ f"'Bottom line: ' that gives a one-sentence takeaway about what "
1037
+ f"these edits collectively did to the article during this period.\n"
1038
+ f"- Do not output any prose outside the bullets and the Bottom line "
1039
+ f"line. No headings, no intro sentence, no closing remark.\n\n"
1040
+ f"Explanations:\n"
1041
+ + "\n\n".join(f"- {s}" for s in snippets_for_prompt)
1042
+ )
1043
 
1044
  try:
1045
  import openai
 
1065
  return {
1066
  "summary": summary_text.strip(),
1067
  "edit_count": match_count_for_response,
1068
+ # Rows that contributed at least one explanation snippet. In combined
1069
+ # mode a single row may contribute several snippets, so this stays a
1070
+ # per-row count rather than the raw snippet total.
1071
+ "explanation_count": rows_with_expl,
1072
  "truncated": truncated,
1073
  "label": payload.label,
1074
+ "labels": combined_labels if is_combined else None,
1075
+ "label_name": response_label_name,
1076
  "period_key": payload.period_key,
1077
  }
1078
 
frontend_build/asset-manifest.json CHANGED
@@ -1,7 +1,7 @@
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.ccff2033.css",
4
- "main.js": "/static/js/main.caab1ec2.js",
5
  "static/js/206.b8423ec3.chunk.js": "/static/js/206.b8423ec3.chunk.js",
6
  "static/media/KaTeX_AMS-Regular.ttf": "/static/media/KaTeX_AMS-Regular.853be92419a6c3766b9a.ttf",
7
  "static/media/KaTeX_Main-Regular.ttf": "/static/media/KaTeX_Main-Regular.9eba1d77abcf2aa6e94e.ttf",
@@ -67,6 +67,6 @@
67
  },
68
  "entrypoints": [
69
  "static/css/main.ccff2033.css",
70
- "static/js/main.caab1ec2.js"
71
  ]
72
  }
 
1
  {
2
  "files": {
3
  "main.css": "/static/css/main.ccff2033.css",
4
+ "main.js": "/static/js/main.8c4ac153.js",
5
  "static/js/206.b8423ec3.chunk.js": "/static/js/206.b8423ec3.chunk.js",
6
  "static/media/KaTeX_AMS-Regular.ttf": "/static/media/KaTeX_AMS-Regular.853be92419a6c3766b9a.ttf",
7
  "static/media/KaTeX_Main-Regular.ttf": "/static/media/KaTeX_Main-Regular.9eba1d77abcf2aa6e94e.ttf",
 
67
  },
68
  "entrypoints": [
69
  "static/css/main.ccff2033.css",
70
+ "static/js/main.8c4ac153.js"
71
  ]
72
  }
frontend_build/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#0e7490"/><meta name="description" content="Wikistar — understand how scientific revisions evolve over time for a specific Wikipedia page."/><link rel="apple-touch-icon" href="/wikistar-192.png"/><link rel="manifest" href="/manifest.json"/><title>Wikistar — Wikipedia Edit History Explorer</title><script defer="defer" src="/static/js/main.caab1ec2.js"></script><link href="/static/css/main.ccff2033.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
 
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#0e7490"/><meta name="description" content="Wikistar — understand how scientific revisions evolve over time for a specific Wikipedia page."/><link rel="apple-touch-icon" href="/wikistar-192.png"/><link rel="manifest" href="/manifest.json"/><title>Wikistar — Wikipedia Edit History Explorer</title><script defer="defer" src="/static/js/main.8c4ac153.js"></script><link href="/static/css/main.ccff2033.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
frontend_build/static/js/{main.caab1ec2.js → main.8c4ac153.js} RENAMED
The diff for this file is too large to render. See raw diff
 
frontend_build/static/js/{main.caab1ec2.js.LICENSE.txt → main.8c4ac153.js.LICENSE.txt} RENAMED
File without changes