From 00cdf7dc845e9a7fc8aa16eee543e8edb3e81580 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Tue, 1 Oct 2024 17:41:03 -0700 Subject: [PATCH 01/18] add panel callback --- docs/docs/integrations/callbacks/panel.ipynb | 251 ++++++++++++++++++ .../langchain_community/callbacks/__init__.py | 5 + .../callbacks/panel_callback.py | 191 +++++++++++++ .../unit_tests/callbacks/test_imports.py | 1 + .../langchain/langchain/callbacks/__init__.py | 2 + .../unit_tests/callbacks/test_imports.py | 1 + 6 files changed, 451 insertions(+) create mode 100644 docs/docs/integrations/callbacks/panel.ipynb create mode 100644 libs/community/langchain_community/callbacks/panel_callback.py diff --git a/docs/docs/integrations/callbacks/panel.ipynb b/docs/docs/integrations/callbacks/panel.ipynb new file mode 100644 index 0000000000000..6e8f6fe8392ba --- /dev/null +++ b/docs/docs/integrations/callbacks/panel.ipynb @@ -0,0 +1,251 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Panel\n", + "\n", + "> [Panel](https://panel.holoviz.org/index.html) is an open-source Python library designed to streamline the development of robust tools, dashboards, and complex applications entirely within Python. With a comprehensive philosophy,\n", + "> Panel integrates seamlessly with the PyData ecosystem, offering powerful, interactive data tables, visualizations, and much more, to unlock, visualize, share, and collaborate on your data for efficient workflows.\n", + "\n", + "In this guide, we will go over how to setup the `PanelCallbackHandler`. The `PanelCallbackHandler` is useful for rendering and streaming the chain of thought from Langchain objects like Tools, Agents, and Chains. It inherits from Langchain’s BaseCallbackHandler.\n", + "\n", + "Check out the panel-chat-examples docs to see more examples on how to use PanelCallbackHandler. If you have an example to demo, we’d love to add it to the panel-chat-examples gallery!\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation and Setup\n", + "\n", + "```bash\n", + "pip install langchain panel\n", + "```\n", + "\n", + "See full instructions in Panel's [Getting started documentation](https://panel.holoviz.org/getting_started/index.html).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Basic chat with an LLM\n", + "\n", + "To get started:\n", + "\n", + "1. Define a chat callback, like `respond` here.\n", + "2. Pass the instance of a `ChatFeed` or `ChatInterface` to `PanelCallbackHandler`.\n", + "3. Pass the `callback_handler` as a list into `callbacks` when constructing or using Langchain objects like `ChatOpenAI` here." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import panel as pn\n", + "from langchain_community.callbacks import PanelCallbackHandler\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "pn.extension()\n", + "\n", + "def respond(contents):\n", + " llm.invoke(contents)\n", + "\n", + "chat_interface = pn.chat.ChatInterface(callback=respond)\n", + "callback = PanelCallbackHandler(chat_interface)\n", + "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", + "chat_interface" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This example shows the response from the LLM only. A LLM by it self does not show any chain of thought. Later we will build an agent that uses tools. This will show chain of thought." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Async chat with an LLM\n", + "\n", + "Using `async` prevents blocking the main thread, enabling concurrent interactions with the app. This improves responsiveness and user experience.\n", + "\n", + "To do so:\n", + "\n", + "1. Prefix the function with `async`\n", + "2. Prefix the call with `await`\n", + "3. Use `ainvoke` instead of `invoke`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import panel as pn\n", + "from langchain_community.callbacks import PanelCallbackHandler\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "pn.extension()\n", + "\n", + "async def respond(contents):\n", + " await llm.ainvoke(contents)\n", + "\n", + "chat_interface = pn.chat.ChatInterface(callback=respond)\n", + "callback = PanelCallbackHandler(chat_interface)\n", + "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", + "chat_interface" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Agents with Tools\n", + "\n", + "Agents and tools can also be used. Simply pass callback to the `AgentExecutor` and its `invoke` method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import panel as pn\n", + "from langchain import hub\n", + "from langchain.agents import AgentExecutor, create_react_agent, load_tools\n", + "from langchain_community.callbacks import PanelCallbackHandler\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "pn.extension()\n", + "\n", + "def respond(contents):\n", + " agent_executor.invoke({\"input\": contents}, {\"callbacks\": [callback]})\n", + "\n", + "chat_interface = pn.chat.ChatInterface(callback=respond)\n", + "callback = PanelCallbackHandler(chat_interface)\n", + "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", + "tools = load_tools([\"ddg-search\"])\n", + "prompt = hub.pull(\"hwchase17/react\")\n", + "agent = create_react_agent(llm, tools, prompt)\n", + "agent_executor = AgentExecutor(agent=agent, tools=tools, callbacks=[callback])\n", + "\n", + "chat_interface" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Chain with Retrievers\n", + "\n", + "RAG is also possible; simply pass `callback` again. Then ask it what the secret number is!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from uuid import uuid4\n", + "\n", + "from langchain.prompts import ChatPromptTemplate\n", + "from langchain.schema.runnable import RunnablePassthrough\n", + "from langchain.text_splitter import CharacterTextSplitter\n", + "from langchain.vectorstores import Chroma\n", + "from langchain_community.callbacks import PanelCallbackHandler\n", + "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", + "\n", + "import panel as pn\n", + "\n", + "TEXT = \"The secret number is 888.\"\n", + "\n", + "TEMPLATE = \"\"\"Answer the question based only on the following context:\n", + "\n", + "{context}\n", + "\n", + "Question: {question}\n", + "\"\"\"\n", + "\n", + "pn.extension(design=\"material\")\n", + "\n", + "\n", + "@pn.cache\n", + "def get_vector_store():\n", + " text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=100)\n", + " texts = text_splitter.split_text(TEXT)\n", + " embeddings = OpenAIEmbeddings()\n", + " db = Chroma.from_texts(texts, embeddings)\n", + " return db\n", + "\n", + "def get_chain(callbacks):\n", + " retriever = db.as_retriever(callbacks=callbacks)\n", + " model = ChatOpenAI(callbacks=callbacks, streaming=True)\n", + "\n", + " def format_docs(docs):\n", + " text = \"\\n\\n\".join([d.page_content for d in docs])\n", + " return text\n", + "\n", + " def hack(docs):\n", + " # https://github.com/langchain-ai/langchain/issues/7290\n", + " for callback in callbacks:\n", + " callback.on_retriever_end(docs, run_id=uuid4())\n", + " return docs\n", + "\n", + " return (\n", + " {\"context\": retriever | hack | format_docs, \"question\": RunnablePassthrough()}\n", + " | prompt\n", + " | model\n", + " )\n", + "\n", + "async def respond(contents):\n", + " chain = get_chain(callbacks=[callback])\n", + " await chain.ainvoke(contents)\n", + "\n", + "db = get_vector_store()\n", + "prompt = ChatPromptTemplate.from_template(TEMPLATE)\n", + "chat_interface = pn.chat.ChatInterface(callback=respond)\n", + "callback = PanelCallbackHandler(chat_interface)\n", + "\n", + "chat_interface" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/libs/community/langchain_community/callbacks/__init__.py b/libs/community/langchain_community/callbacks/__init__.py index 5d36b91f4b1d9..ca004f2577cab 100644 --- a/libs/community/langchain_community/callbacks/__init__.py +++ b/libs/community/langchain_community/callbacks/__init__.py @@ -60,6 +60,9 @@ from langchain_community.callbacks.openai_info import ( OpenAICallbackHandler, ) + from langchain_community.callbacks.panel import ( + PanelCallbackHandler, + ) from langchain_community.callbacks.promptlayer_callback import ( PromptLayerCallbackHandler, ) @@ -105,6 +108,7 @@ "LabelStudioCallbackHandler": "langchain_community.callbacks.labelstudio_callback", "MlflowCallbackHandler": "langchain_community.callbacks.mlflow_callback", "OpenAICallbackHandler": "langchain_community.callbacks.openai_info", + "PanelCallbackHandler": "langchain_community.callbacks.panel_callback", "PromptLayerCallbackHandler": "langchain_community.callbacks.promptlayer_callback", "SageMakerCallbackHandler": "langchain_community.callbacks.sagemaker_callback", "StreamlitCallbackHandler": "langchain_community.callbacks.streamlit", @@ -143,6 +147,7 @@ def __getattr__(name: str) -> Any: "LabelStudioCallbackHandler", "MlflowCallbackHandler", "OpenAICallbackHandler", + "PanelCallbackHandler", "PromptLayerCallbackHandler", "SageMakerCallbackHandler", "StreamlitCallbackHandler", diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py new file mode 100644 index 0000000000000..c658e97c24a90 --- /dev/null +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -0,0 +1,191 @@ +"""The langchain module integrates Langchain support with Panel.""" + +from __future__ import annotations + +from typing import Any, Union, TYPE_CHECKING + +from langchain_core.utils import guard_import +from langchain.callbacks.base import BaseCallbackHandler +from langchain.schema import AgentAction, AgentFinish, LLMResult + +if TYPE_CHECKING: + import panel + from panel.chat.feed import ChatFeed + from panel.chat.interface import ChatInterface + + +def import_panel() -> panel: + """Import panel""" + return guard_import("panel") + + +class PanelCallbackHandler(BaseCallbackHandler): + """ + The Langchain `PanelCallbackHandler` itself is not a widget or pane, but is useful for rendering + and streaming the *chain of thought* from Langchain Tools, Agents, and Chains + as `ChatMessage` objects. + + :Example: + + >>> chat_interface = pn.widgets.ChatInterface(callback=callback, callback_user="Langchain") + >>> callback_handler = pn.widgets.langchain.PanelCallbackHandler(instance=chat_interface) + >>> llm = ChatOpenAI(streaming=True, callbacks=[callback_handler]) + >>> chain = ConversationChain(llm=llm) + + Args: + instance (ChatFeed | ChatInterface): The ChatFeed or ChatInterface instance to stream messages to. + user (str, optional): The user to display in the chat feed. Defaults to "LangChain". + avatar (str, optional): The avatar to display in the chat feed. Defaults to DEFAULT_AVATARS["langchain"]. + """ + + def __init__( + self, + instance: "ChatFeed" | "ChatInterface", + user: str = "LangChain", + avatar: str | None = None, + ): + if BaseCallbackHandler is object: + raise ImportError( + "LangChainCallbackHandler requires `langchain` to be installed." + ) + + panel = import_panel() + self._default_avatars = panel.chat.message.DEFAULT_AVATARS + avatar = avatar or self._default_avatars["langchain"] + + self.instance = instance + self._message = None + self._active_user = user + self._active_avatar = avatar + self._disabled_state = self.instance.disabled + self._is_streaming = None + + self._input_user = user # original user + self._input_avatar = avatar + + def _update_active(self, avatar: str, label: str): + """ + Prevent duplicate labels from being appended to the same user. + """ + # not a typo; Langchain passes a string :/ + if label == "None": + return + + self._active_avatar = avatar + if f"- {label}" not in self._active_user: + self._active_user = f"{self._active_user} - {label}" + + def _reset_active(self): + self._active_user = self._input_user + self._active_avatar = self._input_avatar + self._message = None + + def _on_start(self, serialized, kwargs): + model = kwargs.get("invocation_params", {}).get("model_name", "") + self._is_streaming = serialized.get("kwargs", {}).get("streaming") + messages = self.instance.objects + if messages[-1].user != self._active_user: + self._message = None + if self._active_user and model not in self._active_user: + self._active_user = f"{self._active_user} ({model})" + + def _stream(self, message: str): + if message: + return self.instance.stream( + message, + user=self._active_user, + avatar=self._active_avatar, + message=self._message, + ) + return self._message + + def on_llm_start(self, serialized: dict[str, Any], *args, **kwargs): + self._on_start(serialized, kwargs) + return super().on_llm_start(serialized, *args, **kwargs) + + def on_llm_new_token(self, token: str, **kwargs) -> None: + self._message = self._stream(token) + return super().on_llm_new_token(token, **kwargs) + + def on_llm_end(self, response: LLMResult, *args, **kwargs): + if not self._is_streaming: + # on_llm_new_token does not get called if not streaming + self._stream(response.generations[0][0].text) + + self._reset_active() + return super().on_llm_end(response, *args, **kwargs) + + def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], *args, **kwargs): + return super().on_llm_error(error, *args, **kwargs) + + def on_agent_action(self, action: AgentAction, *args, **kwargs: Any) -> Any: + return super().on_agent_action(action, *args, **kwargs) + + def on_agent_finish(self, finish: AgentFinish, *args, **kwargs: Any) -> Any: + return super().on_agent_finish(finish, *args, **kwargs) + + def on_tool_start( + self, serialized: dict[str, Any], input_str: str, *args, **kwargs + ): + self._update_active(self._default_avatars["tool"], serialized["name"]) + self._stream(f"Tool input: {input_str}") + return super().on_tool_start(serialized, input_str, *args, **kwargs) + + def on_tool_end(self, output: str, *args, **kwargs): + self._stream(output) + self._reset_active() + return super().on_tool_end(output, *args, **kwargs) + + def on_tool_error( + self, error: Union[Exception, KeyboardInterrupt], *args, **kwargs + ): + return super().on_tool_error(error, *args, **kwargs) + + def on_chain_start( + self, serialized: dict[str, Any], inputs: dict[str, Any], *args, **kwargs + ): + self._disabled_state = self.instance.disabled + self.instance.disabled = True + return super().on_chain_start(serialized, inputs, *args, **kwargs) + + def on_chain_end(self, outputs: dict[str, Any], *args, **kwargs): + self.instance.disabled = self._disabled_state + return super().on_chain_end(outputs, *args, **kwargs) + + def on_retriever_error( + self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any + ) -> Any: + """Run when Retriever errors.""" + return super().on_retriever_error(error, **kwargs) + + def on_retriever_end(self, documents, **kwargs: Any) -> Any: + """Run when Retriever ends running.""" + panel = import_panel() + + objects = [ + (f"Document {index}", document.page_content) + for index, document in enumerate(documents) + ] + message = panel.Accordion( + *objects, sizing_mode="stretch_width", margin=(10, 13, 10, 5) + ) + self.instance.send( + message, + user="LangChain (retriever)", + avatar=self._default_avatars["retriever"], + respond=False, + ) + return super().on_retriever_end(documents=documents, **kwargs) + + def on_text(self, text: str, **kwargs: Any): + """Run when text is received.""" + return super().on_text(text, **kwargs) + + def on_chat_model_start( + self, serialized: dict[str, Any], messages: list, **kwargs: Any + ) -> None: + """ + To prevent the inherited class from raising + NotImplementedError, will not call super() here. + """ + self._on_start(serialized, kwargs) diff --git a/libs/community/tests/unit_tests/callbacks/test_imports.py b/libs/community/tests/unit_tests/callbacks/test_imports.py index 566099cbdd0ef..eed7538250cad 100644 --- a/libs/community/tests/unit_tests/callbacks/test_imports.py +++ b/libs/community/tests/unit_tests/callbacks/test_imports.py @@ -4,6 +4,7 @@ "AimCallbackHandler", "ArgillaCallbackHandler", "ArizeCallbackHandler", + "PanelCallbackHandler", "PromptLayerCallbackHandler", "ArthurCallbackHandler", "ClearMLCallbackHandler", diff --git a/libs/langchain/langchain/callbacks/__init__.py b/libs/langchain/langchain/callbacks/__init__.py index 92690fc9029d0..3f66a1a0dc032 100644 --- a/libs/langchain/langchain/callbacks/__init__.py +++ b/libs/langchain/langchain/callbacks/__init__.py @@ -50,6 +50,7 @@ ) from langchain_community.callbacks.mlflow_callback import MlflowCallbackHandler from langchain_community.callbacks.openai_info import OpenAICallbackHandler + from langchain_community.callbacks.panel_callback import PanelCallbackHandler from langchain_community.callbacks.promptlayer_callback import ( PromptLayerCallbackHandler, ) @@ -124,6 +125,7 @@ def __getattr__(name: str) -> Any: "FinalStreamingStdOutCallbackHandler", "LLMThoughtLabeler", "LangChainTracer", + "PanelCallbackHandler", "StreamlitCallbackHandler", "WandbCallbackHandler", "WhyLabsCallbackHandler", diff --git a/libs/langchain/tests/unit_tests/callbacks/test_imports.py b/libs/langchain/tests/unit_tests/callbacks/test_imports.py index a7bd7d366d668..c5570d017371a 100644 --- a/libs/langchain/tests/unit_tests/callbacks/test_imports.py +++ b/libs/langchain/tests/unit_tests/callbacks/test_imports.py @@ -4,6 +4,7 @@ "AimCallbackHandler", "ArgillaCallbackHandler", "ArizeCallbackHandler", + "PanelCallbackHandler", "PromptLayerCallbackHandler", "ArthurCallbackHandler", "ClearMLCallbackHandler", From 5a64c5ed0cb36d6d39d4699a17e45697fee6aa0c Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Tue, 1 Oct 2024 17:48:35 -0700 Subject: [PATCH 02/18] fix bug and add test --- .../callbacks/panel_callback.py | 2 +- .../callbacks/test_panel_callback.py | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 libs/community/tests/integration_tests/callbacks/test_panel_callback.py diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index c658e97c24a90..4e67aca12f2bb 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -84,7 +84,7 @@ def _on_start(self, serialized, kwargs): model = kwargs.get("invocation_params", {}).get("model_name", "") self._is_streaming = serialized.get("kwargs", {}).get("streaming") messages = self.instance.objects - if messages[-1].user != self._active_user: + if not messages or messages[-1].user != self._active_user: self._message = None if self._active_user and model not in self._active_user: self._active_user = f"{self._active_user} ({model})" diff --git a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py new file mode 100644 index 0000000000000..3ed24baa25eda --- /dev/null +++ b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py @@ -0,0 +1,25 @@ +"""Integration tests for the PanelCallbackHandler module.""" + +import pytest + +# Import the internal PanelCallbackHandler from its module - and not from +# the `langchain_community.callbacks.streamlit` package - so that we don't end up using +# Streamlit's externally-provided callback handler. +from langchain_community.callbacks.panel_callback import ( + PanelCallbackHandler, +) +from langchain_openai import ChatOpenAI + + +@pytest.mark.requires("panel") +def test_panel_callback() -> None: + import panel as pn + + chat_interface = pn.chat.ChatInterface() + callback = PanelCallbackHandler(chat_interface) + llm = ChatOpenAI(model_name="gpt-4o-mini", streaming=True, callbacks=[callback]) + llm.invoke("hey") + objects = chat_interface.objects + assert len(objects) == 1 + assert objects[0].avatar == pn.chat.message.DEFAULT_AVATARS["langchain"] + assert objects[0].user == "LangChain (gpt-4o-mini)" From fd46cc515d70b1754d9125aa0a2152da3ba73886 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Tue, 1 Oct 2024 17:51:38 -0700 Subject: [PATCH 03/18] lint --- .../community/langchain_community/callbacks/panel_callback.py | 4 ++-- .../tests/integration_tests/callbacks/test_panel_callback.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index 4e67aca12f2bb..33b9ad9da8929 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -2,11 +2,11 @@ from __future__ import annotations -from typing import Any, Union, TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Union -from langchain_core.utils import guard_import from langchain.callbacks.base import BaseCallbackHandler from langchain.schema import AgentAction, AgentFinish, LLMResult +from langchain_core.utils import guard_import if TYPE_CHECKING: import panel diff --git a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py index 3ed24baa25eda..74d4764ba513f 100644 --- a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py +++ b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py @@ -1,6 +1,7 @@ """Integration tests for the PanelCallbackHandler module.""" import pytest +from langchain_openai import ChatOpenAI # Import the internal PanelCallbackHandler from its module - and not from # the `langchain_community.callbacks.streamlit` package - so that we don't end up using @@ -8,7 +9,6 @@ from langchain_community.callbacks.panel_callback import ( PanelCallbackHandler, ) -from langchain_openai import ChatOpenAI @pytest.mark.requires("panel") From 949b6e5dee6f1e6924aa138fbf0b1580f91b2869 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Wed, 2 Oct 2024 12:34:27 -0700 Subject: [PATCH 04/18] use chat step --- .../callbacks/panel_callback.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index 33b9ad9da8929..2bd7a517b96bb 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -43,22 +43,25 @@ def __init__( instance: "ChatFeed" | "ChatInterface", user: str = "LangChain", avatar: str | None = None, + step_width: int = 500, ): if BaseCallbackHandler is object: raise ImportError( "LangChainCallbackHandler requires `langchain` to be installed." ) - panel = import_panel() - self._default_avatars = panel.chat.message.DEFAULT_AVATARS + self._pn = import_panel() + self._default_avatars = self._pn.chat.message.DEFAULT_AVATARS avatar = avatar or self._default_avatars["langchain"] self.instance = instance + self._step = None self._message = None self._active_user = user self._active_avatar = avatar self._disabled_state = self.instance.disabled self._is_streaming = None + self._step_width = step_width self._input_user = user # original user self._input_avatar = avatar @@ -128,12 +131,21 @@ def on_tool_start( self, serialized: dict[str, Any], input_str: str, *args, **kwargs ): self._update_active(self._default_avatars["tool"], serialized["name"]) - self._stream(f"Tool input: {input_str}") + self._step = self.instance.add_step( + title=f"Tool input: {input_str}", + status="running", + user=self._active_user, + avatar=self._active_avatar, + width=self._step_width, + default_layout="column", + ) return super().on_tool_start(serialized, input_str, *args, **kwargs) def on_tool_end(self, output: str, *args, **kwargs): - self._stream(output) + self._step.stream(output) + self._step.status = "success" self._reset_active() + self._step = None return super().on_tool_end(output, *args, **kwargs) def on_tool_error( @@ -160,13 +172,11 @@ def on_retriever_error( def on_retriever_end(self, documents, **kwargs: Any) -> Any: """Run when Retriever ends running.""" - panel = import_panel() - objects = [ (f"Document {index}", document.page_content) for index, document in enumerate(documents) ] - message = panel.Accordion( + message = self._pn.Accordion( *objects, sizing_mode="stretch_width", margin=(10, 13, 10, 5) ) self.instance.send( From ecd4e410c6d7313c8bea58b5be7251ee7fe491e5 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 18:47:46 -0800 Subject: [PATCH 05/18] lint --- .../callbacks/panel_callback.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index 2bd7a517b96bb..acf2a3afe3b3f 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -21,21 +21,28 @@ def import_panel() -> panel: class PanelCallbackHandler(BaseCallbackHandler): """ - The Langchain `PanelCallbackHandler` itself is not a widget or pane, but is useful for rendering - and streaming the *chain of thought* from Langchain Tools, Agents, and Chains - as `ChatMessage` objects. + The Langchain `PanelCallbackHandler` itself is not a widget or pane, + but is useful for rendering and streaming the *chain of thought* from + Langchain Tools, Agents, and Chains as `ChatMessage` objects. :Example: - >>> chat_interface = pn.widgets.ChatInterface(callback=callback, callback_user="Langchain") - >>> callback_handler = pn.widgets.langchain.PanelCallbackHandler(instance=chat_interface) + >>> chat_interface = pn.widgets.ChatInterface( + callback=callback, callback_user="Langchain" + ) + >>> callback_handler = pn.widgets.langchain.PanelCallbackHandler( + instance=chat_interface + ) >>> llm = ChatOpenAI(streaming=True, callbacks=[callback_handler]) >>> chain = ConversationChain(llm=llm) Args: - instance (ChatFeed | ChatInterface): The ChatFeed or ChatInterface instance to stream messages to. - user (str, optional): The user to display in the chat feed. Defaults to "LangChain". - avatar (str, optional): The avatar to display in the chat feed. Defaults to DEFAULT_AVATARS["langchain"]. + instance (ChatFeed | ChatInterface): The ChatFeed or ChatInterface + instance to stream messages to. + user (str, optional): The user to display in the chat feed. + Defaults to "LangChain". + avatar (str, optional): The avatar to display in the chat feed. + Defaults to DEFAULT_AVATARS["langchain"]. """ def __init__( From da06c1a9ddd61e436a79afd5c066054edc3558ff Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 18:47:52 -0800 Subject: [PATCH 06/18] fix test --- libs/community/langchain_community/callbacks/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/community/langchain_community/callbacks/__init__.py b/libs/community/langchain_community/callbacks/__init__.py index ca004f2577cab..fc35e0401609f 100644 --- a/libs/community/langchain_community/callbacks/__init__.py +++ b/libs/community/langchain_community/callbacks/__init__.py @@ -60,7 +60,7 @@ from langchain_community.callbacks.openai_info import ( OpenAICallbackHandler, ) - from langchain_community.callbacks.panel import ( + from langchain_community.callbacks.panel_callback import ( PanelCallbackHandler, ) from langchain_community.callbacks.promptlayer_callback import ( From 50bd414fb78fac11ceb3f39a240eba530bd42694 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:18:27 -0800 Subject: [PATCH 07/18] more linting --- .../callbacks/panel_callback.py | 157 +++++++++--------- 1 file changed, 81 insertions(+), 76 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index acf2a3afe3b3f..1c3de3df64d9a 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -1,17 +1,17 @@ -"""The langchain module integrates Langchain support with Panel.""" - from __future__ import annotations -from typing import TYPE_CHECKING, Any, Union +from typing import TYPE_CHECKING, Any, Optional, Union +from uuid import UUID from langchain.callbacks.base import BaseCallbackHandler -from langchain.schema import AgentAction, AgentFinish, LLMResult +from langchain.schema import AgentAction, AgentFinish, LLMResult, Document from langchain_core.utils import guard_import if TYPE_CHECKING: import panel from panel.chat.feed import ChatFeed from panel.chat.interface import ChatInterface + from panel.widgets import ChatMessage def import_panel() -> panel: @@ -20,38 +20,13 @@ def import_panel() -> panel: class PanelCallbackHandler(BaseCallbackHandler): - """ - The Langchain `PanelCallbackHandler` itself is not a widget or pane, - but is useful for rendering and streaming the *chain of thought* from - Langchain Tools, Agents, and Chains as `ChatMessage` objects. - - :Example: - - >>> chat_interface = pn.widgets.ChatInterface( - callback=callback, callback_user="Langchain" - ) - >>> callback_handler = pn.widgets.langchain.PanelCallbackHandler( - instance=chat_interface - ) - >>> llm = ChatOpenAI(streaming=True, callbacks=[callback_handler]) - >>> chain = ConversationChain(llm=llm) - - Args: - instance (ChatFeed | ChatInterface): The ChatFeed or ChatInterface - instance to stream messages to. - user (str, optional): The user to display in the chat feed. - Defaults to "LangChain". - avatar (str, optional): The avatar to display in the chat feed. - Defaults to DEFAULT_AVATARS["langchain"]. - """ - def __init__( self, - instance: "ChatFeed" | "ChatInterface", + instance: ChatFeed | ChatInterface, user: str = "LangChain", avatar: str | None = None, step_width: int = 500, - ): + ) -> None: if BaseCallbackHandler is object: raise ImportError( "LangChainCallbackHandler requires `langchain` to be installed." @@ -62,22 +37,18 @@ def __init__( avatar = avatar or self._default_avatars["langchain"] self.instance = instance - self._step = None - self._message = None + self._step: Any = None # Can be None or panel.widgets.ChatMessage + self._message: Optional[ChatMessage] = None self._active_user = user self._active_avatar = avatar self._disabled_state = self.instance.disabled - self._is_streaming = None + self._is_streaming: Optional[bool] = None self._step_width = step_width - self._input_user = user # original user + self._input_user = user self._input_avatar = avatar - def _update_active(self, avatar: str, label: str): - """ - Prevent duplicate labels from being appended to the same user. - """ - # not a typo; Langchain passes a string :/ + def _update_active(self, avatar: str, label: str) -> None: if label == "None": return @@ -85,12 +56,12 @@ def _update_active(self, avatar: str, label: str): if f"- {label}" not in self._active_user: self._active_user = f"{self._active_user} - {label}" - def _reset_active(self): + def _reset_active(self) -> None: self._active_user = self._input_user self._active_avatar = self._input_avatar self._message = None - def _on_start(self, serialized, kwargs): + def _on_start(self, serialized: dict[str, Any], kwargs: dict[str, Any]) -> None: model = kwargs.get("invocation_params", {}).get("model_name", "") self._is_streaming = serialized.get("kwargs", {}).get("streaming") messages = self.instance.objects @@ -99,7 +70,7 @@ def _on_start(self, serialized, kwargs): if self._active_user and model not in self._active_user: self._active_user = f"{self._active_user} ({model})" - def _stream(self, message: str): + def _stream(self, message: str) -> Optional[ChatMessage]: if message: return self.instance.stream( message, @@ -109,34 +80,51 @@ def _stream(self, message: str): ) return self._message - def on_llm_start(self, serialized: dict[str, Any], *args, **kwargs): + def on_llm_start( + self, + serialized: dict[str, Any], + *args: Any, + **kwargs: Any, + ) -> Any: self._on_start(serialized, kwargs) return super().on_llm_start(serialized, *args, **kwargs) - def on_llm_new_token(self, token: str, **kwargs) -> None: + def on_llm_new_token(self, token: str, **kwargs: Any) -> None: self._message = self._stream(token) return super().on_llm_new_token(token, **kwargs) - def on_llm_end(self, response: LLMResult, *args, **kwargs): + def on_llm_end(self, response: LLMResult, *args: Any, **kwargs: Any) -> Any: if not self._is_streaming: - # on_llm_new_token does not get called if not streaming self._stream(response.generations[0][0].text) self._reset_active() return super().on_llm_end(response, *args, **kwargs) - def on_llm_error(self, error: Union[Exception, KeyboardInterrupt], *args, **kwargs): - return super().on_llm_error(error, *args, **kwargs) + def on_llm_error( + self, + error: BaseException, + *, + run_id: UUID, + parent_run_id: Optional[UUID] = None, + **kwargs: Any, + ) -> Any: + return super().on_llm_error( + error, run_id=run_id, parent_run_id=parent_run_id, **kwargs + ) - def on_agent_action(self, action: AgentAction, *args, **kwargs: Any) -> Any: + def on_agent_action(self, action: AgentAction, *args: Any, **kwargs: Any) -> Any: return super().on_agent_action(action, *args, **kwargs) - def on_agent_finish(self, finish: AgentFinish, *args, **kwargs: Any) -> Any: + def on_agent_finish(self, finish: AgentFinish, *args: Any, **kwargs: Any) -> Any: return super().on_agent_finish(finish, *args, **kwargs) def on_tool_start( - self, serialized: dict[str, Any], input_str: str, *args, **kwargs - ): + self, + serialized: dict[str, Any], + input_str: str, + *args: Any, + **kwargs: Any, + ) -> Any: self._update_active(self._default_avatars["tool"], serialized["name"]) self._step = self.instance.add_step( title=f"Tool input: {input_str}", @@ -148,37 +136,56 @@ def on_tool_start( ) return super().on_tool_start(serialized, input_str, *args, **kwargs) - def on_tool_end(self, output: str, *args, **kwargs): - self._step.stream(output) - self._step.status = "success" + def on_tool_end(self, output: str, *args: Any, **kwargs: Any) -> Any: + if self._step is not None: + self._step.stream(output) + self._step.status = "success" self._reset_active() self._step = None return super().on_tool_end(output, *args, **kwargs) def on_tool_error( - self, error: Union[Exception, KeyboardInterrupt], *args, **kwargs - ): - return super().on_tool_error(error, *args, **kwargs) + self, + error: BaseException, + *, + run_id: UUID, + parent_run_id: Optional[UUID] = None, + **kwargs: Any, + ) -> Any: + return super().on_tool_error( + error, run_id=run_id, parent_run_id=parent_run_id, **kwargs + ) def on_chain_start( - self, serialized: dict[str, Any], inputs: dict[str, Any], *args, **kwargs - ): + self, + serialized: dict[str, Any], + inputs: dict[str, Any], + *args: Any, + **kwargs: Any, + ) -> Any: self._disabled_state = self.instance.disabled self.instance.disabled = True return super().on_chain_start(serialized, inputs, *args, **kwargs) - def on_chain_end(self, outputs: dict[str, Any], *args, **kwargs): + def on_chain_end( + self, outputs: dict[str, Any], *args: Any, **kwargs: Any + ) -> Any: self.instance.disabled = self._disabled_state return super().on_chain_end(outputs, *args, **kwargs) def on_retriever_error( - self, error: Union[Exception, KeyboardInterrupt], **kwargs: Any + self, + error: BaseException, + *, + run_id: UUID, + parent_run_id: Optional[UUID] = None, + **kwargs: Any, ) -> Any: - """Run when Retriever errors.""" - return super().on_retriever_error(error, **kwargs) + return super().on_retriever_error( + error, run_id=run_id, parent_run_id=parent_run_id, **kwargs + ) - def on_retriever_end(self, documents, **kwargs: Any) -> Any: - """Run when Retriever ends running.""" + def on_retriever_end(self, documents: list[Document], **kwargs: Any) -> Any: objects = [ (f"Document {index}", document.page_content) for index, document in enumerate(documents) @@ -194,15 +201,13 @@ def on_retriever_end(self, documents, **kwargs: Any) -> Any: ) return super().on_retriever_end(documents=documents, **kwargs) - def on_text(self, text: str, **kwargs: Any): - """Run when text is received.""" - return super().on_text(text, **kwargs) + def on_text(self, text: str, **kwargs: Any) -> None: + super().on_text(text, **kwargs) def on_chat_model_start( - self, serialized: dict[str, Any], messages: list, **kwargs: Any + self, + serialized: dict[str, Any], + messages: list[Any], + **kwargs: Any, ) -> None: - """ - To prevent the inherited class from raising - NotImplementedError, will not call super() here. - """ - self._on_start(serialized, kwargs) + self._on_start(serialized, kwargs) \ No newline at end of file From 8e4a0851aac51eddb4b407dc9b13b9f74c76018b Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:26:20 -0800 Subject: [PATCH 08/18] fix linting --- docs/docs/integrations/callbacks/panel.ipynb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/docs/integrations/callbacks/panel.ipynb b/docs/docs/integrations/callbacks/panel.ipynb index 6e8f6fe8392ba..e280b10f6f961 100644 --- a/docs/docs/integrations/callbacks/panel.ipynb +++ b/docs/docs/integrations/callbacks/panel.ipynb @@ -52,9 +52,11 @@ "\n", "pn.extension()\n", "\n", + "\n", "def respond(contents):\n", " llm.invoke(contents)\n", "\n", + "\n", "chat_interface = pn.chat.ChatInterface(callback=respond)\n", "callback = PanelCallbackHandler(chat_interface)\n", "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", @@ -95,9 +97,11 @@ "\n", "pn.extension()\n", "\n", + "\n", "async def respond(contents):\n", " await llm.ainvoke(contents)\n", "\n", + "\n", "chat_interface = pn.chat.ChatInterface(callback=respond)\n", "callback = PanelCallbackHandler(chat_interface)\n", "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", @@ -127,9 +131,11 @@ "\n", "pn.extension()\n", "\n", + "\n", "def respond(contents):\n", " agent_executor.invoke({\"input\": contents}, {\"callbacks\": [callback]})\n", "\n", + "\n", "chat_interface = pn.chat.ChatInterface(callback=respond)\n", "callback = PanelCallbackHandler(chat_interface)\n", "llm = ChatOpenAI(model_name=\"gpt-4o-mini\", streaming=True, callbacks=[callback])\n", @@ -187,6 +193,7 @@ " db = Chroma.from_texts(texts, embeddings)\n", " return db\n", "\n", + "\n", "def get_chain(callbacks):\n", " retriever = db.as_retriever(callbacks=callbacks)\n", " model = ChatOpenAI(callbacks=callbacks, streaming=True)\n", @@ -207,10 +214,12 @@ " | model\n", " )\n", "\n", + "\n", "async def respond(contents):\n", " chain = get_chain(callbacks=[callback])\n", " await chain.ainvoke(contents)\n", "\n", + "\n", "db = get_vector_store()\n", "prompt = ChatPromptTemplate.from_template(TEMPLATE)\n", "chat_interface = pn.chat.ChatInterface(callback=respond)\n", From f781ebe664c21a289258abce20b4e2d2e7e93a59 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:33:10 -0800 Subject: [PATCH 09/18] lint --- docs/docs/integrations/callbacks/panel.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/docs/integrations/callbacks/panel.ipynb b/docs/docs/integrations/callbacks/panel.ipynb index e280b10f6f961..c4b0dbe633c7d 100644 --- a/docs/docs/integrations/callbacks/panel.ipynb +++ b/docs/docs/integrations/callbacks/panel.ipynb @@ -164,6 +164,7 @@ "source": [ "from uuid import uuid4\n", "\n", + "import panel as pn\n", "from langchain.prompts import ChatPromptTemplate\n", "from langchain.schema.runnable import RunnablePassthrough\n", "from langchain.text_splitter import CharacterTextSplitter\n", @@ -171,8 +172,6 @@ "from langchain_community.callbacks import PanelCallbackHandler\n", "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n", "\n", - "import panel as pn\n", - "\n", "TEXT = \"The secret number is 888.\"\n", "\n", "TEMPLATE = \"\"\"Answer the question based only on the following context:\n", From 6abea5ae1d88cddcbc32d2f85fa22981f0f3f85b Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:35:39 -0800 Subject: [PATCH 10/18] update test --- .../tests/integration_tests/callbacks/test_panel_callback.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py index 74d4764ba513f..4df805612195b 100644 --- a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py +++ b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py @@ -1,7 +1,7 @@ """Integration tests for the PanelCallbackHandler module.""" import pytest -from langchain_openai import ChatOpenAI +from langchain_community.llms import OpenAI # Import the internal PanelCallbackHandler from its module - and not from # the `langchain_community.callbacks.streamlit` package - so that we don't end up using @@ -17,7 +17,7 @@ def test_panel_callback() -> None: chat_interface = pn.chat.ChatInterface() callback = PanelCallbackHandler(chat_interface) - llm = ChatOpenAI(model_name="gpt-4o-mini", streaming=True, callbacks=[callback]) + llm = OpenAI(temperature=0, streaming=True, callbacks=[callback]) llm.invoke("hey") objects = chat_interface.objects assert len(objects) == 1 From fe5eee2df74d4d40af670e922f4ad9ee36655ccd Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:37:56 -0800 Subject: [PATCH 11/18] maybe fix import --- libs/langchain/langchain/callbacks/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/langchain/langchain/callbacks/__init__.py b/libs/langchain/langchain/callbacks/__init__.py index 3f66a1a0dc032..49e1a9beebf06 100644 --- a/libs/langchain/langchain/callbacks/__init__.py +++ b/libs/langchain/langchain/callbacks/__init__.py @@ -85,6 +85,7 @@ "LLMThoughtLabeler": ( "langchain_community.callbacks.streamlit.streamlit_callback_handler" ), + "PanelCallbackHandler": "langchain_community.callbacks.panel_callback", "StreamlitCallbackHandler": "langchain_community.callbacks.streamlit", "WandbCallbackHandler": "langchain_community.callbacks.wandb_callback", "WhyLabsCallbackHandler": "langchain_community.callbacks.whylabs_callback", From 7cf15f2462cc5c68f34e03a3eeb0110baf1b3628 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:45:03 -0800 Subject: [PATCH 12/18] more ci --- .../community/langchain_community/callbacks/panel_callback.py | 4 ++-- libs/langchain/langchain/callbacks/__init__.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index 1c3de3df64d9a..740a111bd8302 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -1,10 +1,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional from uuid import UUID from langchain.callbacks.base import BaseCallbackHandler -from langchain.schema import AgentAction, AgentFinish, LLMResult, Document +from langchain.schema import AgentAction, AgentFinish, Document, LLMResult from langchain_core.utils import guard_import if TYPE_CHECKING: diff --git a/libs/langchain/langchain/callbacks/__init__.py b/libs/langchain/langchain/callbacks/__init__.py index 49e1a9beebf06..7c7e655dd4f23 100644 --- a/libs/langchain/langchain/callbacks/__init__.py +++ b/libs/langchain/langchain/callbacks/__init__.py @@ -85,7 +85,6 @@ "LLMThoughtLabeler": ( "langchain_community.callbacks.streamlit.streamlit_callback_handler" ), - "PanelCallbackHandler": "langchain_community.callbacks.panel_callback", "StreamlitCallbackHandler": "langchain_community.callbacks.streamlit", "WandbCallbackHandler": "langchain_community.callbacks.wandb_callback", "WhyLabsCallbackHandler": "langchain_community.callbacks.whylabs_callback", @@ -126,7 +125,6 @@ def __getattr__(name: str) -> Any: "FinalStreamingStdOutCallbackHandler", "LLMThoughtLabeler", "LangChainTracer", - "PanelCallbackHandler", "StreamlitCallbackHandler", "WandbCallbackHandler", "WhyLabsCallbackHandler", From 85d0bf87097c10e1663a9045db974ff9733ca811 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:46:01 -0800 Subject: [PATCH 13/18] rm unused import --- libs/langchain/langchain/callbacks/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/langchain/langchain/callbacks/__init__.py b/libs/langchain/langchain/callbacks/__init__.py index 7c7e655dd4f23..92690fc9029d0 100644 --- a/libs/langchain/langchain/callbacks/__init__.py +++ b/libs/langchain/langchain/callbacks/__init__.py @@ -50,7 +50,6 @@ ) from langchain_community.callbacks.mlflow_callback import MlflowCallbackHandler from langchain_community.callbacks.openai_info import OpenAICallbackHandler - from langchain_community.callbacks.panel_callback import PanelCallbackHandler from langchain_community.callbacks.promptlayer_callback import ( PromptLayerCallbackHandler, ) From c3cf04ccd469bf2854ded27a1ca0e4817664c28c Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:49:33 -0800 Subject: [PATCH 14/18] lint --- .../langchain_community/callbacks/panel_callback.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index 740a111bd8302..ef8c98879687b 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -167,9 +167,7 @@ def on_chain_start( self.instance.disabled = True return super().on_chain_start(serialized, inputs, *args, **kwargs) - def on_chain_end( - self, outputs: dict[str, Any], *args: Any, **kwargs: Any - ) -> Any: + def on_chain_end(self, outputs: dict[str, Any], *args: Any, **kwargs: Any) -> Any: self.instance.disabled = self._disabled_state return super().on_chain_end(outputs, *args, **kwargs) @@ -210,4 +208,4 @@ def on_chat_model_start( messages: list[Any], **kwargs: Any, ) -> None: - self._on_start(serialized, kwargs) \ No newline at end of file + self._on_start(serialized, kwargs) From 6ce1b01a8cbda2476489216f72b48b493a0bda5a Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:51:58 -0800 Subject: [PATCH 15/18] rm test --- libs/langchain/tests/unit_tests/callbacks/test_imports.py | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/langchain/tests/unit_tests/callbacks/test_imports.py b/libs/langchain/tests/unit_tests/callbacks/test_imports.py index c5570d017371a..a7bd7d366d668 100644 --- a/libs/langchain/tests/unit_tests/callbacks/test_imports.py +++ b/libs/langchain/tests/unit_tests/callbacks/test_imports.py @@ -4,7 +4,6 @@ "AimCallbackHandler", "ArgillaCallbackHandler", "ArizeCallbackHandler", - "PanelCallbackHandler", "PromptLayerCallbackHandler", "ArthurCallbackHandler", "ClearMLCallbackHandler", From 09c6c585bd11a4b5e76a8e88c8f165abd35257a2 Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:54:45 -0800 Subject: [PATCH 16/18] subclass sig --- .../langchain_community/callbacks/panel_callback.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index ef8c98879687b..a8469ea37cb80 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Sequence from uuid import UUID from langchain.callbacks.base import BaseCallbackHandler @@ -182,8 +182,15 @@ def on_retriever_error( return super().on_retriever_error( error, run_id=run_id, parent_run_id=parent_run_id, **kwargs ) - - def on_retriever_end(self, documents: list[Document], **kwargs: Any) -> Any: + + def on_retriever_end( + self, + documents: Sequence[Document], + *, + run_id: UUID, + parent_run_id: Optional[UUID] = None, + **kwargs: Any, + ) -> Any: objects = [ (f"Document {index}", document.page_content) for index, document in enumerate(documents) From a57d77b1705445c56fd43b5958ee47ac3f1307ad Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 19:59:39 -0800 Subject: [PATCH 17/18] whitespace --- libs/community/langchain_community/callbacks/panel_callback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/community/langchain_community/callbacks/panel_callback.py b/libs/community/langchain_community/callbacks/panel_callback.py index a8469ea37cb80..4e9b7d3d3a16c 100644 --- a/libs/community/langchain_community/callbacks/panel_callback.py +++ b/libs/community/langchain_community/callbacks/panel_callback.py @@ -182,7 +182,7 @@ def on_retriever_error( return super().on_retriever_error( error, run_id=run_id, parent_run_id=parent_run_id, **kwargs ) - + def on_retriever_end( self, documents: Sequence[Document], From e2f8177b2326c440d88563424c72bbbf081c8a2d Mon Sep 17 00:00:00 2001 From: Andrew Huang Date: Sun, 10 Nov 2024 20:03:58 -0800 Subject: [PATCH 18/18] lint --- .../tests/integration_tests/callbacks/test_panel_callback.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py index 4df805612195b..c08360b6f2f2c 100644 --- a/libs/community/tests/integration_tests/callbacks/test_panel_callback.py +++ b/libs/community/tests/integration_tests/callbacks/test_panel_callback.py @@ -1,7 +1,6 @@ """Integration tests for the PanelCallbackHandler module.""" import pytest -from langchain_community.llms import OpenAI # Import the internal PanelCallbackHandler from its module - and not from # the `langchain_community.callbacks.streamlit` package - so that we don't end up using @@ -9,6 +8,7 @@ from langchain_community.callbacks.panel_callback import ( PanelCallbackHandler, ) +from langchain_community.llms import OpenAI @pytest.mark.requires("panel")