AnimeOverlord commited on
Commit
178c9ad
·
1 Parent(s): 85803c6

still initial commit

Browse files
Files changed (1) hide show
  1. app.py +50 -35
app.py CHANGED
@@ -5,35 +5,33 @@ import gradio as gr
5
  import modal
6
  from fastrtc import WebRTC, get_cloudflare_turn_credentials
7
 
8
- # Environment control flag to swap pipelines from the HF Space configuration dashboard
9
- USE_GPU_INFERENCE = os.getenv("USE_GPU_INFERENCE", "false").lower() == "true"
10
-
11
- # Connect to your deployed serverless backend running on Modal using 1.0+ SDK syntax
12
  try:
13
- if USE_GPU_INFERENCE:
14
- print("🚀 Mode: Full GPU FLUX.2 Klein Inference")
15
- voxel_model_cls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
16
- voxel_backend = voxel_model_cls().process_frame
17
- else:
18
- print("🏎️ Mode: Zero-latency WebRTC Passthrough Demo")
19
- voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
20
  except Exception as e:
21
- print(f"⚠️ Could not bind Modal backend function layout: {e}")
22
- voxel_backend = None
 
 
 
 
 
23
 
24
- # --- STREAM HANDLING ARCHITECTURE ---
25
 
26
  def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
27
  """
28
  Handles single viewport mode (Minecraft Filter).
29
- Receives frames, processes them via Modal, and returns them to the same component.
30
  """
31
  if frame is None:
32
  return None
33
 
34
  if voxel_backend is None:
35
  output_frame = frame.copy()
