mlops-agent / app.py
Abid Ali Awan
refactor: Revamp Gradio MCP Connector to enhance performance and user experience by implementing a two-phase chat process with tool resolution and streaming responses, updating the system prompt for clarity, and improving file upload handling.
693bdb2
raw
history blame
5.06 kB
"""
Gradio + OpenAI MCP Connector — Clean, Fast, Streaming, With File Upload
"""
import os
import shutil
import gradio as gr
from openai import OpenAI
# ---------------------
# CONFIGURATION
# ---------------------
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
MCP_SERVER_URL = "https://mcp-1st-birthday-auto-deployer.hf.space/gradio_api/mcp/"
MODEL_FAST = "gpt-5-mini" # for tool resolution
MODEL_STREAM = "gpt-5.1" # for final streaming reply
client = OpenAI(api_key=OPENAI_API_KEY)
SYSTEM_PROMPT = """
You are a fast MLOps assistant with access to remote MCP tools.
Use tools only when necessary.
Keep reasoning effort LOW for speed.
After tools run, summarize clearly and concisely.
"""
# ---------------------
# NATIVE MCP CONNECTOR
# ---------------------
TOOLS = [
{
"type": "mcp",
"server_label": "deploy_tools",
"server_url": MCP_SERVER_URL,
# transport auto-detected; HF space supports HTTP
}
]
# ---------------------
# FILE UPLOAD HANDLER
# ---------------------
def handle_upload(file_obj, request: gr.Request):
if file_obj is None:
return None
# Ensure file is in a stable path
local_path = file_obj.name
stable_path = os.path.join("/tmp", os.path.basename(local_path))
try:
shutil.copy(local_path, stable_path)
local_path = stable_path
except Exception:
pass
# Build public Gradio URL
base = str(request.base_url).rstrip("/")
return f"{base}/gradio_api/file={local_path}"
# ---------------------
# MAIN CHAT HANDLER
# ---------------------
def chat_send_stream(user_msg, history, file_url):
"""
2-phase pipeline:
PHASE 1 ➜ Non-streaming tool resolution using gpt-5-mini
PHASE 2 ➜ Streaming final output using gpt-5
"""
if history is None:
history = []
# Build message history for OpenAI
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
for u, a in history:
messages.append({"role": "user", "content": u})
if a:
messages.append({"role": "assistant", "content": a})
# Inject file context
if file_url:
user_msg_full = f"[Uploaded CSV file: {file_url}]\n\n{user_msg}"
else:
user_msg_full = user_msg
messages.append({"role": "user", "content": user_msg_full})
# -----------------------------
# PHASE 1 — TOOL RESOLUTION
# -----------------------------
tool_phase = client.responses.create(
model=MODEL_FAST,
reasoning={"effort": "low"},
tools=TOOLS,
instructions=SYSTEM_PROMPT,
input=messages,
)
tool_feedback = []
# Detect tool calls (if any)
if tool_phase.output:
for item in tool_phase.output:
if item.type == "tool_call":
tool_feedback.append(f"🛠️ Used tool `{item.name}`.")
elif item.type == "tool_result":
tool_feedback.append(f"{item.content}")
if not tool_feedback:
tool_feedback.append("No MCP tools needed.")
else:
tool_feedback.append("No MCP tools needed.")
# Append tool results to messages before final generation
messages.append({"role": "assistant", "content": "\n".join(tool_feedback)})
# Yield intermediate tool feedback to the UI
history.append((user_msg, "\n".join(tool_feedback)))
yield history
# -----------------------------
# PHASE 2 — STREAMING FINAL ANSWER
# -----------------------------
stream = client.responses.create(
model=MODEL_STREAM,
reasoning={"effort": "low"},
instructions=SYSTEM_PROMPT,
input=messages,
stream=True,
)
final_text = ""
for ev in stream:
if ev.type == "response.output_text.delta":
final_text += ev.delta
history[-1] = (user_msg, "\n".join(tool_feedback) + "\n\n" + final_text)
yield history
elif ev.type == "response.completed":
break
stream.close()
# ---------------------
# GRADIO UI
# ---------------------
with gr.Blocks(title="MCP + GPT-5 — Fast Streaming MLOps Agent") as demo:
gr.Markdown("""
# 🚀 AI-Driven MLOps Agent (MCP-Powered)
- Upload a CSV file
- Tools resolve instantly
- Final answer streams smoothly
""")
file_state = gr.State()
uploader = gr.File(label="Upload CSV file", type="filepath", file_count="single")
uploader.change(handle_upload, inputs=[uploader], outputs=[file_state])
chatbot = gr.Chatbot(label="Chat")
msg = gr.Textbox(label="Message")
send = gr.Button("Send")
send.click(
chat_send_stream,
inputs=[msg, chatbot, file_state],
outputs=[chatbot],
).then(lambda: "", outputs=[msg])
msg.submit(
chat_send_stream,
inputs=[msg, chatbot, file_state],
outputs=[chatbot],
).then(lambda: "", outputs=[msg])
if __name__ == "__main__":
demo.queue().launch(
allowed_paths=["/tmp"],
show_error=True,
quiet=True,
)