OppaAI commited on
Commit
d0108c1
·
verified ·
1 Parent(s): 942fc49

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -25
app.py CHANGED
@@ -8,35 +8,59 @@ import gradio as gr
8
  from huggingface_hub import HfApi, InferenceClient
9
  from pydantic import BaseModel, Field
10
 
 
 
 
11
  HF_DATASET_REPO = os.environ.get("HF_DATASET_REPO", "OppaAI/Robot_MCP")
12
  HF_VLM_MODEL = os.environ.get("HF_VLM_MODEL", "Qwen/Qwen2.5-VL-7B-Instruct")
13
 
14
- # ---------------------------------------------------
15
- # Payload Schema (Remains the same as it already expects image_b64)
16
- # ---------------------------------------------------
17
  class RobotWatchPayload(BaseModel):
 
 
 
 
 
 
 
 
18
  hf_token: str = Field(description="Your Hugging Face API token.")
19
  robot_id: str = Field(description="Robot identifier.", default="unknown")
20
  image_b64: str = Field(description="Base64 encoded image data.")
21
 
22
 
23
- # ---------------------------------------------------
24
- # Upload Helper (Remains the same)
25
- # ---------------------------------------------------
26
  def upload_image(image_b64: str, hf_token: str):
 
 
 
 
 
 
 
 
 
 
27
  try:
28
  image_bytes = base64.b64decode(image_b64)
29
  os.makedirs("/tmp", exist_ok=True)
30
 
 
31
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
32
  local_path = f"/tmp/robot_img_{timestamp}.jpg"
33
 
 
34
  with open(local_path, "wb") as f:
35
  f.write(image_bytes)
36
 
37
  filename = f"robot_{timestamp}.jpg"
38
- api = HfApi()
39
 
 
 