36
- cv2.putText(output_frame, "ERROR: Backend App Offline", (20, 40),
37
  cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
38
  return output_frame
39
 
@@ -44,24 +42,27 @@ def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.
44
  frame_bytes = encoded_image.tobytes()
45
 
46
  try:
47
- if USE_GPU_INFERENCE:
 
48
  processed_bytes = voxel_backend.remote(frame_bytes, prompt, strength)
49
- else:
50
  processed_bytes = voxel_backend.remote(frame_bytes)
51
 
52
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
53
  return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
54
  except Exception as err:
55
  fallback_frame = frame.copy()
56
- cv2.putText(fallback_frame, "⚡ Serverless Warm-up...", (20, 40),
57
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
 
 
58
  return fallback_frame
59
 
60
 
61
  def process_demo_stream(frame: np.ndarray) -> np.ndarray:
62
  """
63
  Handles separate multi-viewport tracking (Streaming Demo).
64
- Ingests frames from the raw hardware box and outputs onto the separate Modal display container.
65
  """
66
  if frame is None:
67
  return None
@@ -83,26 +84,22 @@ def process_demo_stream(frame: np.ndarray) -> np.ndarray:
83
  frame_bytes = encoded_image.tobytes()
84
 
85
  try:
86
- if USE_GPU_INFERENCE:
87
  processed_bytes = voxel_backend.remote(frame_bytes, "vanilla minecraft voxel landscape", 0.55)
88
- else:
89
  processed_bytes = voxel_backend.remote(frame_bytes)
90
 
91
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
92
  return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
93
 
94
  except Exception as err:
95
- # Format and write runtime cluster/cold-start exceptions right onto the video frame bounding container
96
  error_frame = np.zeros((480, 640, 3), dtype=np.uint8)
97
  error_frame[:] = 20
98
  cv2.putText(error_frame, "⚡ MODAL RUNTIME EXCEPTION", (40, 180),
99
  cv2.FONT_HERSHEY_DUPLEX, 0.8, (0, 140, 255), 2)
100
-
101
- err_msg = str(err)
102
- cv2.putText(error_frame, f"Error trace: {err_msg[:50]}...", (40, 240),
103
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
104
- cv2.putText(error_frame, "Check cluster logs for container scaling behavior.", (40, 290),
105
- cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 255), 1)
106
  return error_frame
107
 
108
 
@@ -113,7 +110,7 @@ custom_css = """
113
  .header-text h1 { color: #5c8e32; font-family: 'Courier New', Courier, monospace; font-weight: bold; margin-bottom: 5px; }
114
  .header-text p { color: #666; font-size: 1.1em; }
115
 
116
- /* Constrain video bounding tracks to stop screen-flooding layout shifts */
117
  video, .webrtc-video, div[class*="webrtc"] {
118
  max-height: 440px !important;
119
  width: 100% !important;
@@ -133,9 +130,9 @@ with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
133
 
134
  gr.HTML("<hr style='border: 1px solid #ddd; margin-bottom: 20px;'>")
135
 
136
- # Context switcher selection interface
137
  mode_dropdown = gr.Dropdown(
138
- choices=["Streaming Demo", "Minecraft Filter"],
139
  value="Minecraft Filter",
140
  label="🎯 Pipeline View Configuration",
141
  interactive=True
@@ -162,16 +159,17 @@ with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
162
  label="Voxelization Denoising Strength"
163
  )
164
 
 
165
  gr.Markdown(
166
  f"""
167
- > **💡 Pipeline Status:** Deployed via FastRTC. Current Target Mode: `{"GPU Inference (FLUX)" if USE_GPU_INFERENCE else "CPU Passthrough Demo"}`. Change this via the `USE_GPU_INFERENCE` Space Secret.
168
  """
169
  )
170
 
171
  # Right Hand Side Viewport Area
172
  with gr.Column(scale=2):
173
 
174
- # --- LAYOUT VARIANT 1: MINECRAFT FILTER MODE (Standard Single Viewport) ---
175
  with gr.Column(visible=True) as minecraft_layout:
176
  gr.Markdown("### 📺 Spatial Render Pipeline")
177
  webrtc_single = WebRTC(
@@ -181,7 +179,7 @@ with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
181
  rtc_configuration=get_cloudflare_turn_credentials
182
  )
183
 
184
- # --- LAYOUT VARIANT 2: STREAMING DEMO MODE (Decoupled Dual Viewports) ---
185
  with gr.Column(visible=False) as demo_layout:
186
  gr.Markdown("### 📺 Dual-Feed Stream Monitor")
187
  with gr.Row():
@@ -212,4 +210,21 @@ with gr.Blocks(css=custom_css, title="Minecraft Spatial Voxel Filter") as demo:
212
  outputs=[minecraft_layout, demo_layout]
213
  )
214
 
215
- # Wire
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  import modal
6
  from fastrtc import WebRTC, get_cloudflare_turn_credentials
7
 
8
+ # Initialize Modal connection securely on startup with clear logging
 
 
 
9
  try:
10
+ print("🚀 Attempting to connect to Modal VoxelModel class...")
11
+ VoxelModelCls = modal.Cls.from_name("flux-klein-voxel-backend", "VoxelModel")
12
+ voxel_backend = VoxelModelCls().process_frame
 
 
 
 
13
  except Exception as e:
14
+ print(f"⚠️ Modal Cls lookup failed: {e}. Falling back to Function lookup.")
15
+ try:
16
+ voxel_backend = modal.Function.from_name("flux-klein-voxel-backend", "demo_stream_frame")
17
+ except Exception as ex:
18
+ print(f"⚠️ Modal Function lookup also failed: {ex}. Backend will be offline.")
19
+ voxel_backend = None
20
+
21
 
22
+ # --- STREAM HANDLING FUNCTIONS ---
23
 
24
  def process_video_stream(frame: np.ndarray, prompt: str, strength: float) -> np.ndarray:
25
  """
26
  Handles single viewport mode (Minecraft Filter).
27
+ Receives frames via WebRTC, processes them, and returns them to the same viewport.
28
  """
29
  if frame is None:
30
  return None
31
 
32
  if voxel_backend is None:
33
  output_frame = frame.copy()
34
+ cv2.putText(output_frame, "ERROR: Modal Backend Offline", (20, 40),
35
  cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2)
36
  return output_frame
37
 
 
42
  frame_bytes = encoded_image.tobytes()
43
 
44
  try:
45
+ # Dynamically handle single-parameter or multi-parameter signatures
46
+ try:
47
  processed_bytes = voxel_backend.remote(frame_bytes, prompt, strength)
48
+ except TypeError:
49
  processed_bytes = voxel_backend.remote(frame_bytes)
50
 
51
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
52
  return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
53
  except Exception as err:
54
  fallback_frame = frame.copy()
55
+ cv2.putText(fallback_frame, "⚡ Serverless Warm-up / Processing Error...", (20, 40),
56
  cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
57
+ cv2.putText(fallback_frame, str(err)[:40], (20, 70),
58
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 1)
59
  return fallback_frame
60
 
61
 
62
  def process_demo_stream(frame: np.ndarray) -> np.ndarray:
63
  """
64
  Handles separate multi-viewport tracking (Streaming Demo).
65
+ Ingests raw hardware frames and renders processed outputs to a separate container.
66
  """
67
  if frame is None:
68
  return None
 
84
  frame_bytes = encoded_image.tobytes()
85
 
86
  try:
87
+ try:
88
  processed_bytes = voxel_backend.remote(frame_bytes, "vanilla minecraft voxel landscape", 0.55)
89
+ except TypeError:
90
  processed_bytes = voxel_backend.remote(frame_bytes)
91
 
92
  numpy_buffer = np.frombuffer(processed_bytes, dtype=np.uint8)
93
  return cv2.imdecode(numpy_buffer, cv2.IMREAD_COLOR)
94
 
95
  except Exception as err:
96
+ # Format and write runtime cluster exceptions directly onto the video container
97
  error_frame = np.zeros((480, 640, 3), dtype=np.uint8)
98
  error_frame[:] = 20
99
  cv2.putText(error_frame, "⚡ MODAL RUNTIME EXCEPTION", (40, 180),
100
  cv2.FONT_HERSHEY_DUPLEX, 0.8, (0, 140, 255), 2)
101
+ cv2.putText(error_frame, f"Error trace: {str(err)[:50]}...", (40, 240),
 
 
102
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
 
 
103
  return error_frame
104
 
105
 
 
110
  .header-text h1 { color: #5c8e32; font-family: 'Courier New', Courier, monospace; font-weight: bold; margin-bottom: 5px; }
111
  .header-text p { color: #666; font-size: 1.1em; }
112
 
113
+ /* Constrain video bounding tracks to prevent screen-flooding layout shifts */
114
  video, .webrtc-video, div[class*="webrtc"] {
115
  max-height: 440px !important;
116
  width: 100% !important;
 
130
 
131
  gr.HTML("<hr style='border: 1px solid #ddd; margin-bottom: 20px;'>")
132
 
133
+ # Pipeline view selector
134
  mode_dropdown = gr.Dropdown(
135
+ choices=["Minecraft Filter", "Streaming Demo"],
136
  value="Minecraft Filter",
137
  label="🎯 Pipeline View Configuration",
138
  interactive=True
 
159
  label="Voxelization Denoising Strength"
160
  )
161
 
162
+ status_msg = "Connected" if voxel_backend is not None else "Offline / Error"
163
  gr.Markdown(
164
  f"""
165
+ > **💡 Pipeline Status:** Deployed via FastRTC. Modal Backend Status: `{status_msg}`.
166
  """
167
  )
168
 
169
  # Right Hand Side Viewport Area
170
  with gr.Column(scale=2):
171
 
172
+ # --- VARIANT 1: MINECRAFT FILTER MODE (Bidirectional Single Box) ---
173
  with gr.Column(visible=True) as minecraft_layout:
174
  gr.Markdown("### 📺 Spatial Render Pipeline")
175
  webrtc_single = WebRTC(
 
179
  rtc_configuration=get_cloudflare_turn_credentials
180
  )
181
 
182
+ # --- VARIANT 2: STREAMING DEMO MODE (Decoupled dual boxes) ---
183
  with gr.Column(visible=False) as demo_layout:
184
  gr.Markdown("### 📺 Dual-Feed Stream Monitor")
185
  with gr.Row():
 
210
  outputs=[minecraft_layout, demo_layout]
211
  )
212
 
213
+ # Wire up single component bidirectional streaming pipeline
214
+ webrtc_single.stream(
215
+ fn=process_video_stream,
216
+ inputs=[webrtc_single, prompt_input, denoise_strength],
217
+ outputs=[webrtc_single],
218
+ time_limit=150
219
+ )
220
+
221
+ # Wire up cross-component dual tracking streaming pipeline
222
+ webrtc_raw.stream(
223
+ fn=process_demo_stream,
224
+ inputs=[webrtc_raw],
225
+ outputs=[webrtc_processed],
226
+ time_limit=150
227
+ )
228
+
229
+ if __name__ == "__main__":
230
+ demo.launch()