use crate::error::ProviderError;
use async_trait::async_trait;
use futures_util::{Stream, StreamExt};
use lc_core::language_models::{BaseChatModel, BaseLanguageModel, LLMResult, StreamChunk};
use lc_core::runnables::Runnable;
use lc_core::tools::ToolDefinition;
use lc_core::RunnableConfig;
use lc_schema::Message;
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 }
}
}
#[async_trait]
impl<L> Runnable<Vec<Message>, LLMResult> for ChatModelWrapper<L>
where
L: Runnable<Vec<Message>, LLMResult> + Send + Sync,
L::Error: Into<ProviderError>,
{
type Error = ProviderError;
async fn invoke(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, ProviderError> {
self.inner.invoke(input, config).await.map_err(Into::into)
}
async fn batch(
&self,
inputs: Vec<Vec<Message>>,
config: Option<RunnableConfig>,
) -> Result<Vec<LLMResult>, ProviderError> {
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, ProviderError>> + Send>>, ProviderError>
{
let stream = self.inner.stream(input, config).await.map_err(Into::into)?;
Ok(Box::pin(stream.map(|r| r.map_err(Into::into))))
}
}
#[async_trait]
impl<L> BaseLanguageModel<Vec<Message>, LLMResult> for ChatModelWrapper<L>
where
L: BaseLanguageModel<Vec<Message>, LLMResult> + Send + Sync,
L::Error: Into<ProviderError>,
{
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> BaseChatModel for ChatModelWrapper<L>
where
L: BaseChatModel + Send + Sync,
L::Error: Into<ProviderError>,
{
async fn chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, ProviderError> {
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<StreamChunk, ProviderError>> + Send>>, ProviderError>
{
let stream = self
.inner
.stream_chat(messages, config)
.await
.map_err(Into::into)?;
Ok(Box::pin(stream.map(|r| r.map_err(Into::into))))
}
fn bind_tools(
&self,
tools: Vec<ToolDefinition>,
) -> Option<Box<dyn BaseChatModel<Error = ProviderError> + Send + Sync>> {
self.inner.bind_tools(tools).map(|bound| {
Box::new(BoundModel { inner: bound })
as Box<dyn BaseChatModel<Error = ProviderError> + Send + Sync>
})
}
}
struct BoundModel<E> {
inner: Box<dyn BaseChatModel<Error = E> + Send + Sync>,
}
#[async_trait]
impl<E> Runnable<Vec<Message>, LLMResult> for BoundModel<E>
where
E: Into<ProviderError> + std::error::Error + Send + Sync + 'static,
{
type Error = ProviderError;
async fn invoke(
&self,
input: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, ProviderError> {
self.inner.invoke(input, config).await.map_err(Into::into)
}
async fn batch(
&self,
inputs: Vec<Vec<Message>>,
config: Option<RunnableConfig>,
) -> Result<Vec<LLMResult>, ProviderError> {
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, ProviderError>> + Send>>, ProviderError>
{
let stream = self.inner.stream(input, config).await.map_err(Into::into)?;
Ok(Box::pin(stream.map(|r| r.map_err(Into::into))))
}
}
#[async_trait]
impl<E> BaseLanguageModel<Vec<Message>, LLMResult> for BoundModel<E>
where
E: Into<ProviderError> + std::error::Error + Send + Sync + '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> BaseChatModel for BoundModel<E>
where
E: Into<ProviderError> + std::error::Error + Send + Sync + 'static,
{
async fn chat(
&self,
messages: Vec<Message>,
config: Option<RunnableConfig>,
) -> Result<LLMResult, ProviderError> {
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<StreamChunk, ProviderError>> + Send>>, ProviderError>
{
let stream = self
.inner
.stream_chat(messages, config)
.await
.map_err(Into::into)?;
Ok(Box::pin(stream.map(|r| r.map_err(Into::into))))
}
}
pub fn wrap_chat_model<L>(llm: L) -> Arc<dyn BaseChatModel<Error = ProviderError> + Send + Sync>
where
L: BaseChatModel + Send + Sync + 'static,
L::Error: Into<ProviderError>,
{
Arc::new(ChatModelWrapper::new(llm))
}