Impact: Medium (reliability + duplication)
The 429 backoff + batch loop + malformed-response check is near-verbatim across `server/embeddings/voyage.py:72-106`, `openai.py:59-89`, and `jina_api.py:129-151`.
Meanwhile `server/embeddings/jina.py` (self-hosted TEI) and `ollama.py` have no retry at all — a single transient 5xx aborts the file's embedding, and the pipeline then skips the file (leaving it stale).
Recommended change
Extract a shared `_post_batches_with_retry(...)` helper so all five providers share one implementation, and the two self-hosted providers gain retry coverage.
Impact: Medium (reliability + duplication)
The 429 backoff + batch loop + malformed-response check is near-verbatim across `server/embeddings/voyage.py:72-106`, `openai.py:59-89`, and `jina_api.py:129-151`.
Meanwhile `server/embeddings/jina.py` (self-hosted TEI) and `ollama.py` have no retry at all — a single transient 5xx aborts the file's embedding, and the pipeline then skips the file (leaving it stale).
Recommended change
Extract a shared `_post_batches_with_retry(...)` helper so all five providers share one implementation, and the two self-hosted providers gain retry coverage.