use std::{io, sync::Arc};
use serde::{Deserialize, Serialize};
use crate::{Api, ResponseError, anthropic, anthropic::Message, http_request::HttpRequest};
#[derive(Debug)]
pub struct Action {
pub contents: Vec<anthropic::Content>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Conversation {
system: Option<Arc<str>>,
messages: im::Vector<anthropic::Message>,
tools: im::Vector<anthropic::Tool>,
}
impl Conversation {
pub fn new() -> Self {
Self {
system: None,
messages: im::Vector::new(),
tools: im::Vector::new(),
}
}
pub fn set_system<S: Into<Arc<str>>>(&mut self, system: S) -> &mut Self {
self.system = Some(system.into());
self
}
pub fn user_message<S: Into<String>>(&mut self, api: &Api, user_message: S) -> HttpRequest {
let message = anthropic::Message::from_text(anthropic::Role::User, user_message);
self.build_message(api, message)
}
pub fn tool_results(
&mut self,
api: &Api,
tool_results: Vec<anthropic::ToolResult>,
) -> HttpRequest {
let content = tool_results
.into_iter()
.map(anthropic::Content::ToolResult)
.collect();
let message = anthropic::Message {
role: anthropic::Role::User,
content,
};
self.build_message(api, message)
}
fn build_message(&mut self, api: &Api, message: anthropic::Message) -> HttpRequest {
self.messages.push_back(message);
let mut builder = crate::MessagesRequestBuilder::new().set_messages(self.messages.clone());
if let Some(ref system) = self.system {
builder = builder.system(system.clone());
}
if !self.tools.is_empty() {
builder = builder.set_tools(self.tools.clone());
}
builder.build(api)
}
pub fn handle_response(&mut self, response_json: &str) -> Result<Action, ResponseError> {
let response: anthropic::MessagesResponse = crate::deserialize_response(response_json)?;
self.messages.push_back(response.message.clone());
Ok(Action {
contents: response.message.content,
})
}
pub fn to_json<W: io::Write>(&self, writer: W) -> Result<(), serde_json::Error> {
serde_json::to_writer(writer, self)
}
pub fn from_json<R: io::Read>(reader: R) -> Result<Self, serde_json::Error> {
serde_json::from_reader(reader)
}
pub fn clear(&mut self) {
self.messages = im::Vector::new();
}
pub fn history(&self) -> &im::Vector<Message> {
&self.messages
}
pub fn add_tool(&mut self, tool: anthropic::Tool) -> &mut Self {
self.tools.push_back(tool);
self
}
pub fn set_tools<T: Into<im::Vector<anthropic::Tool>>>(&mut self, tools: T) -> &mut Self {
self.tools = tools.into();
self
}
}
impl Default for Conversation {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use schemars::JsonSchema;
use crate::conversation::Conversation;
#[derive(JsonSchema)]
#[allow(dead_code)]
struct TestToolInput {
param: String,
}
#[test]
fn test_conversation_with_tools() {
let api = crate::Api::new("test-api-key");
let mut conversation = Conversation::new();
let test_tool = crate::anthropic::Tool::new::<TestToolInput, _, _>(
"test_tool",
"A test tool for testing",
);
conversation.add_tool(test_tool);
let http_request = conversation.user_message(&api, "Hello, use the tool!");
assert!(http_request.body.contains("\"tools\":["));
assert!(http_request.body.contains("\"name\":\"test_tool\""));
assert!(
http_request
.body
.contains("\"description\":\"A test tool for testing\"")
);
assert!(http_request.body.contains("\"messages\":["));
assert!(http_request.body.contains("\"Hello, use the tool!\""));
}
}