Spaces:
Sleeping
Sleeping
File size: 3,339 Bytes
ad6dc26 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | """Gradio chat UI for the Meridian support chatbot.
Run with: uv run python -m app.ui
"""
import asyncio
import uuid
from contextlib import asynccontextmanager
import gradio as gr
from agents import SQLiteSession
from .agent import build_agent
from .chat import chat_turn
from .config import Config
from .mcp_client import build_mcp_server
CONFIG = Config()
@asynccontextmanager
async def lifespan():
"""Connect MCP once, share across all chat turns; clean up on exit."""
server = build_mcp_server(CONFIG.mcp_server_url)
await server.connect()
try:
yield server
finally:
await server.cleanup()
def build_ui() -> gr.Blocks:
with gr.Blocks(title="Meridian Support") as demo:
gr.Markdown("# Meridian Electronics — Support Chat")
cost_badge = gr.Markdown("**Conversation cost:** $0.00000")
chatbot = gr.Chatbot(height=480)
msg = gr.Textbox(placeholder="Ask about products, orders, or your account...", show_label=False)
clear = gr.Button("Reset conversation")
# Per-browser-session state.
agent_state = gr.State()
session_state = gr.State()
running_cost = gr.State(0.0)
async def initialize():
# Lazy: a single shared MCP server, attached to a fresh agent + session per browser.
# For the prototype we accept that the MCP server reconnects per Gradio process restart.
server = build_mcp_server(CONFIG.mcp_server_url)
await server.connect()
agent = build_agent(CONFIG.openai_model, server)
session = SQLiteSession(f"meridian-{uuid.uuid4().hex[:8]}")
return agent, session, 0.0, "**Conversation cost:** $0.00000"
async def respond(user_message, history, agent, session, cost_so_far):
history = (history or []) + [
{"role": "user", "content": user_message},
{"role": "assistant", "content": ""},
]
cost_label = f"**Conversation cost:** ${cost_so_far:.5f}"
yield history, cost_so_far, cost_label, ""
text = ""
async for partial, cost in chat_turn(user_message, agent, session):
text = partial
history[-1]["content"] = text
if cost is None:
yield history, cost_so_far, cost_label, ""
else:
cost_so_far = cost_so_far + cost.usd
cost_label = (
f"**Conversation cost:** ${cost_so_far:.5f} "
f"· last turn: {cost.format()}"
)
yield history, cost_so_far, cost_label, ""
async def reset():
session = SQLiteSession(f"meridian-{uuid.uuid4().hex[:8]}")
return [], session, 0.0, "**Conversation cost:** $0.00000"
demo.load(initialize, outputs=[agent_state, session_state, running_cost, cost_badge])
msg.submit(
respond,
inputs=[msg, chatbot, agent_state, session_state, running_cost],
outputs=[chatbot, running_cost, cost_badge, msg],
)
clear.click(reset, outputs=[chatbot, session_state, running_cost, cost_badge])
return demo
demo = build_ui()
if __name__ == "__main__":
demo.queue().launch()
|