test(neuron): C2 — guard Responses→chat image translation contract
All checks were successful
CI / CUDA type-check (push) Successful in 32s
build-prerelease / Resolve version stamps (push) Successful in 39s
CI / Format (push) Successful in 44s
CI / Clippy (push) Successful in 2m51s
build-prerelease / Build cortex binary (push) Successful in 4m42s
build-prerelease / Build neuron-blackwell (push) Successful in 5m52s
CI / Test (push) Successful in 6m16s
CI / Build cortex SRPM (push) Has been skipped
CI / Publish cortex to COPR (push) Has been skipped
CI / Build neuron SRPM (push) Has been skipped
CI / Publish neuron to COPR (push) Has been skipped
CI / Bump version in source (push) Has been skipped
build-prerelease / Build neuron-ampere (push) Successful in 8m12s
build-prerelease / Package cortex RPM (push) Successful in 1m26s
build-prerelease / Build neuron-ada (push) Successful in 5m34s
build-prerelease / Package helexa-neuron-ampere RPM (push) Successful in 2m59s
build-prerelease / Package helexa-neuron-ada RPM (push) Successful in 3m2s
build-prerelease / Package helexa-neuron-blackwell RPM (push) Successful in 3m44s
build-prerelease / Publish to rpm.lair.cafe (unstable) (push) Successful in 1m1s

The Responses request translator already emits the chat `image_url`
Parts array Stage B5's vision path consumes, and the non-streaming
(`chat_completion`) and streaming (`responses_stream` → `inference_stream`,
Stage C1) Responses paths both route image content to the vision-aware
prefill — so vision works end-to-end through `/v1/responses` with no
translator change required.

Add a multi-image test asserting order preservation and that the
`detail` hint is tolerated (and dropped, since chat image_url has no
analogue), locking the translator's output to the exact
`image_url.url` shape `extract_images_from_request` walks.

Closes part of #16 (Stage C2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 13:57:43 +03:00
parent 766c20ba47
commit dd592d918d

View File

@@ -646,6 +646,54 @@ mod tests {
assert_eq!(parts[1]["image_url"]["url"], "data:image/png;base64,AAA="); assert_eq!(parts[1]["image_url"]["url"], "data:image/png;base64,AAA=");
} }
#[test]
fn multiple_images_translate_in_order_and_tolerate_detail() {
// C2: a Responses request carrying several InputImage parts
// (with `detail` set) must translate to a chat Parts array that
// preserves image order and the `image_url.url` shape the chat
// vision path (`extract_images_from_request`) walks. The
// `detail` hint has no chat-completions analogue we forward, so
// it's dropped — but it must not break translation.
let req = ResponsesRequest {
model: "m".into(),
input: ResponsesInput::Items(vec![ResponsesInputItem::Message {
role: "user".into(),
content: ResponsesMessageContent::Parts(vec![
ResponsesContentPart::InputText {
text: "compare these".into(),
},
ResponsesContentPart::InputImage {
image_url: "data:image/png;base64,FIRST".into(),
detail: Some("high".into()),
},
ResponsesContentPart::InputImage {
image_url: "data:image/png;base64,SECOND".into(),
detail: None,
},
]),
}]),
instructions: None,
stream: false,
max_output_tokens: None,
temperature: None,
top_p: None,
previous_response_id: None,
extra: Value::Object(Default::default()),
};
let chat = request_to_chat(req).unwrap();
let parts = match &chat.messages[0].content {
MessageContent::Parts(p) => p.clone(),
other => panic!("expected Parts, got {other:?}"),
};
// text + two images, in input order.
assert_eq!(parts.len(), 3);
assert_eq!(parts[0]["type"], "text");
assert_eq!(parts[1]["image_url"]["url"], "data:image/png;base64,FIRST");
assert_eq!(parts[2]["image_url"]["url"], "data:image/png;base64,SECOND");
// `detail` is not forwarded into the chat image_url object.
assert!(parts[1]["image_url"].get("detail").is_none());
}
#[test] #[test]
fn text_only_parts_collapse_to_string() { fn text_only_parts_collapse_to_string() {
let req = ResponsesRequest { let req = ResponsesRequest {