Skip to content

Commit b070fa6

Browse files
committed
chore: More responsive chatbot with sent message reflected immediately.
chore: Web UI beautifications.
1 parent 7c4c8ce commit b070fa6

File tree

6 files changed

+89
-32
lines changed

6 files changed

+89
-32
lines changed

docs/images/icon-bot-avatar.svg

Lines changed: 4 additions & 0 deletions
Loading

docs/images/icon-btn-delete.svg

Lines changed: 3 additions & 0 deletions
Loading

docs/images/icon-btn-send.svg

Lines changed: 6 additions & 0 deletions
Loading

docs/images/icon-user-avatar.svg

Lines changed: 5 additions & 0 deletions
Loading

src/dqa/model/mhqa.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ class MCPToolInvocation(BaseModel):
3838

3939
class MHQAResponse(MHQAActorIO):
4040
user_input: Annotated[str, "The original query from the user"]
41-
agent_output: Annotated[str, "The agent response to the user query"]
41+
agent_output: Annotated[Optional[str], "The agent response to the user query"] = (
42+
None
43+
)
4244
tool_invocations: Annotated[
43-
List[MCPToolInvocation],
45+
Optional[List[MCPToolInvocation]],
4446
"List of MCP tool invocations made during the response generation",
45-
]
47+
] = []
4648

4749

4850
MHQAAgentSkills: TypeAlias = MHQAActorMethods

src/dqa/web/gradio.py

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@
3030

3131
class GradioApp(A2AClientMixin):
3232
_APP_LOGO_PATH = "docs/images/logo.svg"
33+
# https://www.svgrepo.com/svg/529291/user-rounded
34+
_ICON_USER_AVATAR = "docs/images/icon-user-avatar.svg"
35+
# https://www.svgrepo.com/svg/368593/chatbot
36+
_ICON_BOT_AVATAR = "docs/images/icon-bot-avatar.svg"
37+
# https://www.svgrepo.com/svg/496582/send-2
38+
_ICON_BTN_SEND = "docs/images/icon-btn-send.svg"
39+
# https://www.svgrepo.com/svg/499905/delete
40+
_ICON_BTN_DELETE = "docs/images/icon-btn-delete.svg"
3341

