import dash
from dash import html
from dash import dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input
from dash.dependencies import Output
from dash.dependencies import State
from dash.exceptions import PreventUpdate
import dataiku
import base64
import pandas as pd
def write_question_answer_sql(client, dataset_name, connection, connection_type, project_key, question, answer):
"""
Save data into a SQL like dataset
Args:
client: the dataiku client
dataset_name: name of the SQL dataset
connection: name of the SQL connection used for saving
connection_type: type of connection
project_key: project key
question: the question
answer: the answer
"""
dataset = dataiku.Dataset(dataset_name)
table_name = dataset.get_location_info().get('info', {}).get('table')
value_string = f"('{base64.b64encode(question.encode('utf-8')).decode('utf-8')}', '{base64.b64encode(answer.encode('utf-8')).decode('utf-8')}')"
sql = f"""INSERT INTO "{table_name}" (question, answer) VALUES {value_string}"""
client.sql_query(sql, connection=connection, type=connection_type, project_key=dataset.project_key,
post_queries=['COMMIT'])
def write_question_answer_csv(dataset_name, question, answer):
"""
Save data into a CSV like dataset
Args:
dataset_name: the CSV dataset
question: the question
answer: the answer
"""
dataset = dataiku.Dataset(dataset_name)
row = {
"question": [f"""{question}"""],
"answer": [f"""{answer}"""]
}
df = dataset.get_dataframe()
df = pd.concat([df, pd.DataFrame(row)])
with dataset.get_writer() as writer:
writer.write_dataframe(df)
# use the style of examples on the Plotly documentation
app.config.external_stylesheets = [dbc.themes.BOOTSTRAP, "/local/static/loading-state.css"]
search_text_layout = html.Div([
dcc.Store(id='messages', data=[
{"role": "system", "content": "You are a helpful assistant"}]),
dbc.Row([
dbc.Label("Max messages", html_for="max_messages", width=2),
dbc.Col(dbc.Input(id="max_messages", value="5", type="number", min=1, max=10), width=2),
dbc.Col(width=6),
dbc.Col(dbc.Button("Reset conversation", id="flush_messages", n_clicks=0, class_name="btn-danger"), width=2,
class_name="d-grid col-2 gap-2")
], class_name="mb-3", ),
dbc.Row([
dbc.Label("Ask your question", html_for="search_input", width=2),
dbc.Col(html.Div(children=[
dbc.Input(id="search_input", placeholder="What can I do for you?"),
dcc.Loading(id="ls-loading-1", children=[html.Div(id="ls-loading-output-1")], type="default")]), width=10),
], className="mb-3", ),
])
# build your Dash app
app.layout = html.Div([
search_text_layout,
dbc.Row([
dbc.Col(width=2),
dbc.Col(dbc.Textarea(id="text_output", style={"height": "200px"}), width=10)], class_name="mb-3"),
dbc.Row(
[dbc.Col(dbc.Button("Save this answer", id="save_answer", n_clicks=0, class_name="btn-primary", size="lg"))],
justify="end", className="d-grid gap-2 col-12 mx-auto", )
], className="container-fluid mt-3")
LLM_ID = "openai:toto:gpt-3.5-turbo"
client = dataiku.api_client()
project = client.get_default_project()
llm = project.get_llm(LLM_ID)
@app.callback(
[Output("ls-loading-output-1", "children"),
Output("text_output", "value"),
Output("messages", "data")],
Input("search_input", "n_submit"),
State("search_input", "value"),
State("max_messages", "value"),
State("messages", "data"),
running=[
(Output("search_button", "disabled"), True, False),
],
prevent_initial_call=True
)
def get_answer(_, question, max_messages, messages):
"""
Ask a question to Chat GPT (with some context), and give back the response
Args:
_: number of enter pressed in the input text (not used)
question: the question (with the context)
max_messages: number of context messages to keep
messages: the context
Returns:
the response, and an updated version of the context
"""
if not (question) or not (max_messages) or not (messages):
raise PreventUpdate
while len(messages) > int(max_messages):
messages.pop(1)
messages.append({"role": "user", "content": question})
try:
completion = llm.new_completion()
for message in messages:
completion.with_message(message.get('content'), role=message.get('role'))
answer = completion.execute()
if answer.success:
messages.append({"role": "assistant", "content": answer.text})
return ["", answer.text, messages]
else:
return ["", "Something went wrong", messages]
except:
return ["", "Something went wrong", messages]
@app.callback(
Output("messages", "data", allow_duplicate=True),
Input("flush_messages", "n_clicks"),
prevent_initial_call=True
)
def reset_conversation(_clicks):
"""
Reset the conversation
Args:
_clicks: number of clicks on the flush button (unused)
Returns:
a new context for the conversation
"""
return [{"role": "system", "content": "You are a helpful assistant"}]
@app.callback(
Output("save_answer", "n_clicks"),
Input("save_answer", "n_clicks"),
State("search_input", "value"),
State("text_output", "value"),
prevent_initial_call=True
)
def save_answer(_clicks, question, answer):
"""
Save the answer
Args:
_clicks: number of clicks on the flush button (unused)
question: the question
answer: the answer
Returns:
"""
## Uncomment these lines if you need to save into an SQL dataset
# client = dataiku.api_client()
#dataset_name = "History_SQL"
#connection = "PostgreSQL"
#connection_type = "sql"
#project_key = client.get_default_project()
#write_question_answer_sql(client, dataset_name, connection, connection_type, project_key, question, answer)
## Saving into a CSV dataset
dataset_name = "History"
write_question_answer_csv(dataset_name, question, answer)