use crate::core::language_models::{BaseChatModel, BaseLanguageModel, LLMResult};
use crate::core::runnables::Runnable;
use crate::core::tools::ToolDefinition;
use crate::error::Error;
use crate::schema::Message;
use crate::RunnableConfig;
use async_trait::async_trait;
use futures_util::{Stream, StreamExt};
use std::pin::Pin;
use std::sync::Arc;
pub struct ChatModelWrapper<L> {
inner: L,
}
impl<L> ChatModelWrapper<L> {
pub fn new(llm: L) -> Self {
Self { inner: llm }
}
pub fn inner(&self) -> &L {
&self.inner
}
}
impl<L> BaseLanguageModel<Vec<Message>, LLMResult> for ChatModelWrapper<L>
where
L: BaseChatModel + Send + Sync,
L::Error: Into<Error>,
{
fn model_name(&self) -> &str {
self.inner.model_name()
}
fn get_num_tokens(&self, text: &str) -> usize {
self.inner.get_num_tokens(text)
}
fn temperature(&self) -> Option<f32> {
self.inner.temperature()
}
fn max_tokens(&self) -> Option<usize> {
self.inner.max_tokens()
}
fn with_temperature(self, temp: f32) -> Self
where
Self: Sized,
{
Self {
inner: self.inner.with_temperature(temp),
}
}
fn with_max_tokens(self, max: usize) -> Self
where
Self: Sized,
{
Self {
inner: self.inner.with_max_tokens(max),
}
}
}
#[async_trait]
impl<L> Runnable<Vec<Message>, LLMResult> for ChatModelWrapper<L>
where
L: BaseChatModel + Send + Sync,
L::Error: Into<Error>,
{
type Error = Error;
async fn invoke(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, Error> {
self.inner
.invoke(input, config)
.await
.map_err(Into::into)
}
async fn batch(
&self,
inputs: Vec<Vec<Message>>,
config: Option<RunnableConfig>,
) -> Result<Vec<LLMResult>, Error> {
self.inner
.batch(inputs, config)
.await
.map_err(Into::into)
}
async fn stream(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<Pin<Box<dyn Stream<Item = Result<LLMResult, Error>> + Send>>, Error> {
let inner_stream = self
.inner
.stream(input, config)
.await
.map_err(Into::into)?;
let mapped: Pin<Box<dyn Stream<Item = Result<LLMResult, Error>> + Send>> =
Box::pin(inner_stream.map(|item| item.map_err(Into::into)));
Ok(mapped)
}
}
#[async_trait]
impl<L> BaseChatModel for ChatModelWrapper<L>
where
L: BaseChatModel + Send + Sync,
L::Error: Into<Error>,
{
async fn chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, Error> {
self.inner
.chat(messages, config)
.await
.map_err(Into::into)
}
async fn stream_chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<Pin<Box<dyn Stream<Item = Result<String, Error>> + Send>>, Error> {
let inner_stream = self
.inner
.stream_chat(messages, config)
.await
.map_err(Into::into)?;
let mapped: Pin<Box<dyn Stream<Item = Result<String, Error>> + Send>> =
Box::pin(inner_stream.map(|item| item.map_err(Into::into)));
Ok(mapped)
}
fn bind_tools(
&self,
tools: Vec<ToolDefinition>,
) -> Option<Box<dyn BaseChatModel<Error = Error> + Send + Sync>> {
self.inner
.bind_tools(tools)
.map(|boxed| -> Box<dyn BaseChatModel<Error = Error> + Send + Sync> {
Box::new(BoxedModelWrapper { inner: boxed })
})
}
}
struct BoxedModelWrapper<E: std::error::Error + Send + Sync + Into<Error> + 'static> {
inner: Box<dyn BaseChatModel<Error = E> + Send + Sync>,
}
impl<E> BaseLanguageModel<Vec<Message>, LLMResult> for BoxedModelWrapper<E>
where
E: std::error::Error + Send + Sync + Into<Error> + 'static,
{
fn model_name(&self) -> &str {
self.inner.model_name()
}
fn get_num_tokens(&self, text: &str) -> usize {
self.inner.get_num_tokens(text)
}
fn temperature(&self) -> Option<f32> {
self.inner.temperature()
}
fn max_tokens(&self) -> Option<usize> {
self.inner.max_tokens()
}
fn with_temperature(self, _temp: f32) -> Self
where
Self: Sized,
{
self
}
fn with_max_tokens(self, _max: usize) -> Self
where
Self: Sized,
{
self
}
}
#[async_trait]
impl<E> Runnable<Vec<Message>, LLMResult> for BoxedModelWrapper<E>
where
E: std::error::Error + Send + Sync + Into<Error> + 'static,
{
type Error = Error;
async fn invoke(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, Error> {
self.inner
.invoke(input, config)
.await
.map_err(Into::into)
}
async fn batch(
&self,
inputs: Vec<Vec<Message>>,
config: Option<RunnableConfig>,
) -> Result<Vec<LLMResult>, Error> {
self.inner
.batch(inputs, config)
.await
.map_err(Into::into)
}
async fn stream(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<Pin<Box<dyn Stream<Item = Result<LLMResult, Error>> + Send>>, Error> {
let inner_stream = self
.inner
.stream(input, config)
.await
.map_err(Into::into)?;
let mapped: Pin<Box<dyn Stream<Item = Result<LLMResult, Error>> + Send>> =
Box::pin(inner_stream.map(|item| item.map_err(Into::into)));
Ok(mapped)
}
}
#[async_trait]
impl<E> BaseChatModel for BoxedModelWrapper<E>
where
E: std::error::Error + Send + Sync + Into<Error> + 'static,
{
async fn chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, Error> {
self.inner
.chat(messages, config)
.await
.map_err(Into::into)
}
async fn stream_chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<Pin<Box<dyn Stream<Item = Result<String, Error>> + Send>>, Error> {
let inner_stream = self
.inner
.stream_chat(messages, config)
.await
.map_err(Into::into)?;
let mapped: Pin<Box<dyn Stream<Item = Result<String, Error>> + Send>> =
Box::pin(inner_stream.map(|item| item.map_err(Into::into)));
Ok(mapped)
}
fn bind_tools(
&self,
tools: Vec<ToolDefinition>,
) -> Option<Box<dyn BaseChatModel<Error = Error> + Send + Sync>> {
self.inner
.bind_tools(tools)
.map(|boxed| -> Box<dyn BaseChatModel<Error = Error> + Send + Sync> {
Box::new(BoxedModelWrapper { inner: boxed })
})
}
}
pub fn wrap_chat_model<L>(llm: L) -> Arc<dyn BaseChatModel<Error = Error> + Send + Sync>
where
L: BaseChatModel + Send + Sync + 'static,
L::Error: Into<Error>,
{
Arc::new(ChatModelWrapper::new(llm))
}