use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::async_task::AsyncTask;
use crate::async_task::AsyncStream;
use crate::domain::chunk::{ChatMessageChunk, CompletionChunk};
use crate::domain::prompt::Prompt;
pub trait CompletionModel: Send + Sync + Clone {
fn prompt(&self, prompt: Prompt) -> AsyncStream<CompletionChunk>;
}
pub trait CompletionBackend {
fn submit_completion(
&self,
prompt: &str,
tools: &[String],
) -> crate::async_task::AsyncTask<String>;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompletionRequest {
pub system_prompt: String,
pub chat_history: Vec<crate::domain::Message>,
pub documents: Vec<crate::domain::Document>,
pub tools: Vec<ToolDefinition>,
pub temperature: Option<f64>,
pub max_tokens: Option<u64>,
pub chunk_size: Option<usize>,
pub additional_params: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
pub name: String,
pub description: String,
pub parameters: Value,
}
pub struct CompletionRequestBuilder {
system_prompt: Option<String>,
chat_history: Vec<crate::domain::Message>,
documents: Vec<crate::domain::Document>,
tools: Vec<ToolDefinition>,
temperature: Option<f64>,
max_tokens: Option<u64>,
chunk_size: Option<usize>,
additional_params: Option<Value>,
}
pub struct CompletionRequestBuilderWithHandler {
system_prompt: Option<String>,
chat_history: Vec<crate::domain::Message>,
documents: Vec<crate::domain::Document>,
tools: Vec<ToolDefinition>,
temperature: Option<f64>,
max_tokens: Option<u64>,
chunk_size: Option<usize>,
additional_params: Option<Value>,
error_handler: Box<dyn Fn(String) + Send + Sync>,
}
impl CompletionRequest {
pub fn prompt(system_prompt: impl Into<String>) -> CompletionRequestBuilder {
CompletionRequestBuilder {
system_prompt: Some(system_prompt.into()),
chat_history: Vec::new(),
documents: Vec::new(),
tools: Vec::new(),
temperature: None,
max_tokens: None,
chunk_size: None,
additional_params: None,
}
}
}
impl CompletionRequestBuilder {
pub fn system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
self.system_prompt = Some(system_prompt.into());
self
}
pub fn chat_history(mut self, history: Vec<crate::domain::Message>) -> Self {
self.chat_history = history;
self
}
pub fn add_message(mut self, message: crate::domain::Message) -> Self {
self.chat_history.push(message);
self
}
pub fn user(mut self, content: impl Into<String>) -> Self {
self.chat_history.push(crate::domain::Message::user(content));
self
}
pub fn assistant(mut self, content: impl Into<String>) -> Self {
self.chat_history.push(crate::domain::Message::assistant(content));
self
}
pub fn system(mut self, content: impl Into<String>) -> Self {
self.chat_history.push(crate::domain::Message::system(content));
self
}
pub fn documents(mut self, documents: Vec<crate::domain::Document>) -> Self {
self.documents = documents;
self
}
pub fn add_document(mut self, document: crate::domain::Document) -> Self {
self.documents.push(document);
self
}
pub fn tools(mut self, tools: Vec<ToolDefinition>) -> Self {
self.tools = tools;
self
}
pub fn add_tool(mut self, name: impl Into<String>, description: impl Into<String>, parameters: Value) -> Self {
self.tools.push(ToolDefinition {
name: name.into(),
description: description.into(),
parameters,
});
self
}
pub fn temperature(mut self, temp: f64) -> Self {
self.temperature = Some(temp);
self
}
pub fn max_tokens(mut self, max: u64) -> Self {
self.max_tokens = Some(max);
self
}
pub fn chunk_size(mut self, size: usize) -> Self {
self.chunk_size = Some(size);
self
}
pub fn additional_params(mut self, params: Value) -> Self {
self.additional_params = Some(params);
self
}
pub fn params<F>(mut self, f: F) -> Self
where
F: FnOnce() -> hashbrown::HashMap<String, Value>
{
let params = f();
let json_params: serde_json::Map<String, Value> = params
.into_iter()
.collect();
self.additional_params = Some(Value::Object(json_params));
self
}
pub fn on_error<F>(self, handler: F) -> CompletionRequestBuilderWithHandler
where
F: Fn(String) + Send + Sync + 'static,
{
CompletionRequestBuilderWithHandler {
system_prompt: self.system_prompt,
chat_history: self.chat_history,
documents: self.documents,
tools: self.tools,
temperature: self.temperature,
max_tokens: self.max_tokens,
chunk_size: self.chunk_size,
additional_params: self.additional_params,
error_handler: Box::new(handler),
}
}
}
impl CompletionRequestBuilderWithHandler {
pub fn request(self) -> CompletionRequest {
CompletionRequest {
system_prompt: self.system_prompt.unwrap_or_default(),
chat_history: self.chat_history,
documents: self.documents,
tools: self.tools,
temperature: self.temperature,
max_tokens: self.max_tokens,
chunk_size: self.chunk_size,
additional_params: self.additional_params,
}
}
pub fn complete<F>(self, handler: F) -> AsyncStream<CompletionChunk>
where
F: Fn(CompletionRequest) -> AsyncStream<CompletionChunk> + Send + 'static,
{
handler(self.request())
}
pub fn on_completion<F>(self, f: F) -> AsyncTask<String>
where
F: FnOnce(Result<String, String>) -> String + Send + 'static,
{
AsyncTask::spawn(move || {
let result = Ok("Completion response".to_string());
f(result)
})
}
}