akhaliq HF Staff commited on
Commit
1eba2d5
·
1 Parent(s): fd5957e

Fix: return single JSON string from @app .api for reliable Gradio Server serialization

Browse files

The tuple return type was not being serialized by Gradio Server mode,
resulting in empty data reaching the frontend. Now returns a single
JSON string (-> str) which is the simplest and most reliable return
type for @app .api(). Frontend parses the JSON and resolves file paths
via /api/file endpoint. This keeps the Gradio client + ZeroGPU queue.

Files changed (2) hide show
  1. app.py +11 -10
  2. index.html +42 -30
app.py CHANGED
@@ -4405,7 +4405,8 @@ if __name__ == "__main__":
4405
  return HTMLResponse(content="Not found", status_code=404)
4406
  return FileResponse(resolved)
4407
 
4408
- # Expose Gradio api endpoint – returns plain strings for maximum client compatibility.
 
4409
  # File outputs are returned as absolute paths; the frontend resolves them via /api/file.
4410
  @app.api(name="run_generation")
4411
  def api_run_generation(
@@ -4423,7 +4424,7 @@ if __name__ == "__main__":
4423
  validation_timestep_shift: float = 3.5,
4424
  cfg_text_scale: float = 4.0,
4425
  enable_frame_interpolation: bool = False,
4426
- ) -> tuple[str, str, str, str]:
4427
  video_path = extract_path(input_video)
4428
  image_path = extract_path(input_image)
4429
 
@@ -4444,14 +4445,14 @@ if __name__ == "__main__":
4444
  enable_frame_interpolation=enable_frame_interpolation,
4445
  )
4446
 
4447
- # Return plain strings – empty string for missing values.
4448
- # The frontend will resolve file paths via /api/file?path=...
4449
- return (
4450
- str(output_video) if output_video else "",
4451
- str(output_image) if output_image else "",
4452
- str(output_text) if output_text else "",
4453
- str(status) if status else "",
4454
- )
4455
 
4456
  app.launch(
4457
  server_name=args.server_name,
 
4405
  return HTMLResponse(content="Not found", status_code=404)
4406
  return FileResponse(resolved)
4407
 
4408
+ # Expose Gradio api endpoint – returns a single JSON string for maximum client compatibility.
4409
+ # Using a single str return avoids all Gradio Server tuple/FileData serialization issues.
4410
  # File outputs are returned as absolute paths; the frontend resolves them via /api/file.
4411
  @app.api(name="run_generation")
4412
  def api_run_generation(
 
4424
  validation_timestep_shift: float = 3.5,
4425
  cfg_text_scale: float = 4.0,
4426
  enable_frame_interpolation: bool = False,
4427
+ ) -> str:
4428
  video_path = extract_path(input_video)
4429
  image_path = extract_path(input_image)
4430
 
 
4445
  enable_frame_interpolation=enable_frame_interpolation,
4446
  )
4447
 
4448
+ # Return a single JSON string with all result data.
4449
+ # The frontend parses this and resolves file paths via /api/file?path=...
4450
+ return json.dumps({
4451
+ "video": str(output_video) if output_video else "",
4452
+ "image": str(output_image) if output_image else "",
4453
+ "text": str(output_text) if output_text else "",
4454
+ "status": str(status) if status else "",
4455
+ })
4456
 
4457
  app.launch(
4458
  server_name=args.server_name,
index.html CHANGED
@@ -2376,40 +2376,52 @@
2376
  return;
2377
  }
2378
 
2379
- // The API now returns 4 plain strings: [videoPath, imagePath, text, status]
2380
- const rawVideo = result.data[0];
2381
- const rawImage = result.data[1];
2382
- const textData = result.data[2];
2383
- const statusMessage = result.data[3];
2384
-
2385
- console.log("displayResult parsed:", { rawVideo, rawImage, textData, statusMessage });
2386
-
2387
- // Helper: resolve a raw value (string path, FileData object, or URL) into a usable URL
2388
- function resolveMediaUrl(raw) {
2389
- if (!raw) return null;
2390
- // Plain string path from our API
2391
- if (typeof raw === "string") {
2392
- if (!raw || raw.trim() === "") return null;
2393
- if (raw.startsWith("http")) return raw;
2394
- // Absolute file path – serve via /api/file
2395
- return "/api/file?path=" + encodeURIComponent(raw);
2396
- }
2397
- // FileData object with url or path
2398
- if (raw.url) return raw.url;
2399
- if (raw.path) {
2400
- if (raw.path.startsWith("http")) return raw.path;
2401
- return "/api/file?path=" + encodeURIComponent(raw.path);
2402
  }
2403
- return null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2404
  }
2405
 
2406
- const videoUrl = resolveMediaUrl(rawVideo);
2407
- const imageUrl = resolveMediaUrl(rawImage);
2408
- console.log("Resolved URLs:", { videoUrl, imageUrl });
2409
 
2410
- // If there's an error message or empty payload but status contains text
2411
- if (statusMessage && !videoUrl && !imageUrl && !textData) {
2412
- alert(`Inference Status: ${statusMessage}`);
2413
  return;
2414
  }
2415
 
 
2376
  return;
2377
  }
2378
 
2379
+ // The API returns a single JSON string. result.data may be:
2380
+ // - An array with one element: ["json_string"]
2381
+ // - A direct string: "json_string"
2382
+ let rawData = result.data;
2383
+ if (Array.isArray(rawData)) rawData = rawData[0];
2384
+ console.log("rawData after unwrap:", rawData);
2385
+
2386
+ let parsed;
2387
+ try {
2388
+ if (typeof rawData === "string") {
2389
+ parsed = JSON.parse(rawData);
2390
+ } else if (typeof rawData === "object" && rawData !== null) {
2391
+ // Already an object (in case Gradio deserializes it)
2392
+ parsed = rawData;
2393
+ } else {
2394
+ console.warn("displayResult: Unexpected rawData type:", typeof rawData);
2395
+ resetOutputViewer();
2396
+ return;
 
 
 
 
 
2397
  }
2398
+ } catch (e) {
2399
+ console.error("displayResult: Failed to parse JSON:", e, rawData);
2400
+ resetOutputViewer();
2401
+ return;
2402
+ }
2403
+
2404
+ console.log("displayResult parsed:", parsed);
2405
+
2406
+ const videoPath = parsed.video || "";
2407
+ const imagePath = parsed.image || "";
2408
+ const textData = parsed.text || "";
2409
+ const statusMsg = parsed.status || "";
2410
+
2411
+ // Helper: resolve a file path to a servable URL
2412
+ function resolveMediaUrl(filePath) {
2413
+ if (!filePath || filePath.trim() === "") return null;
2414
+ if (filePath.startsWith("http")) return filePath;
2415
+ return "/api/file?path=" + encodeURIComponent(filePath);
2416
  }
2417
 
2418
+ const videoUrl = resolveMediaUrl(videoPath);
2419
+ const imageUrl = resolveMediaUrl(imagePath);
2420
+ console.log("Resolved URLs:", { videoUrl, imageUrl, textData, statusMsg });
2421
 
2422
+ // If there's only a status message with no actual content
2423
+ if (statusMsg && !videoUrl && !imageUrl && !textData) {
2424
+ alert(`Inference Status: ${statusMsg}`);
2425
  return;
2426
  }
2427