fix(frontend): accept empty image URLs with UUIDs - #12208
Conversation
WalkthroughThe chat-completions handler now uses a dedicated parser that normalizes cache-only image parts with empty URLs and UUIDs. Tests cover acceptance with a UUID and rejection without one. ChangesChat completion normalization
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/llm/src/http/service/openai.rs (1)
3831-3841: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the normalization contract in the acceptance test.
The test verifies that the UUID survives serialization but does not assert that
image_urlwas rewritten tonull, which is the core behavior under test.Suggested assertion
- assert_eq!( - serde_json::to_value(request).expect("request should serialize")["messages"][0]["content"] - [0]["uuid"], - "image-42" - ); + let request = serde_json::to_value(request).expect("request should serialize"); + assert_eq!(request["messages"][0]["content"][0]["uuid"], "image-42"); + assert_eq!( + request["messages"][0]["content"][0]["image_url"], + serde_json::Value::Null + );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/llm/src/http/service/openai.rs` around lines 3831 - 3841, Update test_parse_chat_completion_request_accepts_empty_image_url_with_uuid to also assert that the serialized content item's image_url field is null, while preserving the existing UUID assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/llm/src/http/service/openai.rs`:
- Around line 1453-1454: Update normalize_empty_uuid_image_urls to parse the
request body via parse_json_request::<serde_json::Value> instead of
serde_json::from_slice, preserving control-character escaping and lossy UTF-8
handling during normalization retries.
---
Nitpick comments:
In `@lib/llm/src/http/service/openai.rs`:
- Around line 3831-3841: Update
test_parse_chat_completion_request_accepts_empty_image_url_with_uuid to also
assert that the serialized content item's image_url field is null, while
preserving the existing UUID assertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 73c4ad33-46b4-48bc-88ed-5662fdd7579e
📒 Files selected for processing (1)
lib/llm/src/http/service/openai.rs
|
🎯 Code Coverage (details) 🔗 Commit SHA: cab9f61 | Docs | Datadog PR Page | Give us feedback! |
|
I think this belongs one layer down, in The root cause is type-level: Proposed fixOne helper in /// Deserialises an optional media object, treating `{"url": ""}` as absent.
///
/// vLLM's OpenAI-compatible schema requires the media object to be present, so
/// UUID-cache clients emit an empty URL where Dynamo's canonical form is `null`.
/// Normalising at the type boundary keeps the empty-vs-null distinction out of
/// every call site and leaves the `(url, uuid)` policy to one downstream check.
fn deserialize_optional_media<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::de::DeserializeOwned,
{
use serde::de::Error;
match Option::<serde_json::Value>::deserialize(deserializer)? {
None | Some(serde_json::Value::Null) => Ok(None),
Some(value) if value.get("url").and_then(serde_json::Value::as_str) == Some("") => Ok(None),
Some(value) => serde_json::from_value(value).map(Some).map_err(D::Error::custom),
}
}Plus one attribute change on each of #[builder(default)]
- #[serde(default)]
+ #[serde(default, deserialize_with = "deserialize_optional_media")]
pub image_url: Option<ImageUrl>,That crate's own comment says this is exactly why these types are defined locally instead of re-exported from upstream ( I prototyped this locally against Why not the handlerIt's a cost on the steady-state path, not an error path. For a client using UUID caching, every cache-hit request takes the fallback. Microbenchmarked The failing request runs the whole ladder: typed parse, control-char escape scan, Coverage. The handler fix misses Composition. The new fallback wraps Schema. The utoipa-derived OpenAPI schema ( Errors. With the type-level fix, Smaller notes on the current patch
|
Signed-off-by: Zhuangcheng(Jesse) Gu <zcgu@connect.hku.hk>
Signed-off-by: Zhuangcheng(Jesse) Gu <zcgu@connect.hku.hk>
Signed-off-by: Zhuangcheng(Jesse) Gu <zcgu@connect.hku.hk>
37e7f71 to
b85a0ef
Compare
Signed-off-by: Zhuangcheng(Jesse) Gu <zcgu@connect.hku.hk>
Overview:
Allow Dynamo to accept UUID-only image references emitted by AIPerf #869:
{"image_url":{"url":""},"uuid":"image-42"}Dynamo currently rejects the empty URL String (only accept null) during typed deserialization before UUID handling is reached.
Details:
image_url: null.Where should the reviewer start?
lib/llm/src/http/service/openai.rs, specificallyparse_chat_completion_requestandnormalize_empty_uuid_image_urls.Related Issues
Summary by CodeRabbit