""" 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, )