File size: 4,796 Bytes
c92ea65
693bdb2
c92ea65
abfac44
d8c5e10
7936d9a
e01ab7c
d8c5e10
 
abfac44
693bdb2
 
 
abfac44
693bdb2
 
 
abfac44
693bdb2
abfac44
693bdb2
 
 
 
 
 
abfac44
693bdb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7936d9a
 
693bdb2
f5327ec
693bdb2
33bbb87
7936d9a
 
 
 
33bbb87
7936d9a
 
693bdb2
 
 
f5327ec
 
693bdb2
 
 
 
e01ab7c
7936d9a
 
 
e01ab7c
 
 
 
693bdb2
e01ab7c
693bdb2
 
e01ab7c
693bdb2
e01ab7c
693bdb2
e01ab7c
693bdb2
e01ab7c
693bdb2
e01ab7c
693bdb2
 
 
 
 
 
abfac44
 
693bdb2
c92ea65
693bdb2
 
 
 
 
e01ab7c
7936d9a
693bdb2
 
 
 
 
 
e01ab7c
 
693bdb2
 
 
e01ab7c
693bdb2
e01ab7c
 
 
 
693bdb2
 
 
 
e01ab7c
693bdb2
7936d9a
 
693bdb2
 
e01ab7c
 
693bdb2
 
 
 
 
 
abfac44
 
693bdb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7936d9a
 
693bdb2
6eda213
 
abfac44
 
7936d9a
693bdb2
6eda213
 
7aaf290
8b9fcae
7aaf290
ab133b3
 
 
 
 
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
"""
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):
    # history always starts as list of dicts
    if history is None:
        history = []

    # Append the user's message
    history.append({"role": "user", "content": user_msg})

    # Build OpenAI message history
    messages = [{"role": "system", "content": SYSTEM_PROMPT}]
    messages.extend(history)

    # Inject file context
    final_user_msg = user_msg
    if file_url:
        final_user_msg = f"[Uploaded CSV file: {file_url}]\n\n{user_msg}"

    messages[-1] = {"role": "user", "content": final_user_msg}

    # ----------------------------------
    # PHASE 1 — TOOL RESOLUTION
    # ----------------------------------
    tool_phase = client.responses.create(
        model=MODEL_FAST,
        reasoning={"effort": "low"},
        tools=TOOLS,
        instructions=SYSTEM_PROMPT,
        input=messages,
    )

    tool_feedback = []

    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(str(item.content))

        if not tool_feedback:
            tool_feedback.append("No MCP tools needed.")

    else:
        tool_feedback.append("No MCP tools needed.")

    # Add assistant message with tool feedback
    history.append({"role": "assistant", "content": "\n".join(tool_feedback)})

    yield history

    # ----------------------------------
    # PHASE 2 — STREAMING FINAL ANSWER
    # ----------------------------------
    final_msg = history[-1]["content"] + "\n\n"
    history[-1]["content"] = final_msg

    stream = client.responses.create(
        model=MODEL_STREAM,
        reasoning={"effort": "low"},
        instructions=SYSTEM_PROMPT,
        input=messages + [history[-1]],
        stream=True,
    )

    for ev in stream:
        if ev.type == "response.output_text.delta":
            final_msg += ev.delta
            history[-1]["content"] = final_msg
            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,
    )