use crate::options::Options;
use crate::output::Output;
use crate::prompt::{ChatMessageCollection, Prompt, PromptTemplate, StringTemplateError};
use crate::step::Step;
use crate::tokens::{PromptTokensError, TokenizerError};
use crate::traits::{Executor, ExecutorError};
use crate::{parameters, Parameters};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Default)]
pub struct Chain {
state: ChatMessageCollection<String>,
}
impl Chain {
pub fn new(state: PromptTemplate) -> Result<Chain, StringTemplateError> {
state
.format(¶meters!())
.map(|state| state.to_chat())
.map(|state| Self { state })
}
pub fn new_with_message_collection(state: &ChatMessageCollection<String>) -> Chain {
Self {
state: state.clone(),
}
}
pub async fn send_message<E: Executor>(
&mut self,
step: Step,
parameters: &Parameters,
exec: &E,
) -> Result<Output, Error> {
let fmt = step.format(parameters)?;
self.send_message_raw(step.options(), &fmt, exec).await
}
pub async fn send_message_raw<E: Executor>(
&mut self,
options: &Options,
prompt: &Prompt,
exec: &E,
) -> Result<Output, Error> {
let tok = exec.tokens_used(options, prompt)?;
let tokens_remaining = tok.tokens_remaining();
let tokenizer = exec.get_tokenizer(options)?;
self.state.trim_context(&tokenizer, tokens_remaining)?;
let prompt_with_history = Prompt::Chat(self.state.clone()).combine(prompt);
let res = exec.execute(options, &prompt_with_history).await?;
let content = res.to_immediate().await?.as_content().to_chat();
self.state = prompt_with_history.to_chat();
self.state.append(content.clone());
Ok(Output::new_immediate(content.into()))
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("PromptTokensError: {0}")]
PromptTokens(#[from] PromptTokensError),
#[error("TokenizerError: {0}")]
Tokenizer(#[from] TokenizerError),
#[error("ExecutorError: {0}")]
Executor(#[from] ExecutorError),
#[error("No model output")]
NoModelOutput,
#[error("StringTemplateError: {0}")]
StringTemplate(#[from] crate::prompt::StringTemplateError),
}