use mistralai_client::v1::{ agents::*, client::Client, conversations::*, }; mod setup; fn make_client() -> Client { Client::new(None, None, None, None).unwrap() } /// Helper: create a disposable agent for conversation tests (sync). fn create_test_agent(client: &Client, name: &str) -> Agent { let req = make_agent_request(name); client.create_agent(&req).unwrap() } /// Helper: create a disposable agent for conversation tests (async). async fn create_test_agent_async(client: &Client, name: &str) -> Agent { let req = make_agent_request(name); client.create_agent_async(&req).await.unwrap() } fn make_agent_request(name: &str) -> CreateAgentRequest { CreateAgentRequest { model: "mistral-medium-latest".to_string(), name: name.to_string(), description: Some("Conversation test agent".to_string()), instructions: Some("You are a helpful test agent. Keep responses short.".to_string()), tools: None, handoffs: None, completion_args: Some(CompletionArgs { temperature: Some(0.0), ..Default::default() }), metadata: None, } } // --------------------------------------------------------------------------- // Sync tests // --------------------------------------------------------------------------- #[test] fn test_create_conversation_with_agent() { setup::setup(); let client = make_client(); let agent = create_test_agent(&client, "conv-test-create"); let req = CreateConversationRequest { inputs: ConversationInput::Text("What is 2 + 2?".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let response = client.create_conversation(&req).unwrap(); assert!(!response.conversation_id.is_empty()); assert_eq!(response.object, "conversation.response"); assert!(!response.outputs.is_empty()); assert!(response.usage.total_tokens > 0); // Should have an assistant response let text = response.assistant_text(); assert!(text.is_some(), "Expected assistant text in outputs"); assert!(text.unwrap().contains('4'), "Expected answer containing '4'"); // Cleanup client.delete_conversation(&response.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } #[test] fn test_create_conversation_without_agent() { setup::setup(); let client = make_client(); let req = CreateConversationRequest { inputs: ConversationInput::Text("Say hello.".to_string()), model: Some("mistral-medium-latest".to_string()), agent_id: None, agent_version: None, name: None, description: None, instructions: Some("Always respond with exactly 'hello'.".to_string()), completion_args: Some(CompletionArgs { temperature: Some(0.0), ..Default::default() }), tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let response = client.create_conversation(&req).unwrap(); assert!(!response.conversation_id.is_empty()); let text = response.assistant_text().unwrap().to_lowercase(); assert!(text.contains("hello"), "Expected 'hello', got: {text}"); client.delete_conversation(&response.conversation_id).unwrap(); } #[test] fn test_append_to_conversation() { setup::setup(); let client = make_client(); let agent = create_test_agent(&client, "conv-test-append"); // Create conversation let create_req = CreateConversationRequest { inputs: ConversationInput::Text("Remember the number 42.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&create_req).unwrap(); // Append follow-up let append_req = AppendConversationRequest { inputs: ConversationInput::Text("What number did I ask you to remember?".to_string()), completion_args: None, handoff_execution: None, store: None, tool_confirmations: None, stream: false, }; let appended = client .append_conversation(&created.conversation_id, &append_req) .unwrap(); assert_eq!(appended.conversation_id, created.conversation_id); assert!(!appended.outputs.is_empty()); let text = appended.assistant_text().unwrap(); assert!(text.contains("42"), "Expected '42' in response, got: {text}"); assert!(appended.usage.total_tokens > 0); client.delete_conversation(&created.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } #[test] fn test_get_conversation_info() { setup::setup(); let client = make_client(); let agent = create_test_agent(&client, "conv-test-get-info"); let create_req = CreateConversationRequest { inputs: ConversationInput::Text("Hello.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&create_req).unwrap(); let info = client.get_conversation(&created.conversation_id).unwrap(); assert_eq!(info.id, created.conversation_id); assert_eq!(info.agent_id.as_deref(), Some(agent.id.as_str())); client.delete_conversation(&created.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } #[test] fn test_get_conversation_history() { setup::setup(); let client = make_client(); let agent = create_test_agent(&client, "conv-test-history"); // Create and do two turns let create_req = CreateConversationRequest { inputs: ConversationInput::Text("First message.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&create_req).unwrap(); let append_req = AppendConversationRequest { inputs: ConversationInput::Text("Second message.".to_string()), completion_args: None, handoff_execution: None, store: None, tool_confirmations: None, stream: false, }; client .append_conversation(&created.conversation_id, &append_req) .unwrap(); // Get history — should have at least 4 entries (user, assistant, user, assistant) let history = client .get_conversation_history(&created.conversation_id) .unwrap(); assert_eq!(history.conversation_id, created.conversation_id); assert_eq!(history.object, "conversation.history"); assert!( history.entries.len() >= 4, "Expected >= 4 history entries, got {}", history.entries.len() ); // First entry should be a message input assert!(matches!( &history.entries[0], ConversationEntry::MessageInput(_) )); client.delete_conversation(&created.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } #[test] fn test_get_conversation_messages() { setup::setup(); let client = make_client(); let agent = create_test_agent(&client, "conv-test-messages"); let create_req = CreateConversationRequest { inputs: ConversationInput::Text("Hello there.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&create_req).unwrap(); let messages = client .get_conversation_messages(&created.conversation_id) .unwrap(); assert_eq!(messages.conversation_id, created.conversation_id); assert!(!messages.messages.is_empty()); client.delete_conversation(&created.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } #[test] fn test_list_conversations() { setup::setup(); let client = make_client(); let req = CreateConversationRequest { inputs: ConversationInput::Text("List test.".to_string()), model: Some("mistral-medium-latest".to_string()), agent_id: None, agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&req).unwrap(); let list = client.list_conversations().unwrap(); // API returns raw array (no wrapper object) assert!(list.data.iter().any(|c| c.id == created.conversation_id)); client.delete_conversation(&created.conversation_id).unwrap(); } #[test] fn test_delete_conversation() { setup::setup(); let client = make_client(); let req = CreateConversationRequest { inputs: ConversationInput::Text("To be deleted.".to_string()), model: Some("mistral-medium-latest".to_string()), agent_id: None, agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation(&req).unwrap(); let del = client.delete_conversation(&created.conversation_id).unwrap(); assert!(del.deleted); // Should no longer appear in list let list = client.list_conversations().unwrap(); assert!(!list.data.iter().any(|c| c.id == created.conversation_id)); } #[test] fn test_conversation_with_structured_entries() { setup::setup(); let client = make_client(); use mistralai_client::v1::chat::ChatMessageContent; let entries = vec![ConversationEntry::MessageInput(MessageInputEntry { role: "user".to_string(), content: ChatMessageContent::Text("What is the capital of France?".to_string()), prefix: None, id: None, object: None, created_at: None, completed_at: None, })]; let req = CreateConversationRequest { inputs: ConversationInput::Entries(entries), model: Some("mistral-medium-latest".to_string()), agent_id: None, agent_version: None, name: None, description: None, instructions: Some("Respond in one word.".to_string()), completion_args: Some(CompletionArgs { temperature: Some(0.0), ..Default::default() }), tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let response = client.create_conversation(&req).unwrap(); let text = response.assistant_text().unwrap().to_lowercase(); assert!(text.contains("paris"), "Expected 'Paris', got: {text}"); client.delete_conversation(&response.conversation_id).unwrap(); } #[test] fn test_conversation_with_function_calling() { setup::setup(); let client = make_client(); // Create agent with a function tool let agent_req = CreateAgentRequest { model: "mistral-medium-latest".to_string(), name: "conv-test-function".to_string(), description: None, instructions: Some("When asked about temperature, use the get_temperature tool.".to_string()), tools: Some(vec![AgentTool::function( "get_temperature".to_string(), "Get the current temperature in a city".to_string(), serde_json::json!({ "type": "object", "properties": { "city": {"type": "string", "description": "City name"} }, "required": ["city"] }), )]), handoffs: None, completion_args: Some(CompletionArgs { temperature: Some(0.0), ..Default::default() }), metadata: None, }; let agent = client.create_agent(&agent_req).unwrap(); // Create conversation — model should call the function let conv_req = CreateConversationRequest { inputs: ConversationInput::Text("What is the temperature in Paris?".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: Some(HandoffExecution::Client), metadata: None, store: None, stream: false, }; let response = client.create_conversation(&conv_req).unwrap(); // With client-side execution, we should see function calls in outputs let function_calls = response.function_calls(); if !function_calls.is_empty() { assert_eq!(function_calls[0].name, "get_temperature"); let args: serde_json::Value = serde_json::from_str(&function_calls[0].arguments).unwrap(); assert!(args["city"].as_str().is_some()); // Send back the function result let tool_call_id = function_calls[0] .tool_call_id .as_deref() .unwrap_or("unknown"); let result_entries = vec![ConversationEntry::FunctionResult(FunctionResultEntry { tool_call_id: tool_call_id.to_string(), result: "22°C".to_string(), id: None, object: None, created_at: None, completed_at: None, })]; let append_req = AppendConversationRequest { inputs: ConversationInput::Entries(result_entries), completion_args: None, handoff_execution: None, store: None, tool_confirmations: None, stream: false, }; let final_response = client .append_conversation(&response.conversation_id, &append_req) .unwrap(); // Now we should get an assistant text response let text = final_response.assistant_text(); assert!(text.is_some(), "Expected final text after function result"); assert!( text.unwrap().contains("22"), "Expected temperature in response" ); } // If the API handled it server-side instead, we should still have a response else { assert!( response.assistant_text().is_some(), "Expected either function calls or assistant text" ); } client.delete_conversation(&response.conversation_id).unwrap(); client.delete_agent(&agent.id).unwrap(); } // --------------------------------------------------------------------------- // Async tests // --------------------------------------------------------------------------- #[tokio::test] async fn test_create_conversation_async() { setup::setup(); let client = make_client(); let agent = create_test_agent_async(&client, "conv-async-create").await; let req = CreateConversationRequest { inputs: ConversationInput::Text("Async test: what is 3 + 3?".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let response = client.create_conversation_async(&req).await.unwrap(); assert!(!response.conversation_id.is_empty()); let text = response.assistant_text().unwrap(); assert!(text.contains('6'), "Expected '6', got: {text}"); client .delete_conversation_async(&response.conversation_id) .await .unwrap(); client.delete_agent_async(&agent.id).await.unwrap(); } #[tokio::test] async fn test_append_conversation_async() { setup::setup(); let client = make_client(); let agent = create_test_agent_async(&client, "conv-async-append").await; let create_req = CreateConversationRequest { inputs: ConversationInput::Text("My name is Alice.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation_async(&create_req).await.unwrap(); let append_req = AppendConversationRequest { inputs: ConversationInput::Text("What is my name?".to_string()), completion_args: None, handoff_execution: None, store: None, tool_confirmations: None, stream: false, }; let appended = client .append_conversation_async(&created.conversation_id, &append_req) .await .unwrap(); let text = appended.assistant_text().unwrap(); assert!( text.to_lowercase().contains("alice"), "Expected 'Alice' in response, got: {text}" ); client .delete_conversation_async(&created.conversation_id) .await .unwrap(); client.delete_agent_async(&agent.id).await.unwrap(); } #[tokio::test] async fn test_get_conversation_history_async() { setup::setup(); let client = make_client(); let agent = create_test_agent_async(&client, "conv-async-history").await; let create_req = CreateConversationRequest { inputs: ConversationInput::Text("Hello.".to_string()), model: None, agent_id: Some(agent.id.clone()), agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation_async(&create_req).await.unwrap(); let history = client .get_conversation_history_async(&created.conversation_id) .await .unwrap(); assert!(history.entries.len() >= 2); // at least user + assistant client .delete_conversation_async(&created.conversation_id) .await .unwrap(); client.delete_agent_async(&agent.id).await.unwrap(); } #[tokio::test] async fn test_list_conversations_async() { setup::setup(); let client = make_client(); let req = CreateConversationRequest { inputs: ConversationInput::Text("Async list test.".to_string()), model: Some("mistral-medium-latest".to_string()), agent_id: None, agent_version: None, name: None, description: None, instructions: None, completion_args: None, tools: None, handoff_execution: None, metadata: None, store: None, stream: false, }; let created = client.create_conversation_async(&req).await.unwrap(); let list = client.list_conversations_async().await.unwrap(); assert!(list.data.iter().any(|c| c.id == created.conversation_id)); client .delete_conversation_async(&created.conversation_id) .await .unwrap(); }