Files
mistralai-client-rs/tests/v1_chat_multimodal_test.rs
Sienna Meridian Satterwhite a29c3c0109 feat: add Agents API, Conversations API, and multimodal support (v1.1.0)
Agents API (beta): create, get, update, delete, list agents with tools,
handoffs, completion args, and guardrails support.

Conversations API (beta): create, append, history, messages, restart,
delete, list conversations. Supports agent-backed and model-only
conversations with function calling and handoff execution modes.

Multimodal: ChatMessageContent enum (Text/Parts) with ContentPart
variants for text and image_url. Backwards-compatible constructors.
new_user_message_with_images() for mixed content messages.

Chat: reasoning field on ChatResponseChoice for Magistral models.
HTTP: PATCH methods for agent updates.

81 tests (30 live API integration + 35 serde unit + 16 existing).
2026-03-21 20:58:25 +00:00

205 lines
6.0 KiB
Rust

use mistralai_client::v1::chat::*;
#[test]
fn test_content_part_text_serialization() {
let part = ContentPart::Text {
text: "hello".to_string(),
};
let json = serde_json::to_value(&part).unwrap();
assert_eq!(json["type"], "text");
assert_eq!(json["text"], "hello");
}
#[test]
fn test_content_part_image_url_serialization() {
let part = ContentPart::ImageUrl {
image_url: ImageUrl {
url: "https://example.com/image.png".to_string(),
detail: Some("high".to_string()),
},
};
let json = serde_json::to_value(&part).unwrap();
assert_eq!(json["type"], "image_url");
assert_eq!(json["image_url"]["url"], "https://example.com/image.png");
assert_eq!(json["image_url"]["detail"], "high");
}
#[test]
fn test_content_part_image_url_no_detail() {
let part = ContentPart::ImageUrl {
image_url: ImageUrl {
url: "data:image/png;base64,abc123".to_string(),
detail: None,
},
};
let json = serde_json::to_value(&part).unwrap();
assert_eq!(json["type"], "image_url");
assert!(json["image_url"].get("detail").is_none());
}
#[test]
fn test_chat_message_content_text() {
let content = ChatMessageContent::Text("hello world".to_string());
assert_eq!(content.text(), "hello world");
assert_eq!(content.as_text(), Some("hello world"));
assert!(!content.has_images());
assert_eq!(content.to_string(), "hello world");
}
#[test]
fn test_chat_message_content_parts() {
let content = ChatMessageContent::Parts(vec![
ContentPart::Text {
text: "What is this? ".to_string(),
},
ContentPart::ImageUrl {
image_url: ImageUrl {
url: "https://example.com/cat.jpg".to_string(),
detail: None,
},
},
]);
assert_eq!(content.text(), "What is this? ");
assert!(content.as_text().is_none());
assert!(content.has_images());
}
#[test]
fn test_chat_message_content_text_serialization() {
let content = ChatMessageContent::Text("hello".to_string());
let json = serde_json::to_value(&content).unwrap();
assert_eq!(json, serde_json::json!("hello"));
}
#[test]
fn test_chat_message_content_parts_serialization() {
let content = ChatMessageContent::Parts(vec![ContentPart::Text {
text: "hello".to_string(),
}]);
let json = serde_json::to_value(&content).unwrap();
assert!(json.is_array());
assert_eq!(json[0]["type"], "text");
}
#[test]
fn test_chat_message_content_text_deserialization() {
let content: ChatMessageContent = serde_json::from_value(serde_json::json!("hello")).unwrap();
assert_eq!(content.text(), "hello");
}
#[test]
fn test_chat_message_content_parts_deserialization() {
let content: ChatMessageContent = serde_json::from_value(serde_json::json!([
{"type": "text", "text": "describe this"},
{"type": "image_url", "image_url": {"url": "https://example.com/img.jpg"}}
]))
.unwrap();
assert_eq!(content.text(), "describe this");
assert!(content.has_images());
}
#[test]
fn test_new_user_message_text_content() {
let msg = ChatMessage::new_user_message("hello");
let json = serde_json::to_value(&msg).unwrap();
assert_eq!(json["role"], "user");
assert_eq!(json["content"], "hello");
}
#[test]
fn test_new_user_message_with_images() {
let msg = ChatMessage::new_user_message_with_images(vec![
ContentPart::Text {
text: "What is this?".to_string(),
},
ContentPart::ImageUrl {
image_url: ImageUrl {
url: "data:image/png;base64,abc123".to_string(),
detail: None,
},
},
]);
let json = serde_json::to_value(&msg).unwrap();
assert_eq!(json["role"], "user");
assert!(json["content"].is_array());
assert_eq!(json["content"][0]["type"], "text");
assert_eq!(json["content"][1]["type"], "image_url");
}
#[test]
fn test_chat_message_content_from_str() {
let content: ChatMessageContent = "test".into();
assert_eq!(content.text(), "test");
}
#[test]
fn test_chat_message_content_from_string() {
let content: ChatMessageContent = String::from("test").into();
assert_eq!(content.text(), "test");
}
#[test]
fn test_chat_response_choice_with_reasoning() {
let json = serde_json::json!({
"index": 0,
"message": {
"role": "assistant",
"content": "The answer is 42."
},
"finish_reason": "stop",
"reasoning": "Let me think about this step by step..."
});
let choice: ChatResponseChoice = serde_json::from_value(json).unwrap();
assert_eq!(choice.reasoning.as_deref(), Some("Let me think about this step by step..."));
assert_eq!(choice.message.content.text(), "The answer is 42.");
}
#[test]
fn test_chat_response_choice_without_reasoning() {
let json = serde_json::json!({
"index": 0,
"message": {
"role": "assistant",
"content": "Hello"
},
"finish_reason": "stop"
});
let choice: ChatResponseChoice = serde_json::from_value(json).unwrap();
assert!(choice.reasoning.is_none());
}
#[test]
fn test_full_chat_response_roundtrip() {
let json = serde_json::json!({
"id": "chat-abc123",
"object": "chat.completion",
"created": 1711000000,
"model": "mistral-medium-latest",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hi there!"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 5,
"total_tokens": 15
}
});
let resp: ChatResponse = serde_json::from_value(json).unwrap();
assert_eq!(resp.choices[0].message.content.text(), "Hi there!");
assert_eq!(resp.usage.total_tokens, 15);
// Re-serialize and verify
let re_json = serde_json::to_value(&resp).unwrap();
assert_eq!(re_json["choices"][0]["message"]["content"], "Hi there!");
}