How to collapse / remove the long output when using Chat Widget #95

Closed
opened 2026-02-16 00:18:46 -05:00 by yindo · 1 comment
Owner

Originally created by @dentroai on GitHub (Jan 18, 2024).

I'm trying to figure out how to use the chat widget, using the example from the docs: https://python.langchain.com/docs/langserve#chat-widget

Code:


class ChatHistory(CustomUserType):
    chat_history: List[Tuple[str, str]] = Field(
        ...,
        examples=[[("human input", "ai response")]],
        extra={"widget": {"type": "chat", "input": "question", "output": "answer"}},
    )
    question: str


def _format_to_messages(input: ChatHistory) -> List[BaseMessage]:
    """Format the input to a list of messages."""
    history = input.chat_history
    user_input = input.question

    messages = []

    for human, ai in history:
        messages.append(HumanMessage(content=human))
        messages.append(AIMessage(content=ai))
    messages.append(HumanMessage(content=user_input))
    return messages


model = ChatOpenAI()
chat_model = RunnableParallel({"answer": (RunnableLambda(_format_to_messages) | model)})
add_routes(
    app,
    chat_model.with_types(input_type=ChatHistory),
    config_keys=["configurable"],
    path="/chat",
)

As seen on the screenshot below, when using the langserve playground the Output field always shows all the words from the answer, instead of just a single string. The Start button therefor moves all the way down, and when there's a long answer I need to scroll very long until I can hit the Start button again with a new message.

image

I don't want to scroll all the way down to hit Start again because of the long Output field.
Probably it's just some flag or change in the LCEL chain somewhere, but couldn't find the solution for now.

Originally created by @dentroai on GitHub (Jan 18, 2024). I'm trying to figure out how to use the chat widget, using the example from the docs: https://python.langchain.com/docs/langserve#chat-widget Code: ``` class ChatHistory(CustomUserType): chat_history: List[Tuple[str, str]] = Field( ..., examples=[[("human input", "ai response")]], extra={"widget": {"type": "chat", "input": "question", "output": "answer"}}, ) question: str def _format_to_messages(input: ChatHistory) -> List[BaseMessage]: """Format the input to a list of messages.""" history = input.chat_history user_input = input.question messages = [] for human, ai in history: messages.append(HumanMessage(content=human)) messages.append(AIMessage(content=ai)) messages.append(HumanMessage(content=user_input)) return messages model = ChatOpenAI() chat_model = RunnableParallel({"answer": (RunnableLambda(_format_to_messages) | model)}) add_routes( app, chat_model.with_types(input_type=ChatHistory), config_keys=["configurable"], path="/chat", ) ``` As seen on the screenshot below, when using the langserve playground the `Output` field always shows all the words from the answer, instead of just a single string. The `Start` button therefor moves all the way down, and when there's a long answer I need to scroll very long until I can hit the `Start` button again with a new message. ![image](https://github.com/langchain-ai/langserve/assets/135163019/ad4dbf8d-8db0-404a-8849-f3d8221c1619) I don't want to scroll all the way down to hit `Start` again because of the long `Output` field. Probably it's just some flag or change in the LCEL chain somewhere, but couldn't find the solution for now.
yindo closed this issue 2026-02-16 00:18:46 -05:00
Author
Owner

@dentroai commented on GitHub (Jan 23, 2024):

For anyone interested, and my future self, I solved it like this for now:

from langserve.schema import CustomUserType

from typing import List, Tuple
from pydantic import Field
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
from langchain_core.runnables import RunnableLambda, RunnableParallel, RunnablePassthrough, ConfigurableField
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from operator import itemgetter

from dotenv import load_dotenv
load_dotenv()

class ChatHistory(CustomUserType):
    chat_history: List[Tuple[str, str]] = Field(
        ...,
        examples=[[("human input", "ai response")]],
        extra={"widget": {"type": "chat", "input": "question", "output": "answer"}},
    )
    question: str


def _format_to_messages(input: ChatHistory) -> List[BaseMessage]:
    """Format the input to a list of messages."""
    history = input.chat_history
    user_input = input.question

    messages = []

    for human, ai in history:
        messages.append(HumanMessage(content=human))
        messages.append(AIMessage(content=ai))
    messages.append(HumanMessage(content=user_input))
    return messages

model = ChatOpenAI()

chat_model = (
    RunnableLambda(_format_to_messages) 
    | model
    | {"answer": RunnablePassthrough() | StrOutputParser()}
    | {"answer": itemgetter("answer")}
).with_types(input_type=ChatHistory)

That way the output below the chat widget only shows the LLM output as a single string instead of displaying every word separately.
Now I don't have to scroll forever anymore, which is nice.

@dentroai commented on GitHub (Jan 23, 2024): For anyone interested, and my future self, I solved it like this for now: ``` from langserve.schema import CustomUserType from typing import List, Tuple from pydantic import Field from langchain_core.messages import BaseMessage, HumanMessage, AIMessage from langchain_core.runnables import RunnableLambda, RunnableParallel, RunnablePassthrough, ConfigurableField from langchain_openai import ChatOpenAI from langchain_core.output_parsers import StrOutputParser from operator import itemgetter from dotenv import load_dotenv load_dotenv() class ChatHistory(CustomUserType): chat_history: List[Tuple[str, str]] = Field( ..., examples=[[("human input", "ai response")]], extra={"widget": {"type": "chat", "input": "question", "output": "answer"}}, ) question: str def _format_to_messages(input: ChatHistory) -> List[BaseMessage]: """Format the input to a list of messages.""" history = input.chat_history user_input = input.question messages = [] for human, ai in history: messages.append(HumanMessage(content=human)) messages.append(AIMessage(content=ai)) messages.append(HumanMessage(content=user_input)) return messages model = ChatOpenAI() chat_model = ( RunnableLambda(_format_to_messages) | model | {"answer": RunnablePassthrough() | StrOutputParser()} | {"answer": itemgetter("answer")} ).with_types(input_type=ChatHistory) ``` That way the output below the chat widget only shows the LLM output as a single string instead of displaying every word separately. Now I don't have to scroll forever anymore, which is nice.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: langchain-ai/langserve#95