use crate::chat::{ChatMessage, ChatRole, StreamEnd, Tool, ToolCall, ToolResponse};
use crate::support;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ChatRequest {
pub system: Option<String>,
#[serde(default)]
pub messages: Vec<ChatMessage>,
pub tools: Option<Vec<Tool>>,
}
impl ChatRequest {
pub fn new(messages: Vec<ChatMessage>) -> Self {
Self {
messages,
system: None,
tools: None,
}
}
pub fn from_system(content: impl Into<String>) -> Self {
Self {
system: Some(content.into()),
messages: Vec::new(),
tools: None,
}
}
pub fn from_user(content: impl Into<String>) -> Self {
Self {
system: None,
messages: vec![ChatMessage::user(content.into())],
tools: None,
}
}
pub fn from_messages(messages: Vec<ChatMessage>) -> Self {
Self {
system: None,
messages,
tools: None,
}
}
}
impl ChatRequest {
pub fn with_system(mut self, system: impl Into<String>) -> Self {
self.system = Some(system.into());
self
}
pub fn append_message(mut self, msg: impl Into<ChatMessage>) -> Self {
self.messages.push(msg.into());
self
}
pub fn append_messages<I>(mut self, messages: I) -> Self
where
I: IntoIterator,
I::Item: Into<ChatMessage>,
{
self.messages.extend(messages.into_iter().map(Into::into));
self
}
pub fn with_tools<I>(mut self, tools: I) -> Self
where
I: IntoIterator,
I::Item: Into<Tool>,
{
self.tools = Some(tools.into_iter().map(Into::into).collect());
self
}
pub fn append_tool(mut self, tool: impl Into<Tool>) -> Self {
self.tools.get_or_insert_with(Vec::new).push(tool.into());
self
}
pub fn append_tool_use_from_stream_end(mut self, end: &StreamEnd, tool_response: ToolResponse) -> Self {
if let Some(content) = &end.captured_content {
self.messages.push(ChatMessage::assistant(content.clone()));
} else if let Some(calls_ref) = end.captured_tool_calls() {
let calls: Vec<ToolCall> = calls_ref.into_iter().cloned().collect();
if !calls.is_empty() {
self.messages.push(ChatMessage::from(calls));
}
}
self.messages.push(ChatMessage::from(tool_response));
self
}
}
impl ChatRequest {
pub fn iter_systems(&self) -> impl Iterator<Item = &str> {
self.system
.iter()
.map(|s| s.as_str())
.chain(self.messages.iter().filter_map(|message| match message.role {
ChatRole::System => message.content.first_text(),
_ => None,
}))
}
pub fn join_systems(&self) -> Option<String> {
let mut systems: Option<String> = None;
for system in self.iter_systems() {
let systems_content = systems.get_or_insert_with(String::new);
support::combine_text_with_empty_line(systems_content, system);
}
systems
}
#[deprecated(note = "use join_systems()")]
pub fn combine_systems(&self) -> Option<String> {
self.join_systems()
}
}