40
  api.upload_file(
41
  path_or_fileobj=local_path,
42
  path_in_repo=f"tmp/{filename}",
@@ -53,10 +77,20 @@ def upload_image(image_b64: str, hf_token: str):
53
  return None, None, None, 0
54
 
55
 
56
- # ---------------------------------------------------
57
- # JSON Cleaning Helper (Remains the same)
58
- # ---------------------------------------------------
59
  def safe_parse_json_from_text(text: str):
 
 
 
 
 
 
 
 
 
 
60
  if not text:
61
  return None
62
  try:
@@ -76,19 +110,29 @@ def safe_parse_json_from_text(text: str):
76
  return None
77
 
78
 
79
- # ---------------------------------------------------
80
- # Core VLM Analysis Logic (Remains the same)
81
- # ---------------------------------------------------
82
  def run_vlm_analysis(payload: RobotWatchPayload):
83
- # ... (function body remains identical to previous version) ...
 
 
 
 
 
 
 
 
84
  hf_token = payload.hf_token
85
  image_b64 = payload.image_b64
86
  robot_id = payload.robot_id
87
 
 
88
  _, hf_url, _, size_bytes = upload_image(image_b64, hf_token)
89
  if not hf_url:
90
  return {"error": "Image upload failed"}
91
 
 
92
  system_prompt = """
93
  Respond in STRICT JSON ONLY:
94
  {
@@ -131,45 +175,57 @@ Respond in STRICT JSON ONLY:
131
  }
132
 
133
 
134
- # ---------------------------------------------------
135
- # Gradio UI Function (NOW USES BASE64 STRING INPUT)
136
- # ---------------------------------------------------
137
  def robot_watch(
138
  hf_token_input: str,
139
  robot_id_input: str,
140
- image_b64_input: str # Changed input type to a string (base64)
141
  ):
142
  """
143
- Handles input from individual Gradio components (including base64 string),
144
- converts to Pydantic model, and calls the core logic.
 
 
 
 
 
 
 
 
145
  """
146
  if not image_b64_input:
147
  return {"error": "Base64 image string is empty."}
148
 
149
- # Create the Pydantic model instance manually
150
  payload_instance = RobotWatchPayload(
151
  hf_token=hf_token_input,
152
  robot_id=robot_id_input,
153
  image_b64=image_b64_input
154
  )
155
 
156
- # Call the core logic
157
  result = run_vlm_analysis(payload_instance)
158
  return result
159
 
160
 
 
 
 
161
  app = gr.Interface(
162
- fn=robot_watch, # Use the new multi-input function for the UI
163
  inputs=[
164
  gr.Textbox(label="Hugging Face Token", lines=1),
165
  gr.Textbox(label="Robot ID", lines=1, value="unknown"),
166
- gr.Textbox(label="Image Base64 String", lines=5) # Changed input component to Textbox
167
  ],
168
  outputs=gr.Json(label="Tool Output"),
169
  title="Robot MCP Server (Base64 Inputs)",
170
- description="Interface for the robot VLM analysis using individual fields, including base64 image string.",
171
  api_name="predict"
172
  )
173
 
174
  if __name__ == "__main__":
 
175
  app.launch(mcp_server=True)
 
8
  from huggingface_hub import HfApi, InferenceClient
9
  from pydantic import BaseModel, Field
10
 
11
+ # -------------------------------
12
+ # Environment variables / Constants
13
+ # -------------------------------
14
  HF_DATASET_REPO = os.environ.get("HF_DATASET_REPO", "OppaAI/Robot_MCP")
15
  HF_VLM_MODEL = os.environ.get("HF_VLM_MODEL", "Qwen/Qwen2.5-VL-7B-Instruct")
16
 
17
+ # -------------------------------
18
+ # Pydantic schema for the tool payload
19
+ # -------------------------------
20
  class RobotWatchPayload(BaseModel):
21
+ """
22
+ Defines the expected input structure for the robot VLM analysis tool.
23
+
24
+ Attributes:
25
+ hf_token (str): Your Hugging Face API token.
26
+ robot_id (str): Identifier for the robot (default "unknown").
27
+ image_b64 (str): Base64 encoded image string to analyze.
28
+ """
29
  hf_token: str = Field(description="Your Hugging Face API token.")
30
  robot_id: str = Field(description="Robot identifier.", default="unknown")
31
  image_b64: str = Field(description="Base64 encoded image data.")
32
 
33
 
34
+ # -------------------------------
35
+ # Helper function: Upload image to Hugging Face dataset
36
+ # -------------------------------
37
  def upload_image(image_b64: str, hf_token: str):
38
+ """
39
+ Decodes a base64 image string, saves it locally, and uploads to Hugging Face dataset.
40
+
41
+ Args:
42
+ image_b64 (str): Base64 encoded image data.
43
+ hf_token (str): Hugging Face API token.
44
+
45
+ Returns:
46
+ tuple: (local_path, hf_url, filename, size_bytes)
47
+ """
48
  try:
49
  image_bytes = base64.b64decode(image_b64)
50
  os.makedirs("/tmp", exist_ok=True)
51
 
52
+ # Generate unique timestamped filename
53
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
54
  local_path = f"/tmp/robot_img_{timestamp}.jpg"
55
 
56
+ # Save locally
57
  with open(local_path, "wb") as f:
58
  f.write(image_bytes)
59
 
60
  filename = f"robot_{timestamp}.jpg"
 
61
 
62
+ # Upload to Hugging Face dataset
63
+ api = HfApi()
64
  api.upload_file(
65
  path_or_fileobj=local_path,
66
  path_in_repo=f"tmp/{filename}",
 
77
  return None, None, None, 0
78
 
79
 
80
+ # -------------------------------
81
+ # Helper function: Parse JSON safely
82
+ # -------------------------------
83
  def safe_parse_json_from_text(text: str):
84
+ """
85
+ Attempts to parse JSON from text returned by the VLM model.
86
+ Strips any leading/trailing characters and handles malformed responses.
87
+
88
+ Args:
89
+ text (str): Raw text output from the model.
90
+
91
+ Returns:
92
+ dict or None: Parsed JSON dictionary, or None if parsing fails.
93
+ """
94
  if not text:
95
  return None
96
  try:
 
110
  return None
111
 
112
 
113
+ # -------------------------------
114
+ # Core VLM analysis function
115
+ # -------------------------------
116
  def run_vlm_analysis(payload: RobotWatchPayload):
117
+ """
118
+ Main logic for analyzing an image using Hugging Face VLM model.
119
+
120
+ Args:
121
+ payload (RobotWatchPayload): Validated payload containing token, robot_id, and image.
122
+
123
+ Returns:
124
+ dict: Analysis result including description, objects, and raw VLM output.
125
+ """
126
  hf_token = payload.hf_token
127
  image_b64 = payload.image_b64
128
  robot_id = payload.robot_id
129
 
130
+ # Upload the image to Hugging Face dataset
131
  _, hf_url, _, size_bytes = upload_image(image_b64, hf_token)
132
  if not hf_url:
133
  return {"error": "Image upload failed"}
134
 
135
+ # System prompt instructs VLM to return strict JSON
136
  system_prompt = """
137
  Respond in STRICT JSON ONLY:
138
  {
 
175
  }
176
 
177
 
178
+ # -------------------------------
179
+ # Gradio interface function
180
+ # -------------------------------
181
  def robot_watch(
182
  hf_token_input: str,
183
  robot_id_input: str,
184
+ image_b64_input: str
185
  ):
186
  """
187
+ Gradio wrapper for run_vlm_analysis.
188
+ Converts individual fields into Pydantic model and calls core logic.
189
+
190
+ Args:
191
+ hf_token_input (str): Hugging Face API token input from UI.
192
+ robot_id_input (str): Robot ID input from UI.
193
+ image_b64_input (str): Base64 image input from UI.
194
+
195
+ Returns:
196
+ dict: Result from run_vlm_analysis.
197
  """
198
  if not image_b64_input:
199
  return {"error": "Base64 image string is empty."}
200
 
201
+ # Create the payload instance
202
  payload_instance = RobotWatchPayload(
203
  hf_token=hf_token_input,
204
  robot_id=robot_id_input,
205
  image_b64=image_b64_input
206
  )
207
 
208
+ # Run core analysis
209
  result = run_vlm_analysis(payload_instance)
210
  return result
211
 
212
 
213
+ # -------------------------------
214
+ # Gradio App
215
+ # -------------------------------
216
  app = gr.Interface(
217
+ fn=robot_watch,
218
  inputs=[
219
  gr.Textbox(label="Hugging Face Token", lines=1),
220
  gr.Textbox(label="Robot ID", lines=1, value="unknown"),
221
+ gr.Textbox(label="Image Base64 String", lines=5)
222
  ],
223
  outputs=gr.Json(label="Tool Output"),
224
  title="Robot MCP Server (Base64 Inputs)",
225
+ description="Interface for robot VLM analysis using individual fields, including base64 image string.",
226
  api_name="predict"
227
  )
228
 
229
  if __name__ == "__main__":
230
+ # Launch Gradio app with MCP server enabled
231
  app.launch(mcp_server=True)