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).
205 lines
6.0 KiB
Rust
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!");
|
|
}
|