8000 Let the holistic judge show Commons images by daoudclarke · Pull Request #616 · mwmbl/mwmbl · GitHub
[go: up one dir, main page]

Skip to content

Let the holistic judge show Commons images - #616

Merged
daoudclarke merged 6 commits into
seed-images-eval-gatefrom
seed-images-eval-judge-images
Nov 10, 2026
Merged

daoudclarke merged 6 commits into
seed-images-eval-gatefrom
seed-images-eval-judge-images

Conversation

@daoudclarke
@daoudclarke daoudclarke commented Nov 9, 2026 •
Copy link
Copy Markdown
Contributor

Stack

PR 4 of 7 in the #610 evaluation stack: #611 ← #612 ← #613 ← #616 ← #615 ← #617 ← #614. Merge in that order.

I split this out of #615 so each PR stays under 500 added lines.

What changed

mwmbl/rankeval/evaluation/holistic_judge.py can now show Wikimedia Commons pictures in a list:

  • Strip: one entry, rendered as [IMAGES from Wikimedia Commons] 1. <file name> — <description> | 2. …. Descriptions are cut to 150 characters. A thumbnail path is added when one is given.
  • Single picture: {"url", "title", "extract", "image": true}, rendered as [IMAGE] <file name> — <description>.
  • Counting: a strip doesn't count towards the ten results unless it is marked "displaces": true.
  • Prompt notes: only batches that show images get a paragraph saying pictures may appear and asking the judge whether they help the query. The clause saying a strip adds to the results is dropped when no strip in the batch adds. Batches with thumbnails also get a line telling the judge to Read them.
  • Output path: every batch now ends by naming the absolute path its judge writes to.

Results: what the judge sees

List entry Judge sees Counts towards ten
Web result unchanged yes
Strip [IMAGES from Wikimedia Commons] 1. <file name> — <description> | 2. … only with "displaces": true
Single image [IMAGE] <file name> — <description> yes
Thumbnails (vision batches) the strip's thumbnail paths, and a line telling the judge to Read them –

The rubric is otherwise unchanged. Batches without images get the same prompt as before, apart from the output-path line. #615 uses this to judge the arms.

