//! Chat message types shared across the entity layer. //! //! Provides [`Role`] (conversation participant) and [`ChatMessage`] (a single //! message with optional tool-call metadata). Includes convenience constructors //! for each role: `user`, `assistant`, `system`, `tool`/`tool_result`. //! //! # Flow //! //! Messages are constructed via the typed constructors → pushed into //! [`Conversation`](super::conversation::Conversation) → serialized as JSON //! to `conversation.json`. use serde::{Deserialize, Serialize}; /// The conversation participant who authored a message. /// /// Variants: `User`, `Assistant`, `System`, `Tool`. Serialized as lowercase /// strings (e.g. `"user"`, `"assistant"`, `"system"`, `"tool"`). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Role { #[serde(rename = "user")] User, #[serde(rename = "assistant")] Assistant, #[serde(rename = "system")] System, #[serde(rename = "tool")] Tool, } impl Role { /// Return the role as a lowercase string. pub fn as_str(&self) -> &'static str { match self { Role::User => "user", Role::Assistant => "assistant", Role::System => "system", Role::Tool => "tool", } } } impl std::fmt::Display for Role { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.as_str()) } } /// A single message in a conversation, compatible with the OpenAI/Anthropic /// chat-completion API structures. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ChatMessage { /// Who sent this message (user, assistant, system, tool). pub role: Role, /// The message text content. `None` for assistant messages that only /// contain tool calls. pub content: Option, /// Tool-call requests attached to an assistant message (OpenAI-style). #[serde(skip_serializing_if = "Option::is_none")] pub tool_calls: Option>, /// For tool-role messages: the `id` of the `ToolCall` being responded to. #[serde(skip_serializing_if = "Option::is_none")] pub tool_call_id: Option, /// Optional function name for the tool invocation. #[serde(skip_serializing_if = "Option::is_none")] pub name: Option, } impl ChatMessage { /// Build a user-role message with the given text content. pub fn user(content: impl Into) -> Self { ChatMessage { role: Role::User, content: Some(content.into()), tool_calls: None, tool_call_id: None, name: None, } } /// Build an assistant-role message with an optional text response. pub fn assistant(content: Option) -> Self { ChatMessage { role: Role::Assistant, content, tool_calls: None, tool_call_id: None, name: None, } } /// Build a system-role message with the given instruction text. pub fn system(content: impl Into) -> Self { ChatMessage { role: Role::System, content: Some(content.into()), tool_calls: None, tool_call_id: None, name: None, } } /// Build a tool-role result message referencing a prior tool call. pub fn tool(tool_call_id: String, content: String) -> Self { ChatMessage { role: Role::Tool, content: Some(content), tool_calls: None, tool_call_id: Some(tool_call_id), name: None, } } /// Alias for `tool`, used throughout the codebase for tool results. pub fn tool_result(tool_call_id: String, content: String) -> Self { ChatMessage { role: Role::Tool, content: Some(content), tool_calls: None, tool_call_id: Some(tool_call_id), name: None, } } }