use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ChatRole {
System,
User,
Assistant,
Tool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ChatContent {
Text { text: String },
ToolCalls {
text: Option<String>,
calls: Vec<ToolCallRequest>,
},
ToolResult { call_id: String, result: String },
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChatMessage {
pub role: ChatRole,
pub content: ChatContent,
}
impl ChatMessage {
pub fn system(text: impl Into<String>) -> Self {
Self {
role: ChatRole::System,
content: ChatContent::Text { text: text.into() },
}
}
pub fn user(text: impl Into<String>) -> Self {
Self {
role: ChatRole::User,
content: ChatContent::Text { text: text.into() },
}
}
pub fn assistant(text: impl Into<String>) -> Self {
Self {
role: ChatRole::Assistant,
content: ChatContent::Text { text: text.into() },
}
}
pub fn assistant_tool_calls(text: Option<String>, calls: Vec<ToolCallRequest>) -> Self {
Self {
role: ChatRole::Assistant,
content: ChatContent::ToolCalls { text, calls },
}
}
pub fn tool_result(call_id: impl Into<String>, result: impl Into<String>) -> Self {
Self {
role: ChatRole::Tool,
content: ChatContent::ToolResult {
call_id: call_id.into(),
result: result.into(),
},
}
}
pub fn text(&self) -> Option<&str> {
match &self.content {
ChatContent::Text { text } => Some(text),
ChatContent::ToolCalls { text, .. } => text.as_deref(),
ChatContent::ToolResult { result, .. } => Some(result),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDef {
pub name: String,
pub description: String,
pub parameters: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolCallRequest {
pub id: String,
pub name: String,
pub arguments: Value,
}
#[derive(Debug, Clone)]
pub enum ChatResponse {
Message(String),
ToolCalls {
text: Option<String>,
calls: Vec<ToolCallRequest>,
},
}
impl ChatResponse {
pub fn text(&self) -> Option<&str> {
match self {
Self::Message(t) => Some(t),
Self::ToolCalls { text, .. } => text.as_deref(),
}
}
pub fn has_tool_calls(&self) -> bool {
matches!(self, Self::ToolCalls { .. })
}
pub fn tool_calls(&self) -> &[ToolCallRequest] {
match self {
Self::Message(_) => &[],
Self::ToolCalls { calls, .. } => calls,
}
}
}