diff --git a/rust/src/chat/src/backend/hf.rs b/rust/src/chat/src/backend/hf.rs index 54b31b165d56..db6c2b6450df 100644 --- a/rust/src/chat/src/backend/hf.rs +++ b/rust/src/chat/src/backend/hf.rs @@ -236,6 +236,7 @@ mod tests { request_id: "renderer-selection-test".to_string(), messages: vec![ChatMessage::User { content: ChatContent::Text(text.to_string()), + name: None, }], ..ChatRequest::for_test() } diff --git a/rust/src/chat/src/multimodal.rs b/rust/src/chat/src/multimodal.rs index 6aad7e942d53..990700935bb5 100644 --- a/rust/src/chat/src/multimodal.rs +++ b/rust/src/chat/src/multimodal.rs @@ -629,9 +629,9 @@ fn extract_media_parts( ) })?; let content = match message { - ChatMessage::System { content } + ChatMessage::System { content, .. } | ChatMessage::Developer { content, .. } - | ChatMessage::User { content } + | ChatMessage::User { content, .. } | ChatMessage::ToolResponse { content, .. } | ChatMessage::Custom { content, .. } => content, ChatMessage::Assistant { .. } => { @@ -653,9 +653,9 @@ fn extract_media_parts( // Fall back to all media in request message and content-part order (e.g. Jinja). None => Either::Right(request.messages.iter().flat_map(|message| { let content = match message { - ChatMessage::System { content } + ChatMessage::System { content, .. } | ChatMessage::Developer { content, .. } - | ChatMessage::User { content } + | ChatMessage::User { content, .. } | ChatMessage::ToolResponse { content, .. } | ChatMessage::Custom { content, .. } => Some(content), ChatMessage::Assistant { .. } => None, diff --git a/rust/src/chat/src/renderer/deepseek.rs b/rust/src/chat/src/renderer/deepseek.rs index cb50e68891fa..0752f33fe123 100644 --- a/rust/src/chat/src/renderer/deepseek.rs +++ b/rust/src/chat/src/renderer/deepseek.rs @@ -162,7 +162,7 @@ pub(super) fn render_request_with_media_order( render_index += 1; match message { - ChatMessage::System { content } => { + ChatMessage::System { content, .. } => { if dialect == DsDialect::V41 && current_render_index > 0 { out.push_str(SYSTEM_SP_TOKEN); } @@ -180,7 +180,7 @@ pub(super) fn render_request_with_media_order( &mut media_order, )?; } - ChatMessage::Developer { content, tools } => { + ChatMessage::Developer { content, tools, .. } => { render_developer_message( &mut out, message_index, @@ -200,7 +200,7 @@ pub(super) fn render_request_with_media_order( &mut media_order, )?; } - ChatMessage::Assistant { content } => { + ChatMessage::Assistant { content, .. } => { // Mirror Python: thinking block (reasoning + ) is // emitted whenever thinking is active and reasoning isn't // dropped - i.e. drop_thinking is off OR this turn lies @@ -436,9 +436,9 @@ fn starts_user_turn( fn reject_image_placeholder_text(messages: &[ChatMessage]) -> Result<()> { let has_placeholder = |text: &str| text.contains(DEEPSEEK_V41_IMAGE_PLACEHOLDER); let found = messages.iter().any(|message| match message { - ChatMessage::System { content } + ChatMessage::System { content, .. } | ChatMessage::Developer { content, .. } - | ChatMessage::User { content } + | ChatMessage::User { content, .. } | ChatMessage::ToolResponse { content, .. } | ChatMessage::Custom { content, .. } => match content { ChatContent::Text(text) => has_placeholder(text), @@ -446,7 +446,7 @@ fn reject_image_placeholder_text(messages: &[ChatMessage]) -> Result<()> { |part| matches!(part, ChatContentPart::Text { text } if has_placeholder(text)), ), }, - ChatMessage::Assistant { content } => content.iter().any(|block| { + ChatMessage::Assistant { content, .. } => content.iter().any(|block| { matches!( block, AssistantContentBlock::Text { text } | AssistantContentBlock::Reasoning { text } @@ -617,7 +617,7 @@ fn render_user_content_block( out.push_str("\n\n"); } match &messages[message_index] { - ChatMessage::User { content } => { + ChatMessage::User { content, .. } => { write_chat_content(out, content, dialect, message_index, media_order)?; } ChatMessage::ToolResponse { .. } => { diff --git a/rust/src/chat/src/renderer/deepseek_v32/encoding.rs b/rust/src/chat/src/renderer/deepseek_v32/encoding.rs index 33e6eb99d8b6..f4b06b147328 100644 --- a/rust/src/chat/src/renderer/deepseek_v32/encoding.rs +++ b/rust/src/chat/src/renderer/deepseek_v32/encoding.rs @@ -125,21 +125,21 @@ fn render_message( let add_generation_prompt = message_index + 1 != messages.len() || add_generation_prompt; match message { - ChatMessage::System { content } => render_system_message(out, Some(content), &[]), - ChatMessage::Developer { content, tools } => render_developer_message( + ChatMessage::System { content, .. } => render_system_message(out, Some(content), &[]), + ChatMessage::Developer { content, tools, .. } => render_developer_message( out, content, tools.as_deref().unwrap_or(&[]), thinking_mode == ThinkingMode::Thinking && opens_thinking, add_generation_prompt, ), - ChatMessage::User { content } => render_user_message( + ChatMessage::User { content, .. } => render_user_message( out, content, thinking_mode == ThinkingMode::Thinking && opens_thinking, add_generation_prompt, ), - ChatMessage::Assistant { content } => render_assistant_message( + ChatMessage::Assistant { content, .. } => render_assistant_message( out, thinking_mode == ThinkingMode::Thinking && after_last_user_turn, content, @@ -275,6 +275,7 @@ fn render_tool_message( let ChatMessage::Assistant { content: assistant_content, + .. } = &messages[prev_assistant_idx] else { return Err(Error::ChatTemplate( @@ -305,6 +306,7 @@ fn render_tool_message( let ChatMessage::ToolResponse { content, tool_call_id, + .. } = message else { unreachable!("tool response block should only contain tool messages"); diff --git a/rust/src/chat/src/renderer/deepseek_v41/tests.rs b/rust/src/chat/src/renderer/deepseek_v41/tests.rs index 077f535862bd..d25cc1ad862f 100644 --- a/rust/src/chat/src/renderer/deepseek_v41/tests.rs +++ b/rust/src/chat/src/renderer/deepseek_v41/tests.rs @@ -72,6 +72,7 @@ fn inlines_image_placeholder_at_image_part_positions() { ChatContentPart::image_url("https://example.com/carrots.jpeg"), ChatContentPart::text("what is in the image?"), ]), + name: None, }], ..ChatRequest::for_test() }; diff --git a/rust/src/chat/src/renderer/harmony/mod.rs b/rust/src/chat/src/renderer/harmony/mod.rs index d8dafadca74a..aefc93d1ee9b 100644 --- a/rust/src/chat/src/renderer/harmony/mod.rs +++ b/rust/src/chat/src/renderer/harmony/mod.rs @@ -180,8 +180,10 @@ fn peel_leading_instructions( }; match first { - ChatMessage::System { content } => Ok((Some(flatten_text(content)?), None, &messages[1..])), - ChatMessage::Developer { content, tools } => Ok(( + ChatMessage::System { content, .. } => { + Ok((Some(flatten_text(content)?), None, &messages[1..])) + } + ChatMessage::Developer { content, tools, .. } => Ok(( Some(flatten_text(content)?), tools.as_deref(), &messages[1..], @@ -291,7 +293,7 @@ fn to_harmony_message( options: &Options, ) -> Result> { Ok(match message { - ChatMessage::System { content } => { + ChatMessage::System { content, .. } => { let instructions = flatten_text(content)?; vec![system_or_developer_message( "system", @@ -300,20 +302,21 @@ fn to_harmony_message( options, )?] } - ChatMessage::Developer { content, tools } => { + ChatMessage::Developer { content, tools, .. } => { let instructions = flatten_text(content)?; vec![developer_message(Some(instructions), tools.as_deref())] } - ChatMessage::User { content } => { + ChatMessage::User { content, .. } => { vec![Message::from_role_and_content( Role::User, flatten_text(content)?, )] } - ChatMessage::Assistant { content } => assistant_messages(content), + ChatMessage::Assistant { content, .. } => assistant_messages(content), ChatMessage::ToolResponse { content, tool_call_id, + .. } => { let name = tool_call_names.get(tool_call_id).ok_or_else(|| { Error::ChatTemplate(format!( @@ -420,7 +423,7 @@ fn assistant_messages(content: &[AssistantContentBlock]) -> Vec { fn tool_call_names(messages: &[ChatMessage]) -> std::collections::HashMap { let mut names = std::collections::HashMap::new(); for message in messages { - let ChatMessage::Assistant { content } = message else { + let ChatMessage::Assistant { content, .. } = message else { continue; }; for tool_call in content.tool_calls() { diff --git a/rust/src/chat/src/renderer/hf/mod.rs b/rust/src/chat/src/renderer/hf/mod.rs index ab9529639d46..d6422f3b1491 100644 --- a/rust/src/chat/src/renderer/hf/mod.rs +++ b/rust/src/chat/src/renderer/hf/mod.rs @@ -278,6 +278,8 @@ struct TemplateMessage<'a> { tool_calls: Option>, // Tool-role messages refer back to the assistant call they are answering. tool_call_id: Option, + // Optional participant name, passed through for any role. + name: Option<&'a str>, } /// Chat content in the two shapes HF templates commonly expect. @@ -345,8 +347,9 @@ fn to_template_message<'a>( content_format: ChatTemplateContentFormat, multimodal: Option<&MultimodalRenderInfo>, ) -> Result> { + let name = message.name(); Ok(match message { - ChatMessage::System { content } => TemplateMessage { + ChatMessage::System { content, .. } => TemplateMessage { role: "system", content: to_template_content(content, content_format, multimodal)?, tools: None, @@ -354,8 +357,9 @@ fn to_template_message<'a>( reasoning_content: None, tool_calls: None, tool_call_id: None, + name, }, - ChatMessage::Developer { content, tools } => TemplateMessage { + ChatMessage::Developer { content, tools, .. } => TemplateMessage { role: "developer", content: to_template_content(content, content_format, multimodal)?, tools: tools.as_deref().map(to_template_tools), @@ -363,8 +367,9 @@ fn to_template_message<'a>( reasoning_content: None, tool_calls: None, tool_call_id: None, + name, }, - ChatMessage::User { content } => TemplateMessage { + ChatMessage::User { content, .. } => TemplateMessage { role: "user", content: to_template_content(content, content_format, multimodal)?, tools: None, @@ -372,8 +377,9 @@ fn to_template_message<'a>( reasoning_content: None, tool_calls: None, tool_call_id: None, + name, }, - ChatMessage::Assistant { content } => { + ChatMessage::Assistant { content, .. } => { let text = content.text(); let reasoning = content.reasoning(); let tool_calls = to_template_tool_calls(content)?; @@ -387,11 +393,13 @@ fn to_template_message<'a>( reasoning_content: reasoning, tool_calls, tool_call_id: None, + name, } } ChatMessage::ToolResponse { content, tool_call_id, + .. } => TemplateMessage { role: "tool", content: to_template_content(content, content_format, multimodal)?, @@ -400,8 +408,9 @@ fn to_template_message<'a>( reasoning_content: None, tool_calls: None, tool_call_id: Some(tool_call_id.clone()), + name, }, - ChatMessage::Custom { role, content } => TemplateMessage { + ChatMessage::Custom { role, content, .. } => TemplateMessage { role, content: to_template_content(content, content_format, multimodal)?, tools: None, @@ -409,6 +418,7 @@ fn to_template_message<'a>( reasoning_content: None, tool_calls: None, tool_call_id: None, + name, }, }) } @@ -1055,6 +1065,32 @@ mod tests { assert_eq!(rendered, "root=identity;system=policy;user=hello;"); } + #[test] + fn chat_template_passes_message_names_for_every_role() { + let request = sample_request(vec![ + ChatMessage::custom("root", "identity").with_name(Some("operator".to_string())), + ChatMessage::system("policy").with_name(Some("rules".to_string())), + ChatMessage::developer("notes", None).with_name(Some("dev".to_string())), + ChatMessage::user("hello").with_name(Some("alice".to_string())), + ChatMessage::user("hi"), + ChatMessage::assistant_text("hey").with_name(Some("helper".to_string())), + ChatMessage::tool_response("sunny", "call_1").with_name(Some("weather".to_string())), + ]); + + let rendered = render( + Some( + "{% for m in messages %}{{ m.role }}:{{ m.name if m.name is defined else '-' }};{% endfor %}", + ), + &request, + ) + .unwrap(); + + assert_eq!( + rendered, + "root:operator;system:rules;developer:dev;user:alice;user:-;assistant:helper;tool:weather;" + ); + } + #[test] fn chat_template_keeps_string_text_for_openai_detected_templates() { let request = sample_request(vec![ChatMessage::text(ChatRole::User, "hello")]); diff --git a/rust/src/chat/src/renderer/inkling/mod.rs b/rust/src/chat/src/renderer/inkling/mod.rs index f792f70d3288..1de4742efccd 100644 --- a/rust/src/chat/src/renderer/inkling/mod.rs +++ b/rust/src/chat/src/renderer/inkling/mod.rs @@ -322,21 +322,22 @@ impl ChatRenderer for InklingChatRenderer { } match message { - ChatMessage::System { content } => { + ChatMessage::System { content, .. } => { self.write_chat_content(&mut out, self.special.message_system, content)?; } ChatMessage::Developer { content, .. } => { self.write_chat_content(&mut out, self.special.message_system, content)?; } - ChatMessage::User { content } => { + ChatMessage::User { content, .. } => { self.write_chat_content(&mut out, self.special.message_user, content)?; } - ChatMessage::Assistant { content } => { + ChatMessage::Assistant { content, .. } => { self.write_assistant_content(&mut out, content, &mut tool_call_id_to_name)?; } ChatMessage::ToolResponse { content, tool_call_id, + .. } => { self.write_tool_response( &mut out, diff --git a/rust/src/chat/src/renderer/kimi_k3/encoding.rs b/rust/src/chat/src/renderer/kimi_k3/encoding.rs index 242478294794..6f530bec7d0b 100644 --- a/rust/src/chat/src/renderer/kimi_k3/encoding.rs +++ b/rust/src/chat/src/renderer/kimi_k3/encoding.rs @@ -122,6 +122,7 @@ pub(super) fn render_request_with_media_order( let ChatMessage::ToolResponse { content, tool_call_id, + .. } = message else { unreachable!("pending tool run only holds tool responses"); @@ -176,19 +177,20 @@ pub(super) fn render_request_with_media_order( ChatMessage::Developer { content, tools: Some(local_tools), + .. } if !local_tools.is_empty() => { write_tool_declare(&mut out, local_tools, true)?; if !content_is_empty(content) { write_role_message(&mut out, "system", None, message_index, content)?; } } - ChatMessage::System { content } | ChatMessage::Developer { content, .. } => { + ChatMessage::System { content, .. } | ChatMessage::Developer { content, .. } => { write_role_message(&mut out, "system", None, message_index, content)?; } - ChatMessage::User { content } => { + ChatMessage::User { content, .. } => { write_role_message(&mut out, "user", None, message_index, content)?; } - ChatMessage::Assistant { content } => { + ChatMessage::Assistant { content, .. } => { tool_call_id_index.clear(); let mut call_position = 0usize; for block in content { diff --git a/rust/src/chat/src/request.rs b/rust/src/chat/src/request.rs index b52e9fbc0b81..d79271258be6 100644 --- a/rust/src/chat/src/request.rs +++ b/rust/src/chat/src/request.rs @@ -194,31 +194,55 @@ impl From> for ChatContent { /// One chat message. /// +/// Every variant carries the optional participant `name`, which chat templates +/// receive for any role. +/// /// Original Python API reference: /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "role", rename_all = "snake_case")] pub enum ChatMessage { /// System message content. - System { content: ChatContent }, + System { + content: ChatContent, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + }, /// Developer message content plus optional message-local tools. Developer { content: ChatContent, tools: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, }, /// User message content. - User { content: ChatContent }, + User { + content: ChatContent, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + }, /// Assistant history content assembled from structured assistant blocks. - Assistant { content: Vec }, + Assistant { + content: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + }, /// Tool response content associated with one prior assistant tool call. ToolResponse { content: ChatContent, tool_call_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, }, /// Message content under a role outside the standard set, passed to chat /// templates unchanged. #[serde(untagged)] - Custom { role: String, content: ChatContent }, + Custom { + role: String, + content: ChatContent, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + }, } impl ChatMessage { @@ -245,6 +269,7 @@ impl ChatMessage { pub fn system(content: impl Into) -> Self { Self::System { content: content.into(), + name: None, } } @@ -253,6 +278,7 @@ impl ChatMessage { Self::Developer { content: content.into(), tools, + name: None, } } @@ -270,6 +296,7 @@ impl ChatMessage { pub fn user(content: impl Into) -> Self { Self::User { content: content.into(), + name: None, } } @@ -277,13 +304,17 @@ impl ChatMessage { pub fn assistant_text(text: impl Into) -> Self { Self::Assistant { content: vec![AssistantContentBlock::Text { text: text.into() }], + name: None, } } /// Construct one chat message with assistant role and structured content /// blocks. pub fn assistant_blocks(content: Vec) -> Self { - Self::Assistant { content } + Self::Assistant { + content, + name: None, + } } /// Construct one tool-role message. @@ -291,6 +322,7 @@ impl ChatMessage { Self::ToolResponse { content: content.into(), tool_call_id: tool_call_id.into(), + name: None, } } @@ -299,9 +331,33 @@ impl ChatMessage { Self::Custom { role: role.into(), content: content.into(), + name: None, } } + /// Set the participant name of this message. + pub fn with_name(mut self, name: Option) -> Self { + let (Self::System { name: slot, .. } + | Self::Developer { name: slot, .. } + | Self::User { name: slot, .. } + | Self::Assistant { name: slot, .. } + | Self::ToolResponse { name: slot, .. } + | Self::Custom { name: slot, .. }) = &mut self; + *slot = name; + self + } + + /// Return the participant name of this message, if any. + pub fn name(&self) -> Option<&str> { + let (Self::System { name, .. } + | Self::Developer { name, .. } + | Self::User { name, .. } + | Self::Assistant { name, .. } + | Self::ToolResponse { name, .. } + | Self::Custom { name, .. }) = self; + name.as_deref() + } + /// Return the chat role of this message. pub fn role(&self) -> ChatRole { match self { @@ -317,19 +373,19 @@ impl ChatMessage { /// Concatenate the visible text carried by this message. pub fn text_content(&self) -> Result { match self { - Self::System { content } + Self::System { content, .. } | Self::Developer { content, .. } - | Self::User { content } + | Self::User { content, .. } | Self::ToolResponse { content, .. } | Self::Custom { content, .. } => content.try_flatten_to_text(), - Self::Assistant { content } => Ok(content.text()), + Self::Assistant { content, .. } => Ok(content.text()), } } /// Concatenate assistant reasoning text when present. pub fn reasoning_content(&self) -> Option { match self { - Self::Assistant { content } => content.reasoning(), + Self::Assistant { content, .. } => content.reasoning(), Self::System { .. } | Self::Developer { .. } | Self::User { .. } @@ -341,9 +397,9 @@ impl ChatMessage { /// Return whether this message contains any multimodal content. pub fn has_multimodal(&self) -> bool { match self { - Self::System { content } + Self::System { content, .. } | Self::Developer { content, .. } - | Self::User { content } + | Self::User { content, .. } | Self::ToolResponse { content, .. } | Self::Custom { content, .. } => content.has_multimodal(), Self::Assistant { .. } => false, @@ -355,6 +411,7 @@ impl From for ChatMessage { fn from(value: AssistantMessage) -> Self { Self::Assistant { content: value.content, + name: None, } } } diff --git a/rust/src/server/src/routes/openai/chat_completions/convert.rs b/rust/src/server/src/routes/openai/chat_completions/convert.rs index 30c4fc8ad24f..10bb4438d2a7 100644 --- a/rust/src/server/src/routes/openai/chat_completions/convert.rs +++ b/rust/src/server/src/routes/openai/chat_completions/convert.rs @@ -280,15 +280,17 @@ fn extract_last_assistant_content(messages: &[ChatMessage]) -> Option { /// Lower one OpenAI chat message into the `vllm-chat` message shape. pub(crate) fn convert_message(message: ChatMessage) -> Result { match message { - ChatMessage::System { content, .. } => { - Ok(VllmChatMessage::system(convert_content(content)?)) + ChatMessage::System { content, name } => { + Ok(VllmChatMessage::system(convert_content(content)?).with_name(name)) + } + ChatMessage::User { content, name } => { + Ok(VllmChatMessage::user(convert_content(content)?).with_name(name)) } - ChatMessage::User { content, .. } => Ok(VllmChatMessage::user(convert_content(content)?)), ChatMessage::Assistant { content, tool_calls, reasoning, - name: _, + name, } => { let mut blocks = Vec::new(); if let Some(reasoning) = reasoning @@ -308,29 +310,32 @@ pub(crate) fn convert_message(message: ChatMessage) -> Result Ok(VllmChatMessage::tool_response( - convert_content(content)?, - tool_call_id, - )), + name, + } => Ok( + VllmChatMessage::tool_response(convert_content(content)?, tool_call_id).with_name(name), + ), ChatMessage::Function { .. } => { bail_invalid_request!("Function messages are not supported.") } ChatMessage::Developer { content, tools, - name: _, + name, } => Ok(VllmChatMessage::developer( convert_content(content)?, convert_message_tools(tools)?, - )), - ChatMessage::Custom { role, content } => { - Ok(VllmChatMessage::custom(role, convert_content(content)?)) - } + ) + .with_name(name)), + ChatMessage::Custom { + role, + content, + name, + } => Ok(VllmChatMessage::custom(role, convert_content(content)?).with_name(name)), } } @@ -869,6 +874,43 @@ mod tests { assert!(prompt.contains(r#""answer":{"type":"string"}"#)); } + #[test] + fn prepare_chat_request_keeps_message_names() { + let request: ChatCompletionRequest = serde_json::from_value(json!({ + "model": "Qwen/Qwen1.5-0.5B-Chat", + "messages": [ + {"role": "root", "content": "identity", "name": "operator"}, + {"role": "system", "content": "policy", "name": "rules"}, + {"role": "user", "content": "hello", "name": "alice"}, + {"role": "user", "content": "hi", "name": null}, + {"role": "assistant", "content": "hey", "name": "helper"}, + {"role": "tool", "content": "sunny", "tool_call_id": "call_1", "name": "weather"}, + ], + })) + .expect("parse messages"); + + let prepared = prepare_chat_request( + request, + &served(&["Qwen/Qwen1.5-0.5B-Chat"]), + ResolvedRequestContext::default(), + ) + .expect("request is valid"); + + let names: Vec<_> = + prepared.chat_request.messages.iter().map(VllmChatMessage::name).collect(); + assert_eq!( + names, + [ + Some("operator"), + Some("rules"), + Some("alice"), + None, + Some("helper"), + Some("weather"), + ] + ); + } + #[test] fn prepare_chat_request_maps_text_parts() { let mut request = base_request(); @@ -1431,6 +1473,7 @@ mod tests { ChatMessage::Tool { content: MessageContent::Text("Sunny".to_string()), tool_call_id: "call_1".to_string(), + name: None, }, ], tools: Some(vec![Tool { diff --git a/rust/src/server/src/routes/openai/utils/types.rs b/rust/src/server/src/routes/openai/utils/types.rs index e2187bae673c..fd94e43e9aa3 100644 --- a/rust/src/server/src/routes/openai/utils/types.rs +++ b/rust/src/server/src/routes/openai/utils/types.rs @@ -453,6 +453,7 @@ pub enum ChatMessage { Tool { content: MessageContent, tool_call_id: String, + name: Option, }, #[serde(rename = "function")] Function { content: String, name: String }, @@ -468,6 +469,7 @@ pub enum ChatMessage { Custom { role: String, content: MessageContent, + name: Option, }, } @@ -482,10 +484,12 @@ const STANDARD_CHAT_ROLES: &[&str] = &[ ]; /// Wire shape of [`ChatMessage::Custom`]. +#[serde_with::skip_serializing_none] #[derive(Deserialize, Serialize)] -struct CustomChatMessage { +struct CustomChatMessage { role: R, content: C, + name: Option, } impl Serialize for ChatMessage { @@ -494,9 +498,16 @@ impl Serialize for ChatMessage { S: serde::Serializer, { match self { - Self::Custom { role, content } => { - CustomChatMessage { role, content }.serialize(serializer) + Self::Custom { + role, + content, + name, + } => CustomChatMessage { + role, + content, + name: name.as_ref(), } + .serialize(serializer), _ => Self::serialize(self, serializer), } } @@ -516,9 +527,16 @@ impl<'de> Deserialize<'de> for ChatMessage { .is_some_and(|role| !STANDARD_CHAT_ROLES.contains(&role)); if is_custom { - let CustomChatMessage { role, content } = - CustomChatMessage::deserialize(value).map_err(serde::de::Error::custom)?; - Ok(Self::Custom { role, content }) + let CustomChatMessage { + role, + content, + name, + } = CustomChatMessage::deserialize(value).map_err(serde::de::Error::custom)?; + Ok(Self::Custom { + role, + content, + name, + }) } else { Self::deserialize(value).map_err(serde::de::Error::custom) } diff --git a/rust/src/server/src/routes/tests.rs b/rust/src/server/src/routes/tests.rs index 8ea3febf92bc..8addfb64a7c1 100644 --- a/rust/src/server/src/routes/tests.rs +++ b/rust/src/server/src/routes/tests.rs @@ -554,9 +554,9 @@ fn render_fake_message_content( placeholder: &str, ) -> vllm_chat::Result { match message { - ChatMessage::System { content } + ChatMessage::System { content, .. } | ChatMessage::Developer { content, .. } - | ChatMessage::User { content } + | ChatMessage::User { content, .. } | ChatMessage::ToolResponse { content, .. } | ChatMessage::Custom { content, .. } => render_fake_content(content, placeholder), ChatMessage::Assistant { .. } => message.text_content(),