3442
_MD_EU_AI_ACT_TRANSPARENCY = """
3543
**European Union AI Act Transparency notice**: By using this app, you are interacting with an artificial intelligence (AI) system.
@@ -45,39 +53,40 @@ def __init__(self):
4553
f"http://{self._mhqa_a2a_uvicorn_host}:{self._mhqa_a2a_uvicorn_port}"
4654
)
4755

48-
def convert_echo_response_to_chat_messages(self, response: MHQAResponse):
56+
def convert_mhqa_response_to_chat_messages(self, response: MHQAResponse):
4957
chat_messages = []
5058
chat_messages.append(
5159
gr.ChatMessage(
5260
role="user",
5361
content=response.user_input,
5462
)
5563
)
56-
message_id = (
57-
str(uuid4())
58-
if response.tool_invocations and len(response.tool_invocations) > 0
59-
else None
60-
)
61-
chat_messages.append(
62-
gr.ChatMessage(
63-
role="assistant",
64-
content=response.agent_output,
65-
metadata={"id": message_id} if message_id else None,
64+
if response.agent_output:
65+
message_id = (
66+
str(uuid4())
67+
if response.tool_invocations and len(response.tool_invocations) > 0
68+
else None
6669
)
67-
)
68-
69-
for tool_invocation in response.tool_invocations:
7070
chat_messages.append(
7171
gr.ChatMessage(
7272
role="assistant",
73-
content=f"Inputs: {tool_invocation.input}\nOutputs: {tool_invocation.output}\nMetadata: {tool_invocation.metadata}",
74-
metadata={
75-
"parent_id": message_id,
76-
"title": f"Tool used: {tool_invocation.name}",
77-
},
73+
content=response.agent_output,
74+
metadata={"id": message_id} if message_id else None,
7875
)
7976
)
8077

78+
for tool_invocation in response.tool_invocations:
79+
chat_messages.append(
80+
gr.ChatMessage(
81+
role="assistant",
82+
content=f"Inputs: {tool_invocation.input}\nOutputs: {tool_invocation.output}\nMetadata: {tool_invocation.metadata}",
83+
metadata={
84+
"parent_id": message_id,
85+
"title": f"Tool used: {tool_invocation.name}",
86+
},
87+
)
88+
)
89+
8190
return chat_messages
8291

8392
def component_main_content(self):
@@ -111,9 +120,10 @@ def component_main_content(self):
111120
)
112121
state_selected_chat_id = gr.State(value=None)
113122
btn_chat_delete = gr.Button(
114-
"Delete chat",
123+
"Delete selected chat",
115124
size="sm",
116125
variant="stop",
126+
icon=GradioApp._ICON_BTN_DELETE,
117127
interactive=False,
118128
)
119129
gr.Markdown(GradioApp._MD_EU_AI_ACT_TRANSPARENCY)
@@ -125,17 +135,23 @@ def component_main_content(self):
125135
chatbot = gr.Chatbot(
126136
type="messages",
127137
label="Chat history (a new chat will be created if none if selected)",
138+
avatar_images=[
139+
GradioApp._ICON_USER_AVATAR,
140+
GradioApp._ICON_BOT_AVATAR,
141+
],
128142
)
129143
with gr.Row(equal_height=True):
130144
txt_input = gr.Textbox(
131145
scale=3,
132146
lines=4,
133147
label="Your message",
134148
info="Enter your non-trivial question to ask the AI agent.",
135-
placeholder="Type a message and press Enter, or click the Send button.",
149+
placeholder="Type a message and press Shift+Enter, or click the Send button.",
136150
show_copy_button=False,
137151
)
138-
btn_echo = gr.Button("Send", scale=1)
152+
btn_send = gr.Button(
153+
"Send", size="lg", icon=GradioApp._ICON_BTN_SEND, scale=1
154+
)
139155
with gr.Column():
140156
gr.Examples(
141157
label="Example of input messages",
@@ -198,7 +214,7 @@ async def refresh_chat_history_from_agent(chat_id: str) -> list:
198214
chat_history = []
199215
for past_message in validated_response:
200216
chat_history.extend(
201-
self.convert_echo_response_to_chat_messages(past_message)
217+
self.convert_mhqa_response_to_chat_messages(past_message)
202218
)
203219
return chat_history
204220

@@ -311,7 +327,7 @@ async def btn_new_chat_clicked(
311327
yield browser_state_chat_histories, new_chat_id, None
312328

313329
@gr.on(
314-
triggers=[btn_echo.click, txt_input.submit],
330+
triggers=[btn_send.click, txt_input.submit],
315331
inputs=[
316332
txt_input,
317333
state_selected_chat_id,
@@ -354,8 +370,17 @@ async def btn_echo_clicked(
354370
),
355371
)
356372

373+
chat_history.extend(
374+
self.convert_mhqa_response_to_chat_messages(
375+
MHQAResponse(
376+
thread_id=selected_chat_id, user_input=txt_input
377+
)
378+
)
379+
)
380+
last_added_messages = 1
381+
357382
yield (
358-
gr.update(interactive=False),
383+
None,
359384
browser_state_chat_histories,
360385
selected_chat_id,
361386
chat_history,
@@ -382,11 +407,15 @@ async def btn_echo_clicked(
382407
agent_response = MHQAResponse.model_validate_json(
383408
full_message_content
384409
)
385-
chat_history.extend(
386-
self.convert_echo_response_to_chat_messages(
410+
new_messages = (
411+
self.convert_mhqa_response_to_chat_messages(
387412
agent_response
388413
)
389414
)
415+
if last_added_messages > 0:
416+
del chat_history[-last_added_messages:]
417+
chat_history.extend(new_messages)
418+
last_added_messages = len(new_messages)
390419

391420
browser_state_chat_histories[selected_chat_id] = (
392421
chat_history
@@ -396,7 +425,7 @@ async def btn_echo_clicked(
396425
f"No input message was provided for chat ID {selected_chat_id}."
397426
)
398427
yield (
399-
gr.update(value="", interactive=True),
428+
None,
400429
browser_state_chat_histories,
401430
selected_chat_id,
402431
chat_history,
@@ -414,7 +443,15 @@ async def btn_echo_clicked(
414443

415444
def construct_ui(self):
416445
with gr.Blocks(fill_width=True, fill_height=True) as self.ui:
417-
gr.set_static_paths(paths=[GradioApp._APP_LOGO_PATH])
446+
gr.set_static_paths(
447+
paths=[
448+
GradioApp._APP_LOGO_PATH,
449+
GradioApp._ICON_USER_AVATAR,
450+
GradioApp._ICON_BOT_AVATAR,
451+
GradioApp._ICON_BTN_SEND,
452+
GradioApp._ICON_BTN_DELETE,
453+
]
454+
)
418455
self.component_main_content()
419456

420457
return self.ui

0 commit comments

Comments
 (0)