How verified

  • make check passes.
  • test/test_holistic_judge.py passes 12 tests, three of them new. They cover strip and single-image rendering, a strip not counting towards ten, a displacing strip counting towards ten, and the image notes appearing only in batches that show images.
  • A code-review subagent reviewed the change.
  • Coverage: mwmbl/rankeval/seed_images/* is omitted from coverage (set in Add a harness to evaluate images in Seed Search #611), because it is offline evaluation code.

Decisions already made

  • Only images that pass the Jev NSFW check (score below 1.0) are shown or indexed. There is no additional pixel check behind it.

Nothing outside mwmbl/rankeval imports this code, so production is unchanged.

Part of #610

🤖 Generated with Claude Code

A list may now hold a strip of Commons pictures, shown as one entry
`[IMAGES from Wikimedia Commons] 1. <file> — <description> | 2. ...`,
or a single picture shown as `[IMAGE] <file> — <description>`. A strip
adds to the ten results unless it is marked as displacing one. Only
batches that show images get a paragraph telling the judge about them
and to judge whether they help the query, plus a line to open the
thumbnails when there are any; the rubric is otherwise unchanged. Every
batch now names the absolute path its judge writes to.

Part of #610

No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov
codecov Bot commented Nov 9, 2026
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

"extract"} object. Only the top ten count. X is the arm under test and Y the baseline, so
a positive preference favours X.

A list may also hold Wikimedia Commons pictures. A single picture, {"url", "title", "extract",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be careful about properly escaping title/description. These are random-user-entered values and potentially can cause XSS attacks etc.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reply from Claude: Changed in 7503d71 and d6ef9d9. There's no XSS here: this text only goes into a prompt file that an LLM reads offline, and nothing renders it as HTML. The two fields should still be normalised the same way, and now they are. Commons titles and descriptions both go through a new commons_text. It decodes HTML entities and also strips any tags that only appear once those entities are decoded (e.g. &lt;script&gt;). Web extracts are handled as before. On the measured data, no Commons title or description contained a tag or entity, so this changes nothing there.

Stripping markup doesn't stop a description that addresses the judge. In this offline eval such a description could sway at most one verdict, since each comparison is judged in both orders. For production, the front end must render Commons text escaped and never as HTML, and that's in the doc's production notes.

@blueheron786
blueheron786 commented Nov 9, 2026 •
Copy link
Copy Markdown
Collaborator

What happens if we show a Wikipedia entry, too? Will we see the Wikipedia entry + image (if it has one), and then potentially a strip or single image?


NUM_RESULTS = 10
EXTRACT_CHARS = 400
IMAGE_DESCRIPTION_CHARS = 150
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we chunking properly and not stopping mid-word or mid-sentence?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reply from Claude: Fixed in 7503d71. shortened() cuts at the last sentence end within the 150-character limit if one falls in its second half. Otherwise it cuts at the last word boundary and adds "…". The measured runs in #615 used the old mid-word cut: 19 of A2's 59 strips and 37 of A1's 191 images would now read differently. I haven't judged them again, because the change only trims a partial last word or sentence, and the doc says the runs used the old text.

@blueheron786
Copy link
Copy Markdown
Collaborator

Review: PR #616 — Let the holistic judge show Commons images

I found one definite issue in the diff, plus two areas worth tightening before merging.

  1. [P1] Escape untrusted image metadata before rendering it

The reviewer's security concern is valid.

image_text() inserts Wikimedia Commons titles and descriptions directly into the judge's input:

python
return f"{image['title']} — {description or '(no description)'}{thumbnail}"

These values originate outside the trusted application.

Even though descriptions have HTML tags stripped, this isn't a complete sanitisation strategy. In particular, HTML entities are decoded afterwards, and titles aren't processed by plain_text() at all.

I'd recommend consistently normalising both fields and ensuring that untrusted metadata cannot introduce unintended markup or instructions into the judge's input.

One important distinction: this is primarily an untrusted-input/prompt-integrity concern in this context; HTML escaping alone is not a complete defence against prompt injection.

  1. [P2] Description truncation can cut off useful information

Currently:

python
description = " ".join(plain_text(image["description"]).split())[:IMAGE_DESCRIPTION_CHARS]

The 150-character limit is sensible, but slicing can stop halfway through a word or sentence.

For example, a description might contain the subject in its final few words, which would be lost.

I'd truncate at the last word boundary within the limit. Ideally, prefer a sentence boundary where practical.

This also addresses the reviewer's question about chunking.

  1. [P2] Image-only changes might not trigger a comparison

In differing_queries():

python
def top_urls(results: list[dict]) -> list[str]:
return [result["url"] for result in top_results(results)]

Comparisons are selected based on URL differences alone.

This is potentially problematic for image strips: if two arms have the same synthetic strip URL but different images or descriptions, the query won't be judged.

I'd compare the actual rendered content of image entries as well as URLs.

This is particularly important for an evaluation intended to measure the quality of image additions.

daoudclarke and others added 4 commits November 9, 2026 21:35
…ontent

Commons titles now go through the same plain-text normalisation as
descriptions, and entities are decoded before tags are stripped, so an
encoded tag is stripped too. Descriptions are shortened at a sentence end
or word boundary instead of mid-word. Image entries are compared by what
the judge is shown, so two strips with one URL but different pictures
still count as differing.

The measured runs are unaffected in which queries they compare; 19 of
A2's 59 strips would read differently because of the new shortening.

No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… file

Decoding entities before stripping tags also cut text out of web extracts,
so "a &lt; b and c &gt; d" lost everything between the brackets. Web
extracts go back to stripping tags first; only Commons text strips the tags
that decoding reveals, with a pattern that matches tag shapes alone and
can't run quadratically on unclosed brackets. Image entries now compare by
URL as well as text, so two files with the same text still differ.

No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No functional changes to the crawler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@daoudclarke
8000 daoudclarke commented Nov 10, 2026
Copy link
Copy Markdown
Contributor Author

Reply from Claude: Replying to @blueheron786's review above (6751849160).

[P1] Untrusted metadata and [P2] truncation: see the replies on the inline threads. Titles and descriptions now go through commons_text, and descriptions are shortened at sentence or word boundaries.

[P3] Image-only changes. Fixed in 7503d71 and d6ef9d9. Web results are still compared by URL, so earlier Combined Search evals select the same queries. Image entries are now compared by URL plus the text the judge is shown. In the measured runs this doesn't change which queries were compared. Rerunning the selection offline for A1, A2, A2-displace and A2-vision against A0 gives exactly the same queries (118/59/59/20). Every measured comparison was against A0, which has no image entries, and strip URLs are built from the images' page URLs. The one place it would matter is A2-vision against A2, which was never run.

@daoudclarke
Copy link
Copy Markdown
Contributor Author

Reply from Claude: Replying to the Wikipedia question above (6751684614). Combined Search doesn't add Wikipedia separately. Wikipedia pages come back as ordinary Staan results, and nothing deduplicates them against the strip. The front end (routes/search/+page.svelte) shows the #1 result as a WikipediaCard when it's an en.wikipedia.org page, and that card shows the article's lead image. So yes: you'd see the card with its image, then the strip directly under it, since the strip goes after the top result. That happens in 37 of the 59 queries that get a strip, and the lead image may appear in the strip as well. The judge never saw the card's image.

There's now a production note in the doc. Drop from the strip any image that is the card's lead image, matched by the last segment of the summary's originalimage URL. Then judge the card and strip together before deciding whether the strip still helps there.

@daoudclarke
daoudclarke merged commit 0b161ab into wikicommons_image_search Nov 10, 2026
13 checks passed
@daoudclarke
daoudclarke deleted the seed-images-eval-judge-images branch November 10, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0