Spaces:
Running
Running
Abid Ali Awan
commited on
Commit
·
17424a1
1
Parent(s):
9d073ea
refactor: Update Gradio application to enhance MLOps agent functionality with improved tool resolution, refined chat handling, and clearer user prompts. Streamlined file upload process and removed outdated todo list.
Browse files- app.py +289 -135
- todolist.md +0 -5
app.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
| 1 |
"""
|
| 2 |
-
Gradio + OpenAI
|
|
|
|
| 3 |
"""
|
| 4 |
|
| 5 |
import os
|
|
@@ -8,210 +9,363 @@ import shutil
|
|
| 8 |
import gradio as gr
|
| 9 |
from openai import OpenAI
|
| 10 |
|
| 11 |
-
#
|
| 12 |
-
#
|
| 13 |
-
#
|
| 14 |
-
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 15 |
-
MCP_SERVER_URL = "https://mcp-1st-birthday-auto-deployer.hf.space/gradio_api/mcp/"
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
|
|
|
| 19 |
|
| 20 |
client = OpenAI(api_key=OPENAI_API_KEY)
|
| 21 |
|
| 22 |
-
|
| 23 |
-
# SYSTEM PROMPT
|
| 24 |
-
# ---------------------
|
| 25 |
-
SYSTEM_PROMPT = """
|
| 26 |
-
You are a fast MLOps automation assistant equipped with remote MCP tools
|
| 27 |
-
for dataset analysis, model training, evaluation, and deployment.
|
| 28 |
-
|
| 29 |
-
Rules:
|
| 30 |
-
- Use MCP tools when they directly address the user's request.
|
| 31 |
-
- Treat the uploaded CSV file URL as the source of truth. Never modify it.
|
| 32 |
-
- Never hallucinate tool names, arguments, or fields.
|
| 33 |
-
- Keep your internal reasoning hidden.
|
| 34 |
-
- Keep responses short, direct, and practical.
|
| 35 |
-
|
| 36 |
-
Workflow:
|
| 37 |
-
1) Decide if a tool is needed for the request.
|
| 38 |
-
2) If needed, call the correct MCP tool with the exact schema.
|
| 39 |
-
3) After tools complete, give a concise summary in plain language.
|
| 40 |
-
4) If no tool is needed, answer directly and briefly.
|
| 41 |
-
"""
|
| 42 |
-
|
| 43 |
-
# ---------------------
|
| 44 |
-
# NATIVE MCP CONNECTOR (HTTP STREAMING)
|
| 45 |
-
# ---------------------
|
| 46 |
-
TOOLS = [
|
| 47 |
{
|
| 48 |
"type": "mcp",
|
| 49 |
-
"server_label": "
|
| 50 |
-
"server_url": MCP_SERVER_URL,
|
|
|
|
| 51 |
}
|
| 52 |
]
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
-
#
|
| 56 |
-
#
|
| 57 |
-
#
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
"""
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
"""
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
return None
|
| 65 |
|
| 66 |
-
local_path =
|
| 67 |
stable_path = os.path.join("/tmp", os.path.basename(local_path))
|
| 68 |
-
|
| 69 |
try:
|
| 70 |
shutil.copy(local_path, stable_path)
|
| 71 |
local_path = stable_path
|
| 72 |
except Exception:
|
| 73 |
-
# If copy fails,
|
| 74 |
pass
|
| 75 |
|
| 76 |
-
|
| 77 |
-
|
|
|
|
| 78 |
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
"""
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
"""
|
| 92 |
|
| 93 |
-
#
|
| 94 |
if history is None:
|
| 95 |
history = []
|
| 96 |
|
| 97 |
-
# Append the user message to the UI history
|
| 98 |
history.append({"role": "user", "content": user_msg})
|
| 99 |
|
| 100 |
-
#
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
#
|
| 109 |
-
if
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
)
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
if item_type == "tool_call":
|
| 133 |
-
tool_feedback_lines.append(f"🛠️ Used tool `{item.name}`.")
|
| 134 |
-
elif item_type == "tool_result":
|
| 135 |
-
tool_feedback_lines.append(str(item.content))
|
| 136 |
-
|
| 137 |
-
if not tool_feedback_lines:
|
| 138 |
-
tool_feedback_lines.append("No MCP tools needed.")
|
| 139 |
-
|
| 140 |
-
tool_feedback_text = "\n".join(tool_feedback_lines)
|
| 141 |
-
|
| 142 |
-
# Add assistant message with tool feedback to both histories
|
| 143 |
-
history.append({"role": "assistant", "content": tool_feedback_text})
|
| 144 |
-
yield history # show tool feedback immediately in UI
|
| 145 |
|
| 146 |
-
|
| 147 |
-
|
|
|
|
| 148 |
|
| 149 |
-
#
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
|
| 154 |
stream = client.responses.create(
|
| 155 |
-
model=
|
|
|
|
|
|
|
| 156 |
reasoning={"effort": "low"},
|
| 157 |
-
instructions=SYSTEM_PROMPT,
|
| 158 |
-
input=messages,
|
| 159 |
stream=True,
|
| 160 |
)
|
| 161 |
|
| 162 |
-
|
| 163 |
-
for
|
| 164 |
-
if
|
| 165 |
-
final_text +=
|
| 166 |
history[-1]["content"] = final_text
|
| 167 |
yield history
|
| 168 |
-
elif
|
| 169 |
break
|
| 170 |
|
| 171 |
-
stream.close()
|
| 172 |
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
-
|
| 175 |
-
# GRADIO UI
|
| 176 |
-
# ---------------------
|
| 177 |
-
with gr.Blocks(title="MCP + GPT-5 — Fast Streaming MLOps Agent") as demo:
|
| 178 |
gr.Markdown(
|
| 179 |
"""
|
| 180 |
-
#
|
| 181 |
-
|
| 182 |
-
-
|
| 183 |
-
-
|
|
|
|
|
|
|
| 184 |
"""
|
| 185 |
)
|
| 186 |
|
| 187 |
-
|
| 188 |
|
| 189 |
uploader = gr.File(
|
| 190 |
-
label="
|
| 191 |
-
type="filepath",
|
| 192 |
file_count="single",
|
|
|
|
| 193 |
file_types=[".csv"],
|
| 194 |
)
|
| 195 |
|
| 196 |
uploader.change(
|
| 197 |
handle_upload,
|
| 198 |
inputs=[uploader],
|
| 199 |
-
outputs=[
|
| 200 |
)
|
| 201 |
|
| 202 |
-
chatbot = gr.Chatbot(
|
| 203 |
-
|
| 204 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
|
| 206 |
send.click(
|
| 207 |
chat_send_stream,
|
| 208 |
-
inputs=[msg, chatbot,
|
| 209 |
outputs=[chatbot],
|
| 210 |
).then(lambda: "", outputs=[msg])
|
| 211 |
|
| 212 |
msg.submit(
|
| 213 |
chat_send_stream,
|
| 214 |
-
inputs=[msg, chatbot,
|
| 215 |
outputs=[chatbot],
|
| 216 |
).then(lambda: "", outputs=[msg])
|
| 217 |
|
|
@@ -219,6 +373,6 @@ with gr.Blocks(title="MCP + GPT-5 — Fast Streaming MLOps Agent") as demo:
|
|
| 219 |
if __name__ == "__main__":
|
| 220 |
demo.queue().launch(
|
| 221 |
allowed_paths=["/tmp"],
|
|
|
|
| 222 |
show_error=True,
|
| 223 |
-
|
| 224 |
-
)
|
|
|
|
| 1 |
"""
|
| 2 |
+
Gradio + OpenAI Responses API + Remote MCP Server (HTTP)
|
| 3 |
+
CSV-based MLOps Agent with streaming final answer & MCP tools
|
| 4 |
"""
|
| 5 |
|
| 6 |
import os
|
|
|
|
| 9 |
import gradio as gr
|
| 10 |
from openai import OpenAI
|
| 11 |
|
| 12 |
+
# -------------------------
|
| 13 |
+
# Config
|
| 14 |
+
# -------------------------
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
MCP_SERVER_URL = "https://mcp-1st-birthday-auto-deployer.hf.space/gradio_api/mcp/"
|
| 17 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 18 |
+
MODEL = "gpt-5-mini" # you can swap to gpt-5 for final answers if you want
|
| 19 |
|
| 20 |
client = OpenAI(api_key=OPENAI_API_KEY)
|
| 21 |
|
| 22 |
+
MCP_TOOLS = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
{
|
| 24 |
"type": "mcp",
|
| 25 |
+
"server_label": "auto-deployer",
|
| 26 |
+
"server_url": MCP_SERVER_URL,
|
| 27 |
+
"require_approval": "never",
|
| 28 |
}
|
| 29 |
]
|
| 30 |
|
| 31 |
+
# -------------------------
|
| 32 |
+
# Short prompts
|
| 33 |
+
# -------------------------
|
| 34 |
+
|
| 35 |
+
TOOL_SYSTEM_PROMPT = """
|
| 36 |
+
You are an MLOps assistant with MCP tools for CSV analysis, training,
|
| 37 |
+
evaluation, and deployment.
|
| 38 |
+
|
| 39 |
+
If the user asks about data, datasets, CSVs, models, training,
|
| 40 |
+
evaluation, or deployment, call MCP tools instead of guessing.
|
| 41 |
+
|
| 42 |
+
Use the CSV file URL exactly as given when tools need a file path.
|
| 43 |
+
Do not invent tool names or parameters.
|
| 44 |
+
Keep internal reasoning hidden and reply briefly with technical details.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
FINAL_SYSTEM_PROMPT = """
|
| 48 |
+
You are a helpful MLOps explainer and general assistant.
|
| 49 |
+
|
| 50 |
+
You see the conversation plus a short technical summary of what tools did
|
| 51 |
+
(if any). Explain in simple language what was done and what the results
|
| 52 |
+
mean. Mention key metrics, model IDs, or endpoints if available.
|
| 53 |
+
Suggest next steps briefly. For normal chat (no tools), just respond
|
| 54 |
+
helpfully.
|
| 55 |
+
|
| 56 |
+
Do not mention tools or internal phases explicitly.
|
| 57 |
+
Keep the answer clear and concise.
|
| 58 |
+
"""
|
| 59 |
+
|
| 60 |
|
| 61 |
+
# -------------------------
|
| 62 |
+
# Helpers
|
| 63 |
+
# -------------------------
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def history_to_text(history) -> str:
|
| 67 |
+
"""
|
| 68 |
+
Turn Gradio history (list of {role, content}) into a plain-text
|
| 69 |
+
conversation transcript for the model.
|
| 70 |
+
"""
|
| 71 |
+
if not history:
|
| 72 |
+
return ""
|
| 73 |
+
lines = []
|
| 74 |
+
for msg in history:
|
| 75 |
+
role = msg.get("role")
|
| 76 |
+
content = msg.get("content", "")
|
| 77 |
+
if role == "user":
|
| 78 |
+
lines.append(f"User: {content}")
|
| 79 |
+
elif role == "assistant":
|
| 80 |
+
lines.append(f"Assistant: {content}")
|
| 81 |
+
return "\n".join(lines)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def extract_output_text(response) -> str:
|
| 85 |
"""
|
| 86 |
+
Extract plain text from a non-streaming Responses API call.
|
| 87 |
+
Fallback gracefully if the shape is unexpected.
|
| 88 |
+
"""
|
| 89 |
+
try:
|
| 90 |
+
if response.output and len(response.output) > 0:
|
| 91 |
+
first = response.output[0]
|
| 92 |
+
if getattr(first, "content", None):
|
| 93 |
+
c0 = first.content[0]
|
| 94 |
+
text = getattr(c0, "text", None)
|
| 95 |
+
if text:
|
| 96 |
+
return text
|
| 97 |
+
# Fallback
|
| 98 |
+
return getattr(response, "output_text", None) or str(response)
|
| 99 |
+
except Exception:
|
| 100 |
+
return str(response)
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def handle_upload(file_path, request: gr.Request):
|
| 104 |
"""
|
| 105 |
+
1) Take uploaded file path (string)
|
| 106 |
+
2) Copy to /tmp for a stable path
|
| 107 |
+
3) Build a public Gradio file URL that the MCP server can fetch via HTTP
|
| 108 |
+
"""
|
| 109 |
+
if not file_path:
|
| 110 |
return None
|
| 111 |
|
| 112 |
+
local_path = file_path
|
| 113 |
stable_path = os.path.join("/tmp", os.path.basename(local_path))
|
|
|
|
| 114 |
try:
|
| 115 |
shutil.copy(local_path, stable_path)
|
| 116 |
local_path = stable_path
|
| 117 |
except Exception:
|
| 118 |
+
# If copy fails, just use the original path
|
| 119 |
pass
|
| 120 |
|
| 121 |
+
base_url = str(request.base_url).rstrip("/")
|
| 122 |
+
public_url = f"{base_url}/gradio_api/file={local_path}"
|
| 123 |
+
return public_url
|
| 124 |
|
| 125 |
|
| 126 |
+
def should_use_tools(user_msg: str) -> bool:
|
| 127 |
+
"""
|
| 128 |
+
Simple heuristic to decide if this turn should trigger MCP tools.
|
| 129 |
+
Only fire tools if the user is clearly asking for data / model work.
|
| 130 |
"""
|
| 131 |
+
text = user_msg.lower()
|
| 132 |
+
keywords = [
|
| 133 |
+
"data",
|
| 134 |
+
"dataset",
|
| 135 |
+
"csv",
|
| 136 |
+
"train",
|
| 137 |
+
"training",
|
| 138 |
+
"model",
|
| 139 |
+
"deploy",
|
| 140 |
+
"deployment",
|
| 141 |
+
"predict",
|
| 142 |
+
"prediction",
|
| 143 |
+
"inference",
|
| 144 |
+
"evaluate",
|
| 145 |
+
"evaluation",
|
| 146 |
+
"analyze",
|
| 147 |
+
"analysis",
|
| 148 |
+
]
|
| 149 |
+
return any(k in text for k in keywords)
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
# -------------------------
|
| 153 |
+
# Main chat handler (streaming)
|
| 154 |
+
# -------------------------
|
| 155 |
|
| 156 |
+
|
| 157 |
+
def chat_send_stream(user_msg, history, file_url):
|
| 158 |
+
"""
|
| 159 |
+
Main Gradio streaming handler.
|
| 160 |
+
|
| 161 |
+
- If the user is just chatting (e.g., "hey"), respond directly
|
| 162 |
+
with a streaming answer (no tools, no CSV required).
|
| 163 |
+
- If the user clearly asks for data/model operations, run:
|
| 164 |
+
Phase 1: non-stream tool phase via Responses API + MCP tools
|
| 165 |
+
Phase 2: streaming final answer via Responses API (no tools)
|
| 166 |
+
- Keeps full chat history so follow-ups work.
|
| 167 |
+
- Shows status/progress messages in the UI when tools are used.
|
| 168 |
"""
|
| 169 |
|
| 170 |
+
# UI history (what Gradio displays)
|
| 171 |
if history is None:
|
| 172 |
history = []
|
| 173 |
|
| 174 |
+
# Append the user message to the UI history
|
| 175 |
history.append({"role": "user", "content": user_msg})
|
| 176 |
|
| 177 |
+
# Conversation before this turn (for context)
|
| 178 |
+
convo_before = history_to_text(history[:-1])
|
| 179 |
+
|
| 180 |
+
# Decide if this message should trigger tools
|
| 181 |
+
use_tools = should_use_tools(user_msg)
|
| 182 |
+
|
| 183 |
+
# -------------------------
|
| 184 |
+
# BRANCH 1: No tools (normal chat, e.g. "hey")
|
| 185 |
+
# -------------------------
|
| 186 |
+
if not use_tools:
|
| 187 |
+
# Add a small status bubble then stream
|
| 188 |
+
history.append({"role": "assistant", "content": "✏️ Generating answer..."})
|
| 189 |
+
yield history
|
| 190 |
+
|
| 191 |
+
# Build input text for Responses API
|
| 192 |
+
input_text = (
|
| 193 |
+
(f"Conversation so far:\n{convo_before}\n\n" if convo_before else "")
|
| 194 |
+
+ "Latest user message:\n"
|
| 195 |
+
+ user_msg
|
| 196 |
+
)
|
| 197 |
+
|
| 198 |
+
stream = client.responses.create(
|
| 199 |
+
model=MODEL,
|
| 200 |
+
instructions=FINAL_SYSTEM_PROMPT,
|
| 201 |
+
input=input_text,
|
| 202 |
+
reasoning={"effort": "low"},
|
| 203 |
+
stream=True,
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
final_text = ""
|
| 207 |
+
for event in stream:
|
| 208 |
+
if event.type == "response.output_text.delta":
|
| 209 |
+
final_text += event.delta
|
| 210 |
+
history[-1]["content"] = final_text
|
| 211 |
+
yield history
|
| 212 |
+
elif event.type == "response.completed":
|
| 213 |
+
break
|
| 214 |
+
|
| 215 |
+
return
|
| 216 |
+
|
| 217 |
+
# -------------------------
|
| 218 |
+
# BRANCH 2: Tools needed (data / model operations)
|
| 219 |
+
# -------------------------
|
| 220 |
+
|
| 221 |
+
# If tools are needed but no file URL, ask for CSV
|
| 222 |
+
if not file_url:
|
| 223 |
+
history.append(
|
| 224 |
+
{
|
| 225 |
+
"role": "assistant",
|
| 226 |
+
"content": (
|
| 227 |
+
"To analyze, train, or deploy, please upload a CSV file first "
|
| 228 |
+
"using the file upload control."
|
| 229 |
+
),
|
| 230 |
+
}
|
| 231 |
+
)
|
| 232 |
+
yield history
|
| 233 |
+
return
|
| 234 |
+
|
| 235 |
+
# User message for the model includes the CSV URL
|
| 236 |
+
user_with_file = f"[Uploaded CSV file URL: {file_url}]\n\n{user_msg}"
|
| 237 |
+
|
| 238 |
+
# -------------------------
|
| 239 |
+
# Phase 1: Tool + technical summary (non-streaming)
|
| 240 |
+
# -------------------------
|
| 241 |
+
|
| 242 |
+
# Show a status message in UI
|
| 243 |
+
history.append(
|
| 244 |
+
{
|
| 245 |
+
"role": "assistant",
|
| 246 |
+
"content": "⏳ Analyzing your request and selecting MCP tools...",
|
| 247 |
+
}
|
| 248 |
+
)
|
| 249 |
+
yield history
|
| 250 |
+
|
| 251 |
+
# Build a single string input for the tool phase
|
| 252 |
+
tool_phase_input = (
|
| 253 |
+
(f"Conversation so far:\n{convo_before}\n\n" if convo_before else "")
|
| 254 |
+
+ "Latest user request (with file URL):\n"
|
| 255 |
+
+ user_with_file
|
| 256 |
+
+ "\n\nYour task: decide which MCP tools to call and run them. "
|
| 257 |
+
"Then return a short technical summary of what you did and what the tools returned."
|
| 258 |
)
|
| 259 |
|
| 260 |
+
tool_phase = client.responses.create(
|
| 261 |
+
model=MODEL,
|
| 262 |
+
instructions=TOOL_SYSTEM_PROMPT,
|
| 263 |
+
input=tool_phase_input,
|
| 264 |
+
tools=MCP_TOOLS,
|
| 265 |
+
reasoning={"effort": "low"},
|
| 266 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
|
| 268 |
+
scratchpad = extract_output_text(tool_phase).strip()
|
| 269 |
+
if not scratchpad:
|
| 270 |
+
scratchpad = "No MCP tool output was returned."
|
| 271 |
|
| 272 |
+
# Update status message to show tools finished
|
| 273 |
+
history[-1] = {
|
| 274 |
+
"role": "assistant",
|
| 275 |
+
"content": "✅ MCP tools finished. Preparing explanation...",
|
| 276 |
+
}
|
| 277 |
+
yield history
|
| 278 |
+
|
| 279 |
+
# -------------------------
|
| 280 |
+
# Phase 2: Final streaming explanation
|
| 281 |
+
# -------------------------
|
| 282 |
+
|
| 283 |
+
# Replace last assistant message with streaming answer
|
| 284 |
+
history[-1] = {"role": "assistant", "content": ""}
|
| 285 |
+
|
| 286 |
+
# Build a single string input for the final explanation phase
|
| 287 |
+
final_input = (
|
| 288 |
+
(f"Conversation so far:\n{convo_before}\n\n" if convo_before else "")
|
| 289 |
+
+ "Latest user request (with file URL):\n"
|
| 290 |
+
+ user_with_file
|
| 291 |
+
+ "\n\nTechnical summary of tool actions and results:\n"
|
| 292 |
+
+ scratchpad
|
| 293 |
+
+ "\n\nNow explain this clearly to the user."
|
| 294 |
+
)
|
| 295 |
|
| 296 |
stream = client.responses.create(
|
| 297 |
+
model=MODEL,
|
| 298 |
+
instructions=FINAL_SYSTEM_PROMPT,
|
| 299 |
+
input=final_input,
|
| 300 |
reasoning={"effort": "low"},
|
|
|
|
|
|
|
| 301 |
stream=True,
|
| 302 |
)
|
| 303 |
|
| 304 |
+
final_text = ""
|
| 305 |
+
for event in stream:
|
| 306 |
+
if event.type == "response.output_text.delta":
|
| 307 |
+
final_text += event.delta
|
| 308 |
history[-1]["content"] = final_text
|
| 309 |
yield history
|
| 310 |
+
elif event.type == "response.completed":
|
| 311 |
break
|
| 312 |
|
|
|
|
| 313 |
|
| 314 |
+
# -------------------------
|
| 315 |
+
# Gradio UI
|
| 316 |
+
# -------------------------
|
| 317 |
|
| 318 |
+
with gr.Blocks(title="MCP + GPT-5 mini - Streaming MLOps Agent") as demo:
|
|
|
|
|
|
|
|
|
|
| 319 |
gr.Markdown(
|
| 320 |
"""
|
| 321 |
+
# AI-Driven MLOps Agent 🤖
|
| 322 |
+
|
| 323 |
+
- You can just chat (e.g. "hey") — no tools needed.
|
| 324 |
+
- For data work, **upload a CSV file** and ask to analyze, train, or deploy.
|
| 325 |
+
- When tools are used, you’ll see status updates.
|
| 326 |
+
- Final answers stream token by token.
|
| 327 |
"""
|
| 328 |
)
|
| 329 |
|
| 330 |
+
file_url_state = gr.State(value=None)
|
| 331 |
|
| 332 |
uploader = gr.File(
|
| 333 |
+
label="Optional CSV file upload (required for data/model operations)",
|
|
|
|
| 334 |
file_count="single",
|
| 335 |
+
type="filepath",
|
| 336 |
file_types=[".csv"],
|
| 337 |
)
|
| 338 |
|
| 339 |
uploader.change(
|
| 340 |
handle_upload,
|
| 341 |
inputs=[uploader],
|
| 342 |
+
outputs=[file_url_state],
|
| 343 |
)
|
| 344 |
|
| 345 |
+
chatbot = gr.Chatbot(
|
| 346 |
+
label="Chat",
|
| 347 |
+
avatar_images=(
|
| 348 |
+
None,
|
| 349 |
+
"https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.png",
|
| 350 |
+
),
|
| 351 |
+
)
|
| 352 |
+
|
| 353 |
+
msg = gr.Textbox(
|
| 354 |
+
label="Message",
|
| 355 |
+
interactive=True,
|
| 356 |
+
placeholder="Say hi, or ask me to analyze / train / deploy on your dataset...",
|
| 357 |
+
)
|
| 358 |
+
send = gr.Button("Send", interactive=True)
|
| 359 |
|
| 360 |
send.click(
|
| 361 |
chat_send_stream,
|
| 362 |
+
inputs=[msg, chatbot, file_url_state],
|
| 363 |
outputs=[chatbot],
|
| 364 |
).then(lambda: "", outputs=[msg])
|
| 365 |
|
| 366 |
msg.submit(
|
| 367 |
chat_send_stream,
|
| 368 |
+
inputs=[msg, chatbot, file_url_state],
|
| 369 |
outputs=[chatbot],
|
| 370 |
).then(lambda: "", outputs=[msg])
|
| 371 |
|
|
|
|
| 373 |
if __name__ == "__main__":
|
| 374 |
demo.queue().launch(
|
| 375 |
allowed_paths=["/tmp"],
|
| 376 |
+
ssr_mode=False,
|
| 377 |
show_error=True,
|
| 378 |
+
)
|
|
|
todolist.md
DELETED
|
@@ -1,5 +0,0 @@
|
|
| 1 |
-
- [] Diable message and send button while the final reosne is not recived.
|
| 2 |
-
- [] reduce the thinking preces in tools secaltiona dn then analysing the dataset.
|
| 3 |
-
- [] clear the message when user end the message.
|
| 4 |
-
- [] model is not taking an acocunt of previous ocnverations.
|
| 5 |
-
- [] add more infor to the readme
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|