Source code for sherpa_ai.models.chat_model_with_logging

"""Chat model with logging functionality for Sherpa AI.

This module provides a wrapper for chat models that adds detailed logging
capabilities. It defines the ChatModelWithLogging class which logs all
model interactions including inputs, outputs, and model information.
"""

import json
import typing
from typing import Any, Coroutine, List, Optional

from langchain_core.callbacks import ( 
    AsyncCallbackManagerForLLMRun,
    CallbackManagerForLLMRun,
)
from langchain_core.language_models import BaseChatModel 
from langchain_core.messages import BaseMessage 
from langchain_core.outputs import ChatResult 
from loguru import logger 


[docs] class ChatModelWithLogging(BaseChatModel): """Chat model wrapper that adds detailed logging functionality. This class wraps any chat model to add comprehensive logging of all interactions, including input messages, generated responses, and model information. Attributes: llm (BaseChatModel): The underlying chat model to wrap. logger (type(logger)): Logger instance for recording interactions. Example: >>> base_model = ChatOpenAI() >>> model = ChatModelWithLogging(llm=base_model, logger=custom_logger) >>> response = model.generate([Message("Hello")]) >>> # Logs will include input, output, and model info """ llm: BaseChatModel logger: type(logger) @property def _llm_type(self): """Get the type of the underlying language model. Returns: str: Type identifier for the language model. Example: >>> model = ChatModelWithLogging(llm=ChatOpenAI()) >>> print(model._llm_type) 'openai' """ return self.llm._llm_type def _generate( self, messages: List[BaseMessage], stop: Optional[List[str]] = None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs: Any, ) -> ChatResult: """Generate chat completions with detailed logging. This method wraps the underlying model's generation functionality to add comprehensive logging of inputs, outputs, and model information. Args: messages (List[BaseMessage]): List of messages to generate from. stop (Optional[List[str]]): List of stop sequences. run_manager (Optional[CallbackManagerForLLMRun]): Callback manager. **kwargs (Any): Additional arguments for the model. Returns: ChatResult: Generated chat completion result. Example: >>> model = ChatModelWithLogging(llm=ChatOpenAI()) >>> result = model._generate([Message("Hello")]) >>> print(result.generations[0].text) 'Hi there!' >>> # Check logs for detailed interaction info """ # get the name of the language model. For models like OpenAI, this is the model # name (e.g., gpt-3.5-turbo). for other LLMs, this is the type of the LLM llm_name = ( self.llm.model_name if hasattr(self.llm, "model_name") else self._llm_type ) input_text = [] for message in messages: # make sure all the messages stay on the same line input_text.append( {"text": message.content.replace("\n", "\\n"), "agent": message.type} ) result = self.llm._generate(messages, stop, run_manager, **kwargs) # only one generation for a LLM call generation = result.generations[0] log = { "input": input_text, # make sure all the messages stay on the same line "output": generation.message.content.replace("\n", "\\n"), "llm_name": llm_name, } self.logger.info(json.dumps(log)) return result def _agenerate( self, messages: List[BaseMessage], stop: Optional[List[str]] = None, run_manager: Optional[AsyncCallbackManagerForLLMRun] = None, **kwargs: Any, ) -> Coroutine[Any, Any, ChatResult]: """Asynchronously generate chat completions. Args: messages (List[BaseMessage]): List of messages in the conversation. stop (Optional[List[str]]): List of stop sequences. run_manager (Optional[AsyncCallbackManagerForLLMRun]): Callback manager. **kwargs (Any): Additional arguments for the model. Returns: Coroutine[Any, Any, ChatResult]: Coroutine for the generation result. Example: >>> model = ChatModelWithLogging(llm=ChatOpenAI()) >>> result = await model._agenerate([Message("Hello")]) >>> print(result.generations[0].text) 'Hi there!' """ self.llm.agenerate(messages, stop, run_manager, **kwargs)