Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
chat_history.py247 linesDownload Raw Back to langchain_core
1"""Chat message history stores a history of the message interactions in a chat."""2 3from __future__ import annotations4 5from abc import ABC, abstractmethod6from typing import TYPE_CHECKING7 8from pydantic import BaseModel, Field9 10from langchain_core.messages import (11    AIMessage,12    BaseMessage,13    HumanMessage,14    get_buffer_string,15)16from langchain_core.runnables.config import run_in_executor17 18if TYPE_CHECKING:19    from collections.abc import Sequence20 21 22class BaseChatMessageHistory(ABC):23    """Abstract base class for storing chat message history.24 25    Implementations guidelines:26 27    Implementations are expected to over-ride all or some of the following methods:28 29    * `add_messages`: sync variant for bulk addition of messages30    * `aadd_messages`: async variant for bulk addition of messages31    * `messages`: sync variant for getting messages32    * `aget_messages`: async variant for getting messages33    * `clear`: sync variant for clearing messages34    * `aclear`: async variant for clearing messages35 36    `add_messages` contains a default implementation that calls `add_message`37    for each message in the sequence. This is provided for backwards compatibility38    with existing implementations which only had `add_message`.39 40    Async variants all have default implementations that call the sync variants.41    Implementers can choose to override the async implementations to provide42    truly async implementations.43 44    Usage guidelines:45 46    When used for updating history, users should favor usage of `add_messages`47    over `add_message` or other variants like `add_user_message` and `add_ai_message`48    to avoid unnecessary round-trips to the underlying persistence layer.49 50    Example:51        ```python52        import json53        import os54        from langchain_core.messages import messages_from_dict, message_to_dict55 56 57        class FileChatMessageHistory(BaseChatMessageHistory):58            storage_path: str59            session_id: str60 61            @property62            def messages(self) -> list[BaseMessage]:63                try:64                    with open(65                        os.path.join(self.storage_path, self.session_id),66                        "r",67                        encoding="utf-8",68                    ) as f:69                        messages_data = json.load(f)70                    return messages_from_dict(messages_data)71                except FileNotFoundError:72                    return []73 74            def add_messages(self, messages: Sequence[BaseMessage]) -> None:75                all_messages = list(self.messages)  # Existing messages76                all_messages.extend(messages)  # Add new messages77 78                serialized = [message_to_dict(message) for message in all_messages]79                file_path = os.path.join(self.storage_path, self.session_id)80                os.makedirs(os.path.dirname(file_path), exist_ok=True)81                with open(file_path, "w", encoding="utf-8") as f:82                    json.dump(serialized, f)83 84            def clear(self) -> None:85                file_path = os.path.join(self.storage_path, self.session_id)86                os.makedirs(os.path.dirname(file_path), exist_ok=True)87                with open(file_path, "w", encoding="utf-8") as f:88                    json.dump([], f)89        ```90    """91 92    messages: list[BaseMessage]93    """A property or attribute that returns a list of messages.94 95    In general, getting the messages may involve IO to the underlying persistence96    layer, so this operation is expected to incur some latency.97    """98 99    async def aget_messages(self) -> list[BaseMessage]:100        """Async version of getting messages.101 102        Can over-ride this method to provide an efficient async implementation.103 104        In general, fetching messages may involve IO to the underlying persistence105        layer.106 107        Returns:108            The messages.109        """110        return await run_in_executor(None, lambda: self.messages)111 112    def add_user_message(self, message: HumanMessage | str) -> None:113        """Convenience method for adding a human message string to the store.114 115        !!! note116 117            This is a convenience method. Code should favor the bulk `add_messages`118            interface instead to save on round-trips to the persistence layer.119 120        This method may be deprecated in a future release.121 122        Args:123            message: The `HumanMessage` to add to the store.124        """125        if isinstance(message, HumanMessage):126            self.add_message(message)127        else:128            self.add_message(HumanMessage(content=message))129 130    def add_ai_message(self, message: AIMessage | str) -> None:131        """Convenience method for adding an `AIMessage` string to the store.132 133        !!! note134 135            This is a convenience method. Code should favor the bulk `add_messages`136            interface instead to save on round-trips to the persistence layer.137 138        This method may be deprecated in a future release.139 140        Args:141            message: The `AIMessage` to add.142        """143        if isinstance(message, AIMessage):144            self.add_message(message)145        else:146            self.add_message(AIMessage(content=message))147 148    def add_message(self, message: BaseMessage) -> None:149        """Add a Message object to the store.150 151        Args:152            message: A `BaseMessage` object to store.153 154        Raises:155            NotImplementedError: If the sub-class has not implemented an efficient156                `add_messages` method.157        """158        if type(self).add_messages != BaseChatMessageHistory.add_messages:159            # This means that the sub-class has implemented an efficient add_messages160            # method, so we should use it.161            self.add_messages([message])162        else:163            msg = (164                "add_message is not implemented for this class. "165                "Please implement add_message or add_messages."166            )167            raise NotImplementedError(msg)168 169    def add_messages(self, messages: Sequence[BaseMessage]) -> None:170        """Add a list of messages.171 172        Implementations should over-ride this method to handle bulk addition of messages173        in an efficient manner to avoid unnecessary round-trips to the underlying store.174 175        Args:176            messages: A sequence of `BaseMessage` objects to store.177        """178        for message in messages:179            self.add_message(message)180 181    async def aadd_messages(self, messages: Sequence[BaseMessage]) -> None:182        """Async add a list of messages.183 184        Args:185            messages: A sequence of `BaseMessage` objects to store.186        """187        await run_in_executor(None, self.add_messages, messages)188 189    @abstractmethod190    def clear(self) -> None:191        """Remove all messages from the store."""192 193    async def aclear(self) -> None:194        """Async remove all messages from the store."""195        await run_in_executor(None, self.clear)196 197    def __str__(self) -> str:198        """Return a string representation of the chat history."""199        return get_buffer_string(self.messages)200 201 202class InMemoryChatMessageHistory(BaseChatMessageHistory, BaseModel):203    """In memory implementation of chat message history.204 205    Stores messages in a memory list.206    """207 208    messages: list[BaseMessage] = Field(default_factory=list)209    """A list of messages stored in memory."""210 211    async def aget_messages(self) -> list[BaseMessage]:212        """Async version of getting messages.213 214        Can over-ride this method to provide an efficient async implementation.215 216        In general, fetching messages may involve IO to the underlying persistence217        layer.218 219        Returns:220            List of messages.221        """222        return self.messages223 224    def add_message(self, message: BaseMessage) -> None:225        """Add a self-created message to the store.226 227        Args:228            message: The message to add.229        """230        self.messages.append(message)231 232    async def aadd_messages(self, messages: Sequence[BaseMessage]) -> None:233        """Async add messages to the store.234 235        Args:236            messages: The messages to add.237        """238        self.add_messages(messages)239 240    def clear(self) -> None:241        """Clear all messages from the store."""242        self.messages = []243 244    async def aclear(self) -> None:245        """Async clear all messages from the store."""246        self.clear()247 
codekingpro/portable-devtools · Team Ai