fix(semantic): neutralize image data URIs before remote embedding requests
Linked issue
Approved issue: #817
What changes
embed_texts sends chunk text verbatim as the embeddings input. VLM-backed
embedding deployments — NVIDIA's current nemotron-*embed* models, for
example — parse data:image/<mime>;base64 anywhere in an input string as an
image part and reject the whole request when VLM serving is disabled:
HTTP 400 image inputs require VLM serving to be enabled on this server
Source code legitimately builds such strings (image_url=f"data:image/png;base64,{...}",
screenshot helpers, docstrings naming the format), so one chunk killed its
batch and, with it, the entire semantic build — repeatedly, from chunk 0, every
session.
This change neutralizes the data:image/ prefix to data-uri:image/ in the
OpenAI-compatible request body only:
- the persisted chunk text is unchanged, so search snippets and provenance keep
the original bytes; fastembedandollamapayloads are unchanged, since neither interprets data
URIs;text/plain,application/json,audio/*data URIs and the uppercase form
are left alone — they already embed successfully against the same deployment,
so rewriting them would only perturb vectors;- the helper borrows when the prefix is absent, so the common row costs one
substring scan and no allocation.
The trigger is a literal, case-sensitive prefix and the backend does not
validate the payload, so a plain str::replace is enough; no regex is needed
on the request path.
embed_texts is the single request builder for both build batches and
interactive query embeds, so both are covered.
How it was checked
cargo fmt --check: clean.- Unit tests (
image_data_uri_tests, 7 passed / 0 failed): borrowed
pass-through, source-line template literal, multiple occurrences per row,
docstring mention without a payload, non-image data URIs untouched, uppercase
untouched, and a serialized body assertion that nodata:image/survives.
Run in the debug profile:cargo test -p agent-file-tools --lib image_data_uri_tests.
cargo test --releasecannot build this crate's lib tests at this commit
becausestorage_retention::test_volume_uuid_guardis#[cfg(debug_assertions)];
that is unrelated to this change. - End-to-end against
https://integrate.api.nvidia.com/v1with
nvidia/nemotron-3-embed-1b, on a one-file repository containing
f"data:image/png;base64,{payload.decode('ascii')}", viaaft index:- stock 0.62.0:
kind=semantic status=gap usable=false reason="openai compatible request failed (HTTP 400 Bad Request): {\"message\": \"image inputs require VLM serving to be enabled on this server\" ...}", exit 2; - patched:
kind=semantic status=success result_count=4, exit 0.
- stock 0.62.0:
- Real corpus, both roots that triggered the report, same NVIDIA backend, built
throughaft indexinto the same storage the plugin uses:/mnt/s/ComfyUI:kind=search status=success result_count=1222,
kind=semantic status=success result_count=20651(the stock binary failed
this root at batch 134/326 with the 400);/go/career-ops:kind=search status=success result_count=3455,
kind=semantic status=success result_count=3850.
Exit 0, no build-breaker suspensions recorded afterwards.
Notes for review
- No configuration knob: the rewrite is semantically neutral for an embedder and
applies only to text that no longer matches the data-URI grammar. SynapseandOllamapayload builders are untouched; if a future backend is
known to parse data URIs, the helper is one call away.
AI assistance disclosure: this submission was produced with an AI coding agent
(DeepSeek V4.1 Flash driving an autonomous agent session). The agent reproduced
the failure end-to-end, wrote the patch and tests, and ran the verification
commands listed above; a human operator reviewed the diagnosis and directed the
approach. Review the diff with that in mind.
Summary by cubic
Fixes semantic embedding failures where VLM-backed deployments (e.g. NVIDIA's nemotron embed) reject chunks containing data:image/ prefixes as image inputs, returning HTTP 400 and breaking the whole build.
- Rewrites
data:image/todata-uri:image/only in the OpenAI-compatible request body; persisted chunk text,fastembedandollamapayloads, and non-image data URIs stay byte-identical. - The rewrite borrows when the prefix is absent, so the common row costs one substring scan and no allocation.
- Adds unit tests covering borrowed pass-through, multiple occurrences, docstring mentions, non-image URIs, and serialized request body.