feat/ai-chat: Add core components for Database chat #5

Merged
quadfaselt merged 5 commits from feat/ai-chat into main 2024-09-03 13:38:24 +00:00
4 changed files with 210 additions and 3 deletions
Showing only changes of commit 923dc3b439 - Show all commits

View File

@@ -1,8 +1,142 @@
from dash import Dash, html
from typing import Any, Dict, Tuple
app = Dash()
from dash import (
Dash,
Input,
Output,
State,
callback,
dcc,
get_asset_url,
html,
no_update,
)
from dash.exceptions import PreventUpdate
from .app_styles import header_style
from .data_chat import send_message
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
app = Dash(__name__, external_stylesheets=external_stylesheets)
app.index_string = header_style
err_style = {
"height": "0px",
"overflow": "hidden",
"transition": "height 0.5s ease-in-out",
"border-radius": "15px",
"background-color": "#FFCCCB",
"text-align": "center",
"color": "#FF6B6B",
"margin-top": "20px",
"margin-left": "20px",
"margin-right": "20px",
"font-weight": "bold",
"display": "flex",
"justify-content": "center",
"align-items": "center",
}
start_value = "Stelle deine Frage an die Datenbank..."
app.layout = html.Div(
[
html.Div(
[
html.H1("Datenbank-Chat", className="heading"),
html.Img(src=get_asset_url("logo.png"), className="logo"),
],
className="header-container",
), # Header
dcc.Store(
id="tmp-value", data=start_value, storage_type="session"
), # Store previous prompt
dcc.Textarea(
id="input-field",
value=start_value,
style={"width": "96%", "height": 200, "margin-left": "20px"},
), # Input field
html.Div([]), # Needed for keeping the layout clean
html.Button(
"Abschicken",
id="submit-button",
n_clicks=0,
disabled=False,
style={"margin-left": "20px"},
), # Submit button
html.Div(
[html.P("Bitte eine neue Anfrage eingeben.")], id="error", style=err_style
), # Error message (only visible if input is not updated but submit button is clicked)
dcc.Loading(
id="loading",
type="default",
children=[
html.Div(
"Hier erscheint die Antwort der Datenbank.",
id="text-output",
style={
"whiteSpace": "pre-line",
"margin-top": "30px",
"margin-left": "20px",
"margin-right": "20px",
"border": "2px solid #86bc25",
"border-radius": "15px",
"padding": "20px",
},
)
],
),
], # Output field
className="container",
)
@callback(
Output("text-output", "children"),
Output("tmp-value", "data"),
Output("error", "style"),
Input("submit-button", "n_clicks"),
State("input-field", "value"),
State("tmp-value", "data"),
prevent_initial_call=True,
running=[
(Output("submit-button", "disabled"), True, False),
(
Output("submit-button", "style"),
{"opacity": 0.5, "margin-left": "20px"},
{"opacity": 1.0, "margin-left": "20px"},
),
],
)
def update_output(n_clicks: int, value: str, data: str) -> Tuple[str, str, Dict[str, Any]]:
"""Update the output based on user input and button clicks.
Parameters
----------
n_clicks : int
Number of times the submit button has been clicked.
value : str
Current value of the input field.
data : str
Previously stored value.
Returns
-------
Tuple[str, str, Dict[str, Any]]
Updated output text, new stored value, and error style.
"""
global err_style
if n_clicks > 0 and value != data:
result = send_message(value)
err_style["height"] = "0px"
return result, value, err_style
elif value == data:
err_style["height"] = "50px"
return no_update, no_update, err_style
raise PreventUpdate
app.layout = [html.Div(children="Hello World")]
server = app.server

37
app/app_styles.py Normal file
View File

@@ -0,0 +1,37 @@
header_style = """
<!DOCTYPE html>
<html>
<head>
{%metas%}
<title>{%title%}</title>
{%favicon%}
{%css%}
<style>
.header-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
background-color: #f8f9fa;
}
.heading {
font-size: 2.5em;
font-weight: bold;
color: #333;
}
.logo {
height: 30px;
width: auto;
}
</style>
</head>
<body>
{%app_entry%}
<footer>
{%config%}
{%scripts%}
{%renderer%}
</footer>
</body>
</html>
"""

BIN
app/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

36
app/data_chat.py Normal file
View File

@@ -0,0 +1,36 @@
import os
from openai import AzureOpenAI
# Set up credentials
# NOTE: When running locally, these have to be set in the environment
client = AzureOpenAI(
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
api_key=os.getenv("AZURE_OPENAI_KEY"),
api_version="2024-02-01",
)
deployment_name = "sqlai"
def send_message(message: str) -> str:
"""Send a message to the openai chat completion API and return the response.
Parameters
----------
message : str
The user's message to be sent to the chat completion API.
Returns
-------
str
The content of the assistant's response message.
"""
response = client.chat.completions.create(
model=deployment_name,
messages=[
{"role": "system", "content": "Du bist ein hilfreicher Assistent."},
{"role": "user", "content": message},
],
)
return response.choices[0].message.content