fix: unbreak document ingestion (docling to_formats) and OpenRouter chat model ids - #356
fix: unbreak document ingestion (docling to_formats) and OpenRouter chat model ids#356Deodat-Lawson wants to merge 2 commits into
Conversation
docling-serve validates each to_formats entry against its OutputFormat
enum. Sending the JSON string '["md"]' makes the whole string arrive as
a single, bogus enum member, so every conversion failed:
{"detail":[{"type":"enum","loc":["body","to_formats",0],
"msg":"Input should be 'md', 'json', 'yaml', 'html', ..."}]}
The converter surfaced that as a typed 502 docling-failed, the worker
retried three times and parked the outbox event, and the document
produced zero chunks. Retrieval then found nothing and the QA route
answered "No relevant content found for the given question" — identical
to the message for an empty workspace, which made a broken ingestion
pipeline look like an empty one.
Verified against the running service: '["md"]' returns 422 and "md"
returns 200 with the expected md_content. After the fix the parked event
processed on first retry and produced 8 embedded retrieval chunks.
The unit test asserted the JSON-array form, so it locked in the broken
shape and would have blocked this fix in CI. Updated to match.
Nothing in this repo changed to cause the break: docker-compose.yml
pins docling-serve to an implicit `latest`, so the image moved under us.
Pinning it is left as a separate decision.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
chat-models.yaml declared bare Gemini ids (gemini-2.5-flash) while CHAT_BASE_URL points at https://openrouter.ai/api/v1. Ids are sent to the endpoint verbatim, and OpenRouter does not serve the bare names — it serves google/gemini-2.5-flash — so every chat request 404'd. Only the `id:` fields change. The `preset:` keys already carried the google/ prefix because they name a bundled behavior entry rather than a model on the endpoint, so they were correct as written. Also refreshes the comment above the block, which described the ids as "the BARE ids Google's endpoint expects" and no longer matched. It now explains both directions: keep the prefix for OpenRouter, drop it when talking to Google's own compatibility endpoint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_9404a880-b9e4-4d1d-a834-21430b62e2f6) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b42225d0fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # a bundled behavior entry, not a model on the endpoint. | ||
| primary: | ||
| id: gemini-2.5-flash | ||
| id: google/gemini-2.5-flash |
There was a problem hiding this comment.
Keep bare IDs for the built-in Gemini endpoint
When CHAT_BASE_URL is unset—the supported default path—resolveChatEndpoint still selects Google's compatibility endpoint (apps/web/src/server/chat-endpoint.ts:155-175), and the documented .env.example also points there. This configuration now sends google/gemini-2.5-* verbatim even though that endpoint expects bare Gemini IDs, so primary, fast, and reasoning chat requests fail for default and documented Google deployments. Keep the shipped IDs bare, or change the actual endpoint default and provide a separate configuration for direct Google users.
Useful? React with 👍 / 👎.
Two independent bugs found while bringing the Compose stack up and testing document ingestion end to end. Both broke a core path silently, and both were verified against the running stack rather than reasoned about.
1. Document ingestion was completely broken (
services/document-converter)Every Office/PDF upload failed.
docling-servevalidates eachto_formatsentry against itsOutputFormatenum, but the client sent the JSON string'["md"]', so the entire string arrived as one bogus enum member:{"detail":[{"type":"enum","loc":["body","to_formats",0], "msg":"Input should be 'md', 'json', 'yaml', 'html', ..."}]}The failure chain, and why it was hard to spot:
POST /v1/convert/file→ 422502 docling-failedSo a fully broken ingestion pipeline was indistinguishable from a workspace with no documents. Diagnosing it required querying
pdr_ai_v2_event_outbox.last_errordirectly, because the client discards docling's response body and logs only the status code.Verified both directions against the running service:
'["md"]'→ 422,"md"→ 200 with the expectedmd_content. After the fix the parked event processed on first retry and produced 8 embedded retrieval chunks from a real.docx.The unit test asserted the JSON-array form, so it had pinned the broken shape and would have blocked this fix in CI. Updated to match.
2. Chat 404'd on every request (
apps/web/config/chat-models.yaml)chat-models.yamldeclared bare Gemini ids (gemini-2.5-flash) whileCHAT_BASE_URLpoints athttps://openrouter.ai/api/v1. Ids go to the endpoint verbatim and OpenRouter servesgoogle/gemini-2.5-flash, not the bare name — so every chat request failed.Only the
id:fields change. Thepreset:keys already carried thegoogle/prefix, because they name a bundled behavior entry rather than a model on the endpoint — they were correct as written.The comment above the block described the ids as "the BARE ids Google's endpoint expects" and no longer matched, so it now explains both directions: keep the prefix for OpenRouter, drop it for Google's own compatibility endpoint.
Reviewer notes
docker-compose.yml:240isimage: ghcr.io/docling-project/docling-servewith no tag, so it resolves tolatestand the image moved underneath us. Everydocker compose pullre-rolls that dice. Pinning it to a tag or digest is deliberately not in this PR — it's a separate call about how this project wants to handle upstream image drift, but it's the fix for the whole class of problem.docling.ts. The 422 detail names the exact offending field; discarding it turned a one-line bug into a database-archaeology session.services/*is outside the pnpm workspace, so its vitest suite was not run locally — thedocument-converterCI job covers it. The behavioral fix was verified directly against the live service instead.Related: #355 (the empty-state message that made this so hard to diagnose).
Note
Medium Risk
Touches document conversion and default chat model routing—core user paths—but the edits are small, well-scoped contract/config fixes with an updated unit assertion.
Overview
Restores two broken core paths: document ingestion and default chat.
Ingestion:
docling-servenow receivesto_formatsas the enum valuemdinstead of the JSON string["md"], which was rejected as 422 and left uploads looking like an empty workspace. The converter unit test is updated to pin the correct form field.Chat: Default Gemini
ids inchat-models.yamlget OpenRouter’sgoogle/prefix so they matchCHAT_BASE_URL. Presets are unchanged. Comments now say to drop the prefix if talking to Google’s own compatibility endpoint.Reviewed by Cursor Bugbot for commit b42225d. Bugbot is set up for automated code reviews on this repo. Configure here.