|
import asyncio |
|
import threading |
|
from markdown_it import MarkdownIt |
|
from mdit_py_plugins import front_matter |
|
|
|
from reactpy import component, html, hooks, use_effect, svg, run |
|
from reactpy.backend.starlette import configure |
|
from starlette.applications import Starlette |
|
from vectara_agentic.agent import AgentStatusType |
|
from agent import initialize_agent, get_agent_config |
|
|
|
wait_message = "Please wait. Assistant at work..." |
|
|
|
@component |
|
def App(): |
|
|
|
|
|
show_logs, set_show_logs = hooks.use_state(False) |
|
|
|
|
|
show_logs_button, set_show_logs_button = hooks.use_state(False) |
|
|
|
|
|
first_turn, set_first_turn = hooks.use_state(True) |
|
|
|
|
|
more_info, set_more_info = hooks.use_state(False) |
|
|
|
|
|
collapsed, set_collapse = hooks.use_state(False) |
|
|
|
|
|
messages, set_messages = hooks.use_state([]) |
|
|
|
message, set_message = hooks.use_state("") |
|
|
|
|
|
def use_agent_logger(): |
|
agent_log_entries, set_agent_log_entries = hooks.use_state([]) |
|
|
|
def reset_log_entries(): |
|
set_agent_log_entries([]) |
|
|
|
def add_log_entry(new_log_entry): |
|
set_agent_log_entries(lambda previous_entries: previous_entries + [new_log_entry]) |
|
|
|
return agent_log_entries, add_log_entry, reset_log_entries |
|
|
|
log_entries, add_log_entry, reset_log_entries = use_agent_logger() |
|
|
|
def update_func(status_type: AgentStatusType, msg: str): |
|
if status_type != AgentStatusType.AGENT_UPDATE: |
|
output = f"{status_type.value} - {msg}" |
|
add_log_entry(output) |
|
|
|
cfg, _ = hooks.use_state(get_agent_config()) |
|
agent, _ = hooks.use_state(initialize_agent(cfg, update_func)) |
|
|
|
def toggle_header(event=None): |
|
set_more_info(not more_info) |
|
|
|
def toggle_footer(event=None): |
|
set_collapse(not collapsed) |
|
|
|
|
|
def start_over(event=None): |
|
set_messages([]) |
|
set_first_turn(True) |
|
|
|
def display_message(new_messages): |
|
if first_turn: |
|
set_first_turn(False) |
|
|
|
set_messages(messages + list(new_messages)) |
|
|
|
async def chat_response(user_message): |
|
response = await asyncio.to_thread(agent.chat, user_message) |
|
return response |
|
|
|
async def send_message_async(sent_message): |
|
response = await chat_response(sent_message) |
|
set_messages(messages[:-1]) |
|
display_message( |
|
[ |
|
{"user": "human", "message": sent_message}, |
|
{"user": "bot", "message": response}, |
|
] |
|
) |
|
set_show_logs_button(True) |
|
|
|
def send_message(event=None): |
|
print(f"DEBUG: SEND MESSAGE CALLED FOR MESSAGE {message}") |
|
if message.strip(): |
|
sent_message = message |
|
set_message("") |
|
|
|
set_show_logs_button(False) |
|
set_show_logs(False) |
|
reset_log_entries() |
|
display_message([{"user": "human", "message": sent_message}]) |
|
display_message([{"user": "bot", "message": wait_message}]) |
|
|
|
asyncio.create_task(send_message_async(sent_message)) |
|
|
|
def send_example(ex_prompt): |
|
if ex_prompt.strip(): |
|
sent_message = ex_prompt |
|
set_message("") |
|
reset_log_entries() |
|
display_message([{"user": "human", "message": sent_message}]) |
|
display_message([{"user": "bot", "message": wait_message}]) |
|
|
|
asyncio.create_task(send_message_async(sent_message)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def handle_key_down(event): |
|
if event['key'] == 'Enter' and message.strip(): |
|
await send_message() |
|
set_message("") |
|
|
|
def handle_input_change(event): |
|
set_message(event['target']['value']) |
|
|
|
@component |
|
def Header(demo_name: str, short_description: str, extra_info: str): |
|
return html.header( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"display": "flex", |
|
"justifyContent": "space-between", |
|
"alignItems": "center", |
|
} |
|
}, |
|
html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"alignItems": "center", |
|
"flex": 2, |
|
"textAlign": "left", |
|
"padding": "10px", |
|
} |
|
}, |
|
html.img( |
|
{ |
|
"src": "https://avatars.githubusercontent.com/u/108304503?s=200&v=4", |
|
"style": { |
|
"height": "30px", |
|
"marginRight": "15px", |
|
"marginLeft": "5px", |
|
"transform": "translateY(-2px)", |
|
} |
|
} |
|
), |
|
html.p( |
|
{ |
|
"style": { |
|
"fontSize": "25px", |
|
"fontFamily": "Georgia, 'Times New Roman', Times, serif", |
|
} |
|
}, |
|
f"{demo_name}" |
|
), |
|
), |
|
html.div( |
|
{ |
|
"style": { |
|
"flex": 5, |
|
"textAlign": "center", |
|
"padding": "10px 0px 15px 0px", |
|
"fontFamily": "Lato", |
|
"position": "relative", |
|
} |
|
}, |
|
html.h3(f"Welcome to the {demo_name} Demo"), |
|
html.p( |
|
short_description, |
|
html.button( |
|
{ |
|
"style": { |
|
"display": "inline" if not more_info else "none", |
|
"backgroundColor": "#FFFFFF", |
|
"color": "#757575", |
|
"fontSize": "13px", |
|
"cursor": "pointer", |
|
"border": "none", |
|
"padding": "0px 0px 0px 5px", |
|
}, |
|
"type": "button", |
|
"onClick": toggle_header, |
|
}, |
|
html.u( |
|
{ |
|
"style": { |
|
"flex": 2, |
|
"textAlign": "right", |
|
"padding": "10px", |
|
} |
|
}, |
|
"Learn More" |
|
) |
|
), |
|
f" {extra_info}" if more_info else "" |
|
), |
|
html.button( |
|
{ |
|
"style": { |
|
"display": "block" if more_info else "none", |
|
"background": "none", |
|
"border": "none", |
|
"color": "#757575", |
|
"cursor": "pointer", |
|
"position": "absolute", |
|
"left": "50%", |
|
"transform": "translateX(-50%)", |
|
"bottom": "1px", |
|
}, |
|
"type": "button", |
|
"on_click": toggle_header, |
|
}, |
|
svg.svg( |
|
{ |
|
"width": "20", |
|
"height": "20", |
|
"viewBox": "0 0 20 20", |
|
"fill": "none", |
|
"stroke": "black", |
|
"strokeWidth": "2", |
|
"strokeLinecap": "round", |
|
"strokeLinejoin": "round", |
|
}, |
|
svg.path( |
|
{ |
|
"d": "M12 19V5M5 12l7-7 7 7", |
|
"stroke": "currentColor", |
|
} |
|
) |
|
) |
|
) |
|
), |
|
html.div( |
|
{ |
|
"style": { |
|
"flex": 2, |
|
"textAlign": "right", |
|
"padding": "10px", |
|
} |
|
}, |
|
html.button( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"color": "#757575", |
|
"fontSize": "14px", |
|
"cursor": "pointer", |
|
"border": "1px solid #e2dfdf", |
|
"borderRadius": "5px", |
|
"padding": "6px 20px", |
|
"marginRight": "15px", |
|
}, |
|
"type": "button", |
|
"onClick": start_over, |
|
}, |
|
"Start Over?" |
|
) |
|
) |
|
) |
|
|
|
def markdown_to_html(markdown_text): |
|
md = ( |
|
MarkdownIt("commonmark", {"breaks": True, "html": True}) |
|
.use(front_matter.front_matter_plugin) |
|
) |
|
return md.render(markdown_text) |
|
|
|
@component |
|
def MarkdownRenderer(content): |
|
html_content = markdown_to_html(content) |
|
return html.div( |
|
{ |
|
"style": { |
|
"fontFamily": "Arial", |
|
"color": "#49454F", |
|
"fontSize": "14px", |
|
}, |
|
"dangerouslySetInnerHTML": {"__html": html_content} |
|
} |
|
) |
|
|
|
@component |
|
def ExamplePrompts(): |
|
example_questions = [example.strip() for example in cfg['examples'].split(";")] if cfg.examples else [] |
|
|
|
def create_prompt_button(question): |
|
return html.button( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"borderWidth": "1px", |
|
"borderColor": "#65558F", |
|
"borderRadius": "20px", |
|
"padding": "5px 7px", |
|
"margin": "5px", |
|
"cursor": "pointer", |
|
"color": "#21005D", |
|
"fontSize": "13px", |
|
}, |
|
"type": "button", |
|
"onClick": lambda _: send_example(question), |
|
}, |
|
question |
|
) |
|
|
|
if first_turn: |
|
return html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"transform": "translate(4%, 100%)", |
|
"flexDirection": "column", |
|
"justifyContent": "center", |
|
"width": "90%", |
|
} |
|
}, |
|
html.p( |
|
{ |
|
"style": { |
|
"fontSize": "16px", |
|
"fontFamily": "Arial", |
|
"color": "#49454F", |
|
"marginBottom": "5px", |
|
"transform": "translateX(3%)", |
|
"textAlign": "left", |
|
} |
|
}, |
|
"Queries to try:" |
|
), |
|
html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"flexWrap": "wrap", |
|
"justifyContent": "center", |
|
} |
|
}, |
|
*[create_prompt_button(q) for q in example_questions] |
|
) |
|
) |
|
|
|
return None |
|
|
|
@component |
|
def ChatBox(): |
|
return html.div( |
|
{ |
|
"style": { |
|
"position": "fixed", |
|
"bottom": "70px" if collapsed else "140px", |
|
"left": "50%", |
|
"transform": "translateX(-50%)", |
|
"width": "80%", |
|
"display": "flex", |
|
"alignItems": "center", |
|
"backgroundColor": "#FFFFFF", |
|
"borderRadius": "50px", |
|
"zIndex": "1000", |
|
} |
|
}, |
|
html.input( |
|
{ |
|
"type": "text", |
|
"value": message, |
|
"placeholder": "Your Message", |
|
"onChange": handle_input_change, |
|
"onKeyDown": handle_key_down, |
|
"style": { |
|
"width": "100%", |
|
"padding": "10px 50px 10px 20px", |
|
"border": "none", |
|
"borderRadius": "50px", |
|
"color": "#65558F", |
|
} |
|
} |
|
), |
|
html.button( |
|
{ |
|
"type": "button", |
|
"onClick": send_message, |
|
"style": { |
|
"position": "absolute", |
|
"right": "8px", |
|
"top": "50%", |
|
"transform":"translateY(-50%)", |
|
"background": "none", |
|
"border": "none", |
|
"cursor": "pointer", |
|
"padding": "8px", |
|
"display": "flex", |
|
"alignItems": "center", |
|
"justifyContent": "center", |
|
} |
|
}, |
|
svg.svg( |
|
{ |
|
"xmlns": "http://www.w3.org/2000/svg", |
|
"width": "20", |
|
"height": "20", |
|
"fill": "none", |
|
"stroke": "currentColor", |
|
"stroke-linecap": "round", |
|
"stroke-linejoin": "round", |
|
"stroke-width": "2", |
|
"viewBox": "0 0 24 24", |
|
"class": "feather feather-send", |
|
}, |
|
svg.path({"d": "M22 2 11 13"}), |
|
svg.path({"d": "M22 2 15 22 11 13 2 9z"}) |
|
) |
|
) |
|
) |
|
|
|
|
|
@component |
|
def ChatMessage(user, message): |
|
|
|
return html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"width": "75%", |
|
"transform": "translateX(20%)", |
|
"justifyContent": "flex-end" if user == "human" else "flex-start", |
|
"margin": "20px 0px 10px 0px" |
|
} |
|
}, |
|
html.div( |
|
{ |
|
"style": { |
|
"maxWidth": "50%", |
|
"padding": "0px 15px 0px 10px", |
|
"borderRadius": "15px", |
|
"backgroundColor": "#E8DEF8" if user == "human" else "#ECE6F0", |
|
"color": "#000000", |
|
} |
|
}, |
|
MarkdownRenderer(message) |
|
) |
|
) |
|
|
|
@component |
|
def Logs(): |
|
if (len(messages) > 0) and (len(log_entries) > 0) and (messages[-1]["message"] != wait_message) and (show_logs_button): |
|
if not show_logs: |
|
return html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"transform": "translateX(75%)", |
|
"width": "20%", |
|
"margin": "0px", |
|
} |
|
}, |
|
html.button( |
|
{ |
|
"style": { |
|
"position": "flex", |
|
"background": "none", |
|
"color": "#757575", |
|
"border": "none", |
|
"cursor": "pointer", |
|
"fontFamily": "Arial", |
|
"fontSize": "14px", |
|
}, |
|
"type": "button", |
|
"onClick": lambda _: set_show_logs(True) |
|
}, |
|
"Show Logs" |
|
) |
|
) |
|
else: |
|
return html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"transform": "translateX(20%)", |
|
"border": "2px solid #e2dfdf", |
|
"borderRadius": "10px", |
|
"width": "75%", |
|
} |
|
}, |
|
html.div( |
|
[ |
|
html.p( |
|
{ |
|
"style": { |
|
"fontSize": "14px", |
|
"marginLeft": "10px", |
|
} |
|
}, |
|
entry |
|
) for entry in log_entries |
|
], |
|
html.button( |
|
{ |
|
"style": { |
|
"position": "flex", |
|
"background": "none", |
|
"color": "#757575", |
|
"border": "none", |
|
"cursor": "pointer", |
|
"fontFamily": "Arial", |
|
"fontSize": "14px", |
|
"marginBottom": "10px", |
|
"marginLeft": "5px", |
|
}, |
|
"type": "button", |
|
"onClick": lambda _: set_show_logs(False) |
|
}, |
|
"Hide Logs" |
|
) |
|
) |
|
) |
|
|
|
return None |
|
|
|
@component |
|
def ChatUI(): |
|
|
|
def render_chat_content(): |
|
if first_turn: |
|
return ExamplePrompts() |
|
else: |
|
return [ChatMessage(msg["user"], msg["message"]) for msg in messages] |
|
|
|
return html.div( |
|
{ |
|
"style": { |
|
"display": "flex", |
|
"flexDirection": "column", |
|
"padding": "10px", |
|
"backgroundColor": "#F4F1F4", |
|
"overflowY": "auto", |
|
"height": f"calc(100vh - {265 if collapsed else 335}px)", |
|
"marginBottom": "15px", |
|
"paddingBottom": "15px", |
|
}, |
|
}, |
|
render_chat_content(), |
|
Logs() |
|
) |
|
|
|
|
|
@component |
|
def Footer(): |
|
|
|
if collapsed: |
|
return html.footer( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"position": "fixed", |
|
"bottom": 0, |
|
"width": "100%", |
|
"height": "50px", |
|
} |
|
}, |
|
html.button( |
|
{ |
|
"style": { |
|
"background": "none", |
|
"border": "none", |
|
"color": "#757575", |
|
"cursor": "pointer", |
|
"position": "absolute", |
|
"bottom": "5px", |
|
"right": "10px", |
|
}, |
|
"type": "button", |
|
"on_click": toggle_footer, |
|
}, |
|
svg.svg( |
|
{ |
|
"xmlns": "http://www.w3.org/2000/svg", |
|
"width": "24", |
|
"height": "24", |
|
"fill": "none", |
|
"stroke": "currentColor", |
|
"stroke-linecap": "round", |
|
"stroke-linejoin": "round", |
|
"stroke-width": "3", |
|
"viewBox": "0 0 24 24", |
|
}, |
|
svg.path({"d": "M19 14l-7-7-7 7"}) |
|
) |
|
) |
|
) |
|
|
|
else: |
|
return html.footer( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"position": "fixed", |
|
"bottom": 0, |
|
"width": "100%", |
|
} |
|
}, |
|
html.div( |
|
{ |
|
"style": { |
|
"backgroundColor": "#FFFFFF", |
|
"padding": "0px 20px 0px 50px", |
|
"position": "relative", |
|
"display": "block", |
|
} |
|
}, |
|
html.button( |
|
{ |
|
"style": { |
|
"position": "absolute", |
|
"right": "10px", |
|
"background": "none", |
|
"border": "none", |
|
"color": "#757575", |
|
"cursor": "pointer", |
|
}, |
|
"type": "button", |
|
"on_click": toggle_footer, |
|
}, |
|
svg.svg( |
|
{ |
|
"xmlns": "http://www.w3.org/2000/svg", |
|
"width": "24", |
|
"height": "24", |
|
"fill": "none", |
|
"stroke": "currentColor", |
|
"stroke-linecap": "round", |
|
"stroke-linejoin": "round", |
|
"stroke-width": "3", |
|
"viewBox": "0 0 24 24", |
|
}, |
|
svg.path({"d": "M18 6L6 18"}), |
|
svg.path({"d": "M6 6l12 12"}) |
|
) |
|
), |
|
html.p( |
|
{ |
|
"style": { |
|
"fontSize": "20px", |
|
"color": "#4b4851" |
|
} |
|
}, |
|
"How this works?", |
|
), |
|
html.p( |
|
{ |
|
"style": { |
|
"color": "#757575", |
|
} |
|
}, |
|
"This app was built with ", |
|
html.a( |
|
{ |
|
"href": "https://vectara.com/", |
|
"target": "_blank", |
|
}, |
|
"Vectara", |
|
), |
|
html.br(), |
|
"It demonstrates the use of Agentic-RAG functionality with Vectara", |
|
) |
|
) |
|
) |
|
|
|
return html.div( |
|
{ |
|
"style": { |
|
"backgroundColor": "#F4F1F4", |
|
"margin": "0", |
|
"padding": "0", |
|
"minHeight": "100vh", |
|
"display": "flex", |
|
"boxSizing": "border-box", |
|
"flexDirection": "column", |
|
"overflowX": "hidden", |
|
"position": "relative", |
|
} |
|
}, |
|
html.head( |
|
html.style(""" |
|
body { |
|
margin: 0; |
|
padding: 0; |
|
} |
|
""") |
|
), |
|
Header( |
|
demo_name=cfg['demo_name'], |
|
short_description=cfg['short_description'], |
|
extra_info=cfg['extra_info'] |
|
), |
|
ChatUI(), |
|
ChatBox(), |
|
Footer(), |
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
run(App) |
|
|