Skip to content

Commit 6fdfe25

Browse files
Fix: add retry logic to LiteLLM embedding for transient 500 errors (#396)
* Add retry logic to LiteLLM embedding calls for transient errors The embedding path lacked num_retries, causing OpenAI 500 errors to propagate as unhandled exceptions. The completion path already uses num_retries=3; this aligns embedding to match. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix CI docker-build by adding PostgreSQL and Redis containers The test was running the Morphik container standalone without any database or cache, causing the entrypoint to timeout waiting for PostgreSQL. Now spins up pgvector and Redis containers in a shared Docker network, fixes the POSTGRES_URI scheme, and disables ColPali to avoid loading ML models in CI. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Fix MORPHIK_EMBEDDING_API_DOMAIN validation error in CI docker-build The Settings model expects a list[str] for MORPHIK_EMBEDDING_API_DOMAIN but the CI test config provided a plain string. Fixed by: 1. Using a list in the CI test TOML config 2. Adding a defensive str-to-list coercion in config.py for robustness Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5b46a55 commit 6fdfe25

3 files changed

Lines changed: 53 additions & 7 deletions

File tree

.github/workflows/docker-build.yml

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,11 @@ jobs:
182182
colpali_pdf_dpi = 150
183183
184184
[morphik]
185-
enable_colpali = true
185+
enable_colpali = false
186186
mode = "self_hosted"
187187
use_local_env = true
188188
api_domain = "api.morphik.ai"
189-
morphik_embedding_api_domain = "http://localhost:6000"
189+
morphik_embedding_api_domain = ["http://localhost:6000"]
190190
colpali_mode = "local"
191191
192192
[pdf_viewer]
@@ -203,9 +203,47 @@ jobs:
203203
max_local_bytes = 1073741824
204204
EOF
205205
206+
# Create a Docker network for the test
207+
docker network create test-net
208+
209+
# Start PostgreSQL container with pgvector
210+
PG_CONTAINER=$(docker run -d --name postgres --network test-net \
211+
-e POSTGRES_USER=morphik \
212+
-e POSTGRES_PASSWORD=morphik \
213+
-e POSTGRES_DB=morphik \
214+
pgvector/pgvector:pg16)
215+
216+
# Start Redis container
217+
REDIS_CONTAINER=$(docker run -d --name redis --network test-net redis:7-alpine)
218+
echo "Started Redis container: $REDIS_CONTAINER"
219+
220+
echo "Started PostgreSQL container: $PG_CONTAINER"
221+
222+
# Wait for PostgreSQL to be ready
223+
pg_timeout=30
224+
pg_elapsed=0
225+
echo "Waiting for PostgreSQL to be ready..."
226+
while [ $pg_elapsed -lt $pg_timeout ]; do
227+
if docker exec postgres pg_isready -U morphik -d morphik > /dev/null 2>&1; then
228+
echo "✅ PostgreSQL is ready"
229+
break
230+
fi
231+
sleep 1
232+
pg_elapsed=$((pg_elapsed + 1))
233+
done
234+
235+
if [ $pg_elapsed -ge $pg_timeout ]; then
236+
echo "❌ PostgreSQL failed to start within ${pg_timeout} seconds"
237+
docker logs postgres
238+
docker rm -f postgres redis
239+
docker network rm test-net
240+
exit 1
241+
fi
242+
206243
# Start container in detached mode with config mounted
207-
CONTAINER_ID=$(docker run -d -p 8000:8000 \
208-
-e POSTGRES_URI="postgresql://morphik:morphik@localhost:5432/morphik" \
244+
CONTAINER_ID=$(docker run -d --network test-net -p 8000:8000 \
245+
-e POSTGRES_URI="postgresql+asyncpg://morphik:morphik@postgres:5432/morphik" \
246+
-e PGPASSWORD="morphik" \
209247
-v "$(pwd)/morphik.toml.test:/app/morphik.toml" \
210248
"$IMAGE_TAG")
211249
@@ -235,6 +273,8 @@ jobs:
235273
docker logs "$CONTAINER_ID"
236274
docker stop "$CONTAINER_ID"
237275
docker rm "$CONTAINER_ID"
276+
docker rm -f postgres redis
277+
docker network rm test-net
238278
exit 1
239279
fi
240280
@@ -247,11 +287,15 @@ jobs:
247287
docker logs "$CONTAINER_ID"
248288
docker stop "$CONTAINER_ID"
249289
docker rm "$CONTAINER_ID"
290+
docker rm -f postgres redis
291+
docker network rm test-net
250292
exit 1
251293
fi
252294
253295
# Clean up
254-
echo "🧹 Cleaning up container"
296+
echo "🧹 Cleaning up containers"
255297
docker stop "$CONTAINER_ID"
256298
docker rm "$CONTAINER_ID"
299+
docker rm -f postgres redis
300+
docker network rm test-net
257301
echo "✅ Test completed successfully"

core/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ def get_settings() -> Settings:
372372
api_domain = config["morphik"].get("api_domain", "api.morphik.ai")
373373
# morphik_embedding_api_domain is always a list of endpoints
374374
embedding_api_endpoints = config["morphik"].get("morphik_embedding_api_domain", [f"https://{api_domain}"])
375+
if isinstance(embedding_api_endpoints, str):
376+
embedding_api_endpoints = [embedding_api_endpoints]
375377
secret_manager = config["morphik"].get("secret_manager", "env")
376378

377379
settings_dict.update(

core/embedding/litellm_embedding.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ async def embed_documents(self, texts: List[str]) -> List[List[float]]:
7777
# Use a harmless placeholder; some LiteLLM providers demand a key even if backend ignores it
7878
model_params["api_key"] = get_settings().LITELLM_DUMMY_API_KEY
7979

80-
# Call LiteLLM
81-
response = await litellm.aembedding(input=texts, **model_params)
80+
# Call LiteLLM with retries for transient provider errors (e.g. OpenAI 500s)
81+
response = await litellm.aembedding(input=texts, num_retries=3, **model_params)
8282

8383
embeddings = [data["embedding"] for data in response.data]
8484

0 commit comments

Comments
 (0)