use crate::core::{
language_model::{LanguageModelResponseContentType, Usage},
tools::{ToolCallInfo, ToolResultInfo},
};
#[derive(Debug, Clone)]
pub enum Role {
System,
User,
Assistant,
}
#[derive(Debug, Clone)]
pub enum Message {
System(SystemMessage),
User(UserMessage),
Assistant(AssistantMessage),
Tool(ToolResultInfo),
Developer(String),
}
pub type Messages = Vec<Message>;
impl Message {
pub fn conversation_builder() -> MessageBuilder<Conversation> {
MessageBuilder::conversation_builder()
}
pub fn builder() -> MessageBuilder<Initial> {
MessageBuilder::default()
}
}
#[derive(Debug, Clone)]
pub struct SystemMessage {
pub content: String,
}
impl SystemMessage {
pub fn new(content: impl Into<String>) -> Self {
Self {
content: content.into(),
}
}
}
impl From<String> for SystemMessage {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for SystemMessage {
fn from(value: &str) -> Self {
Self::new(value)
}
}
#[derive(Debug, Clone)]
pub struct UserMessage {
pub content: String,
}
impl UserMessage {
pub fn new(content: impl Into<String>) -> Self {
Self {
content: content.into(),
}
}
}
impl From<String> for UserMessage {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for UserMessage {
fn from(value: &str) -> Self {
Self::new(value)
}
}
#[derive(Default, Debug, Clone)]
pub struct AssistantMessage {
pub content: LanguageModelResponseContentType,
pub usage: Option<Usage>,
}
impl From<String> for AssistantMessage {
fn from(value: String) -> Self {
Self {
content: value.into(),
usage: None,
}
}
}
impl AssistantMessage {
pub fn new(content: LanguageModelResponseContentType, usage: Option<Usage>) -> Self {
Self { content, usage }
}
}
#[derive(Debug, Clone)]
pub struct Initial;
#[derive(Debug, Clone)]
pub struct Conversation;
#[derive(Debug, Clone)]
pub struct MessageBuilder<State = Initial> {
messages: Messages,
state: std::marker::PhantomData<State>,
}
impl MessageBuilder {
pub fn conversation_builder() -> MessageBuilder<Conversation> {
MessageBuilder {
messages: Vec::new(),
state: std::marker::PhantomData,
}
}
}
impl Default for MessageBuilder {
fn default() -> Self {
MessageBuilder {
messages: Vec::new(),
state: std::marker::PhantomData,
}
}
}
impl<State> MessageBuilder<State> {
pub fn build(self) -> Messages {
self.messages
}
}
impl MessageBuilder<Initial> {
pub fn system(mut self, content: impl Into<String>) -> MessageBuilder<Conversation> {
self.messages.push(Message::System(content.into().into()));
MessageBuilder {
messages: self.messages,
state: std::marker::PhantomData,
}
}
pub fn user(mut self, content: impl Into<String>) -> MessageBuilder<Conversation> {
self.messages.push(Message::User(content.into().into()));
MessageBuilder {
messages: self.messages,
state: std::marker::PhantomData,
}
}
}
impl MessageBuilder<Conversation> {
pub fn user(mut self, content: impl Into<String>) -> MessageBuilder<Conversation> {
self.messages.push(Message::User(content.into().into()));
MessageBuilder {
messages: self.messages,
state: std::marker::PhantomData,
}
}
pub fn assistant(mut self, content: impl Into<String>) -> MessageBuilder<Conversation> {
self.messages
.push(Message::Assistant(content.into().into()));
MessageBuilder {
messages: self.messages,
state: std::marker::PhantomData,
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct TaggedMessage {
pub step_id: usize,
pub message: Message,
}
impl TaggedMessage {
pub fn new(step_id: usize, message: Message) -> Self {
Self { step_id, message }
}
pub fn initial_step_msg(message: Message) -> Self {
Self {
step_id: 0,
message,
}
}
}
impl From<Message> for TaggedMessage {
fn from(value: Message) -> Self {
Self::initial_step_msg(value)
}
}
impl From<TaggedMessage> for Message {
fn from(value: TaggedMessage) -> Self {
value.message
}
}
pub(crate) trait TaggedMessageHelpers {
fn extract_tool_calls(&self) -> Option<Vec<ToolCallInfo>>;
fn extract_tool_results(&self) -> Option<Vec<ToolResultInfo>>;
}
impl TaggedMessageHelpers for [TaggedMessage] {
fn extract_tool_calls(&self) -> Option<Vec<ToolCallInfo>> {
let calls: Vec<ToolCallInfo> = self
.iter()
.filter_map(|msg| match msg.message {
Message::Assistant(AssistantMessage {
content: LanguageModelResponseContentType::ToolCall(ref tool_info),
..
}) => Some(tool_info.clone()),
_ => None,
})
.collect();
if calls.is_empty() { None } else { Some(calls) }
}
fn extract_tool_results(&self) -> Option<Vec<ToolResultInfo>> {
let results: Vec<ToolResultInfo> = self
.iter()
.filter_map(|msg| match msg.message {
Message::Tool(ref info) => Some(info.clone()),
_ => None,
})
.collect();
if results.is_empty() {
None
} else {
Some(results)
}
}
}