Alfred_agent / app.py
shan gao
change
2976609
import os
import gradio as gr
from langchain_core.messages import AnyMessage, HumanMessage, AIMessage
from agent import build_graph
def create_qna_interface():
"""Create the Q&A chatbot interface."""
# ======================================
# 2) Helper functions for the Gradio UI
# ======================================
def _msg_content_to_str(msg: AnyMessage) -> str:
"""
Coerce LangChain message content (which might contain tool call structures)
into displayable text for the Chatbot.
"""
# Most often, content is a string already
content = getattr(msg, "content", "")
if isinstance(content, str):
return content
# If it's a list of parts (e.g., tool call traces), join any text parts
if isinstance(content, list):
texts = []
for part in content:
if isinstance(part, dict) and "text" in part:
texts.append(part["text"])
elif isinstance(part, str):
texts.append(part)
return "\n".join(texts) if texts else str(content)
# Fallback
return str(content)
def startup_state() -> list[AnyMessage]:
"""Start with an empty conversation."""
return []
# Gradio expects chatbot history as list[tuple[str, str]]
def submit_user_message(
user_text: str,
chat_history: list[tuple[str, str]],
agent_messages: list[AnyMessage],
):
"""
1) Append HumanMessage to agent state
2) Run Alfred
3) Extract last AIMessage and append to chat_history
"""
if not user_text or user_text.strip() == "":
return gr.update(), chat_history, agent_messages
# Step 1: add HumanMessage to state
agent_messages = list(agent_messages or [])
agent_messages.append(HumanMessage(content=user_text))
# get API key
api_key = os.getenv('HF_TOKEN')
# Step 2: run the graph
alfred = build_graph(hf_token=api_key)
out = alfred.invoke({"messages": agent_messages})
# The graph returns a new messages list *including* the latest assistant/tool steps.
# We use the last AIMessage as the displayed reply.
new_msgs: list[AnyMessage] = out["messages"]
agent_messages = new_msgs # keep full state for the next turn
# Find the last assistant message to show in the UI
ai_text = ""
for m in reversed(new_msgs):
if isinstance(m, AIMessage):
ai_text = _msg_content_to_str(m)
break
if not ai_text:
# fallback: in rare cases of only tool messages, show a generic note
ai_text = "I processed your request using my tools."
chat_history = list(chat_history or [])
chat_history.append({"role": "user", "content": user_text})
chat_history.append({"role": "assistant", "content": ai_text})
return "", chat_history, agent_messages
def clear_chat():
"""Reset the Gradio UI and agent state."""
return [], startup_state()
# ========================
# 3) Gradio App UI layout
# ========================
with gr.Blocks(title="Alfred — LangGraph Agent") as demo:
gr.Markdown(
"""
# 🎩 Alfred — Your LangGraph Agent
Ask questions and Alfred will respond, using:
- a vector search tool over the guest list
- DuckDuckGo web search
"""
)
with gr.Row():
chatbot = gr.Chatbot(
label="Conversation",
type="messages",
height=500,
show_copy_button=True,
avatar_images=(None, None), # customize if you like
)
with gr.Row():
txt = gr.Textbox(
label="Your message",
placeholder="Ask anything…",
autofocus=True,
scale=4,
)
send_btn = gr.Button("Send", variant="primary", scale=1)
clear_btn = gr.Button("Clear")
# Hidden state: the agent’s full message list (LangChain messages)
agent_state = gr.State(startup_state())
# Wire up events
txt.submit(submit_user_message, [txt, chatbot, agent_state], [txt, chatbot, agent_state])
send_btn.click(submit_user_message, [txt, chatbot, agent_state], [txt, chatbot, agent_state])
clear_btn.click(clear_chat, outputs=[chatbot, agent_state])
return demo
# Entry point
if __name__ == "__main__":
# Create and launch the interface
demo = create_qna_interface()
demo.launch()