Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Creating a Chat pane? #4702

Closed
ahuang11 opened this issue Apr 26, 2023 · 2 comments
Closed

Feature Request: Creating a Chat pane? #4702

ahuang11 opened this issue Apr 26, 2023 · 2 comments
Labels
type: enhancement Minor feature or improvement to an existing feature

Comments

@ahuang11
Copy link
Contributor

ahuang11 commented Apr 26, 2023

Is your feature request related to a problem? Please describe.

With the rise of ChatGPT / AI chat bots, I wonder if having a Chat pane makes sense?

Describe the solution you'd like

A clear and concise description of what you want to happen.

Based on the idea of https://panel.holoviz.org/reference/widgets/FileSelector.html: perhaps a combo of pn.WidgetBox, pair of pn.pane.PNG + pn.pane.Markdown aligned to left + right, and a pn.widgets.TextInput at the bottom.

image

Here's a Streamlit package that does this.
https://pypi.org/project/streamlit-chat/

Describe alternatives you've considered

A clear and concise description of any alternative solutions or features you've considered.

Users could probably create this themselves if they put in the effort.

Additional context

Add any other context or screenshots about the feature request here.

@MarcSkovMadsen
Copy link
Collaborator

MarcSkovMadsen commented Apr 27, 2023

Hi @ahuang11

I've also often been wondering about how to do this.

Here is a first experiment

chat-bot

import time

import param

import panel as pn

from panel import panel as _panel
from panel.layout.base import ListLike
from panel.reactive import ReactiveHTML

pn.extension(sizing_mode="stretch_width")

class ChatMessage(ListLike, ReactiveHTML):
    _template = """
<style>
    .chat {
        display: flex;
        align-items: center;
        margin-bottom: 10px;
        width: 100%;
    }
    .chat img {
        width: 50px;
        height: 50px;
        border-radius: 50%;
        margin-right: 10px;
    }
    .chat .message {
        background-color: #f1f0f0;
        padding: 10px;
        border-radius: 10px;
        max-width: 100%;
        font-size: 16px;
        line-height: 1.5;
    }
    .chat .message.from-me:after {
        content: "";
        display: block;
        position: absolute;
        width: 0;
        height: 0;
        border-top: 10px solid transparent;
        border-right: 10px solid #b3d7ff;
        border-bottom: 10px solid transparent;
        left: -10px;
        top: 10px;
    }
</style>

<div class="chat" id="chat">
    <img src="${avatar}" alt="Avatar" id="chatImg"></img>
    <div class="message from-me" id="chatMessage" style="background:${background_color}">
        {% for obj in objects %}
        <div id="chatMessageItem">${obj}</div>
        {% endfor %}
    </div>
</div>
"""

    objects = param.List()
    avatar = param.String(default="https://via.placeholder.com/50x50")
    background_color = param.Color("#b3d7ff")

    _child_config = {
        "avatar": "literal",
        "background_color": "literal"
    }

    def __init__(self, *objects, **params):
        if 'objects' in params and objects:
            raise ValueError(
                "Either supply objects as an positional argument or "
                "as a keyword argument, not both."
            )
        objects = params.pop('objects', objects)
        if not objects:
            objects = [None, None]
        super().__init__(objects=[_panel(obj) for obj in objects], **params)

my_config = dict(
    avatar="https://i.pravatar.cc/150?img=3",
    background_color="#b3d7ff"

)
other_config = dict(
    avatar="https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/ChatGPT_logo.svg/120px-ChatGPT_logo.svg.png",
    background_color="#f1f0f0",
)


messages = pn.Column(
    ChatMessage("Hi there. Ready for some Q&A?", **other_config),
)

question_input = pn.widgets.TextInput(name="Question", description="Input your question here")
submit = pn.widgets.Button(name="Submit", description="Click to submit", button_type="primary", sizing_mode="fixed")

@pn.depends(submit, question_input, watch=True)
def answer(_, question):
    if not question:
        return
    question_input.value=""
    messages.append(ChatMessage(question, **my_config))
    time.sleep(0.5)
    answer = "Sorry. I would not know the answer to that. But I can show you a nice plot!"
    messages.append(ChatMessage(answer, **other_config))

pn.Column(messages, question_input, submit, max_width=800).servable()

This might also be interesting for @sophiamyang and her blog posts.

Todo

[] Get styling in order an into seperate CSS file.
[] Fix styling issues. For some reason it breaks the text without using 100% width.

Inspired by ChatGPT I would probably

[] Add possibility for upvote/ downvote buttons on the right
[] Add possibility to re-edit question

@philippjfr philippjfr added the type: enhancement Minor feature or improvement to an existing feature label Apr 27, 2023
@ahuang11
Copy link
Contributor Author

ahuang11 commented Apr 27, 2023

I created a draft here which I based off FileSelector. Not sure if that's the best way to implement it! Also, not sure if there's a better way to represent a list of messages from multiple users (currently a List[Dict[str, str]], but maybe it's easier if it's a List[Tuple])

#4723

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement Minor feature or improvement to an existing feature
Projects
None yet
Development

No branches or pull requests

3 participants