11from __future__ import annotations
22
3+ import asyncio
34import logging
45
56import httpx
1213_API_URL = "https://api.voyageai.com/v1/embeddings"
1314# Voyage caps batch size at 128 inputs per request.
1415_BATCH_SIZE = 128
16+ _BACKOFF_DELAYS = [10 , 20 , 30 , 40 ]
1517
1618# Native output dimensions for known models. Some models (voyage-code-3, voyage-3,
1719# voyage-3-large) accept an `output_dimension` API parameter to shrink/grow this;
@@ -67,9 +69,7 @@ async def embed_query(self, text: str) -> list[float]:
6769 vectors = await self ._embed ([text ], input_type = "query" )
6870 return vectors [0 ] if vectors else []
6971
70- async def _embed (
71- self , texts : list [str ], input_type : str
72- ) -> list [list [float ]]:
72+ async def _embed (self , texts : list [str ], input_type : str ) -> list [list [float ]]:
7373 if not texts :
7474 return []
7575 all_vectors : list [list [float ]] = []
@@ -82,7 +82,18 @@ async def _embed(
8282 }
8383 if self ._dims_override is not None :
8484 body ["output_dimension" ] = self ._dims_override
85- resp = await self ._client .post (_API_URL , json = body )
85+ for attempt in range (4 ):
86+ resp = await self ._client .post (_API_URL , json = body )
87+ if resp .status_code != 429 :
88+ break
89+ retry_after = float (resp .headers .get ("Retry-After" , 0 ))
90+ wait = retry_after if retry_after > 0 else _BACKOFF_DELAYS [attempt ]
91+ logger .warning (
92+ "Voyage rate-limited (429) — retrying in %.0fs (attempt %d/4)" ,
93+ wait ,
94+ attempt + 1 ,
95+ )
96+ await asyncio .sleep (wait )
8697 resp .raise_for_status ()
8798 data = resp .json ()
8899 batch_vectors = [item ["embedding" ] for item in data .get ("data" , [])]
0 commit comments