Skip to content

Commit 1e2da69

Browse files
committed
fix(oci): convert image_url to camelCase imageUrl for OCI vision requests
OCI Generative AI expects camelCase field names. The SDK transforms message roles and content types but passed image_url through as-is, causing 400 errors on Command A Vision with inline images. Added integration test that sends a 1x1 red PNG to Command A Vision and verifies the model correctly identifies the color. Fixes cohere-ai#760
1 parent 2598c9a commit 1e2da69

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

src/cohere/oci_client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ def transform_request_to_oci(
720720
if isinstance(item, dict) and "type" in item:
721721
transformed_item = item.copy()
722722
transformed_item["type"] = item["type"].upper()
723+
# OCI expects camelCase: image_url → imageUrl
724+
if "image_url" in transformed_item:
725+
transformed_item["imageUrl"] = transformed_item.pop("image_url")
723726
transformed_content.append(transformed_item)
724727
else:
725728
transformed_content.append(item)

tests/test_oci_client.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,37 @@ def test_chat_v2(self):
185185
self.assertIsNotNone(response)
186186
self.assertIsNotNone(response.message)
187187

188+
def test_chat_vision_v2(self):
189+
"""Test vision with inline image on Command A Vision."""
190+
import base64, struct, zlib
191+
192+
# Create a minimal 1x1 red PNG
193+
raw = b'\x00\xff\x00\x00'
194+
compressed = zlib.compress(raw)
195+
def chunk(ctype, data):
196+
c = ctype + data
197+
return struct.pack('>I', len(data)) + c + struct.pack('>I', zlib.crc32(c) & 0xffffffff)
198+
ihdr = struct.pack('>IIBBBBB', 1, 1, 8, 2, 0, 0, 0)
199+
png = b'\x89PNG\r\n\x1a\n' + chunk(b'IHDR', ihdr) + chunk(b'IDAT', compressed) + chunk(b'IEND', b'')
200+
img_b64 = base64.b64encode(png).decode()
201+
202+
response = self.client.chat(
203+
model="command-a-vision",
204+
messages=[{
205+
"role": "user",
206+
"content": [
207+
{"type": "text", "text": "What color is this image? Reply with one word."},
208+
{"type": "image_url", "image_url": {"url": f"data:image/png;base64,{img_b64}"}},
209+
],
210+
}],
211+
)
212+
213+
self.assertIsNotNone(response)
214+
self.assertIsNotNone(response.message)
215+
self.assertTrue(len(response.message.content) > 0)
216+
# The 1x1 red pixel should be identified as red
217+
self.assertIn("red", response.message.content[0].text.lower())
218+
188219
def test_chat_stream_v2(self):
189220
"""Test streaming chat with v2 client."""
190221
events = []

0 commit comments

Comments
 (0)