Skip to content

embeddings: generate-embeddings-saturate.py, a vLLM server recipe for large runs - #190

Merged
davanstrien merged 5 commits into
mainfrom
embeddings/saturate-recipe
Nov 10, 2026
Merged

davanstrien merged 5 commits into
mainfrom
embeddings/saturate-recipe

Conversation

@davanstrien

@davanstrien davanstrien commented Nov 10, 2026 •

Copy link
Copy Markdown
Owner

A second embeddings recipe for large text runs. generate-embeddings.py stays the default.

What it does

  • Starts a vLLM server in the Job on the pinned vllm/vllm-openai:v0.30.0 image, and sends it one text per request with saturate.
  • The [tool.hf-jobs] header sets the image, a10g-small, a 2h timeout and HF_TOKEN, so the command needs no job flags.
  • Writes one row per input row (id, the text column, embeddings, model, prompt_tokens, error). A run can be resumed by running the same command again.
  • Cuts each text at the model's own maximum, which the server reports, unless --max-seq-len is set. A value above the model's maximum is lowered to it.
  • Picks the prompt as generate-embeddings.py does: --prompt, then the model's registered document prompt, then the known-family table.
  • Writes the dataset card with the standard reproduction section and the uv-script and hf-jobs tags.

generate-embeddings-vllm.py stays in the folder. Its docstring and the README row now name this recipe as its successor.

Measured

One A10G, 20,000 IMDb reviews of up to 300 words, embedding step only, one run each.

Model generate-embeddings.py this recipe
Qwen/Qwen3-Embedding-0.6B 96 rows/s 187 rows/s
BAAI/bge-m3 71 rows/s 276 rows/s

The server was ready 100 to 120 seconds after it started in those runs, so generate-embeddings.py is quicker for a few hundred rows.

Checks

100-row smoke Jobs, each with 100 of 100 rows, 0 error rows, unit-length vectors and 100 unique ids:

  • Default model, header only, on a10g-small: cut at the model maximum (32768), longest text 1164 tokens.
  • Default model on l4x1 (an earlier version with a fixed 512-token cut).
  • intfloat/multilingual-e5-small with --max-seq-len 100000: lowered to 512, prefix passage: .
  • BAAI/bge-m3 with --max-seq-len 128: longest text 128 tokens. This run used the committed file.

Records are in maintenance/verifications.jsonl. tools/build-catalog.py --verify-models and tools/recipe-status.py --check pass.

Review

A Codex review found four issues outside the default path. All are fixed in 16457ba:

  • A resumed run that processed no rows wrote a card with dim None and 0 embedded. The dimension now comes from the server, and the card gives the rows of the last run and the rows written earlier.
  • --id-column index and --id-column content were read as saturate id strategies. They are passed as a callable, as in lighton-ocr3-saturate.py.
  • The card's reproduce command now carries every option that changes the result.
  • The docstring says that a --retry-errors run keeps the old error record, and how to read one record per id.

Tested after the fix: the committed file run twice on the same output. The fresh run wrote 100 of 100 rows. The resumed run processed 0 rows, left 100 rows with 100 unique ids, and kept a valid card. The --id-column fix was not run on a Job.

Notes

  • At the end of a run the vLLM log prints an EngineDeadError traceback. It appears after the last row is written, when the server is stopped.
  • Both this PR and embeddings: header on generate-embeddings.py, and nine more declared models #189 append to maintenance/verifications.jsonl and regenerate maintenance/STATUS.md. The second one to merge needs a merge from main first.
  • With this recipe, Qwen/Qwen3-Embedding-0.6B is declared on the Run on Jobs Space (30 to 31 models on main).

🤖 Generated with Claude Code

https://claude.ai/code/session_016isSPpNH4MzMcKDBpSwokh

davanstrien and others added 5 commits November 10, 2026 17:23
… large runs

The script starts a vLLM server on the pinned vllm/vllm-openai:v0.30.0
image and sends it one text per request with saturate. The output has one
row per input row and a run can be resumed.

On one A10G with 20,000 IMDb reviews, embedding step only, one run each:
Qwen3-Embedding-0.6B 187 rows/s against 96 for generate-embeddings.py,
and bge-m3 276 rows/s against 71.

Texts are cut at the model's own maximum, which the server reports, unless
--max-seq-len is set. Prompts follow generate-embeddings.py: --prompt, then
the model's registered document prompt, then the known-family table.

generate-embeddings-vllm.py stays, and its docstring names this recipe as
its successor.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016isSPpNH4MzMcKDBpSwokh
Four 100-row runs: the default model on a10g-small and l4x1, an e5 model
with a cut above its maximum, and bge-m3 with --max-seq-len 128.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016isSPpNH4MzMcKDBpSwokh
- A resumed run that processed no rows wrote a card with "dim None" and
  "0 embedded". The dimension now comes from one request to the server,
  and the card gives the rows of the last run and the rows written before.
- --id-column index and --id-column content were read as saturate id
  strategies, not as column names. They are now passed as a callable, as
  in lighton-ocr3-saturate.py.
- The card's reproduce command now carries the options that change the
  result: config, split, id column, sample limit, text cut, prompt, query
  mode and output column.
- The docstring says that a --retry-errors run keeps the old error record,
  and how to read one record per id.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016isSPpNH4MzMcKDBpSwokh
…s-saturate.py

A fresh run and a resumed run of the committed file on the same output.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016isSPpNH4MzMcKDBpSwokh
# Conflicts:
#	maintenance/STATUS.md
#	maintenance/verifications.jsonl
@davanstrien
davanstrien merged commit 9455eca into main Nov 10, 2026
1 check passed
@davanstrien
davanstrien deleted the embeddings/saturate-recipe branch November 10, 2026 17:57
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.

1 participant