jandan138 commited on
Commit
ad1357a
·
verified ·
1 Parent(s): 974eb16

Upload 10 files

Browse files
Files changed (11) hide show
  1. .gitattributes +1 -0
  2. .gitignore +41 -0
  3. app.py +275 -0
  4. app_demo.py +275 -0
  5. assets/scene_1.png +3 -0
  6. backend_api.py +53 -0
  7. config.py +46 -0
  8. logging_utils.py +89 -0
  9. requirements.txt +4 -0
  10. simulation.py +115 -0
  11. ui_components.py +43 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ assets/scene_1.png filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ *.pyo
4
+ *.pyd
5
+ .Python
6
+ build/
7
+ develop-eggs/
8
+ dist/
9
+ downloads/
10
+ eggs/
11
+ .eggs/
12
+ lib/
13
+ lib64/
14
+ parts/
15
+ sdist/
16
+ var/
17
+ wheels/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+
22
+ # Logs
23
+ logs/*.log
24
+ *.log
25
+
26
+ # Environment variables
27
+ .env
28
+ .venv
29
+ env/
30
+ venv/
31
+
32
+ # IDE
33
+ .vscode/
34
+ .idea/
35
+ *.swp
36
+ *.swo
37
+ *~
38
+
39
+ # OS
40
+ .DS_Store
41
+ Thumbs.db
app.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+ # 主入口文件,负责启动 Gradio UI
3
+ import gradio as gr
4
+ from config import SCENE_CONFIGS, MODEL_CHOICES, MODE_CHOICES
5
+ from backend_api import submit_to_backend, get_task_status, get_task_result
6
+ from logging_utils import log_access, log_submission, is_request_allowed
7
+ from simulation import stream_simulation_results, convert_to_h264
8
+ from ui_components import update_history_display, update_scene_display, update_log_display, get_scene_instruction
9
+ import os
10
+ from datetime import datetime
11
+
12
+ SESSION_TASKS = {}
13
+
14
+ def run_simulation(scene, model, mode, prompt, history, request: gr.Request):
15
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
16
+ scene_desc = SCENE_CONFIGS.get(scene, {}).get("description", scene)
17
+ user_ip = request.client.host if request else "unknown"
18
+ session_id = request.session_hash
19
+ if not is_request_allowed(user_ip):
20
+ log_submission(scene, prompt, model, user_ip, "IP blocked temporarily")
21
+ raise gr.Error("Too many requests from this IP. Please wait and try again one minute later.")
22
+ # 传递model和mode给后端
23
+ #submission_result = submit_to_backend(scene, prompt, user=model) # 可根据后端接口调整
24
+ submission_result = submit_to_backend(scene, prompt, mode, model, user_ip)
25
+ if submission_result.get("status") != "pending":
26
+ log_submission(scene, prompt, model, user_ip, "Submission failed")
27
+ raise gr.Error(f"Submission failed: {submission_result.get('message', 'unknown issue')}")
28
+ try:
29
+ task_id = submission_result["task_id"]
30
+ SESSION_TASKS[session_id] = task_id
31
+ gr.Info(f"Simulation started, task_id: {task_id}")
32
+ import time
33
+ time.sleep(5)
34
+ status = get_task_status(task_id)
35
+ result_folder = status.get("result", "")
36
+ except Exception as e:
37
+ log_submission(scene, prompt, model, user_ip, str(e))
38
+ raise gr.Error(f"error occurred when parsing submission result from backend: {str(e)}")
39
+ if not os.path.exists(result_folder):
40
+ log_submission(scene, prompt, model, user_ip, "Result folder provided by backend doesn't exist")
41
+ raise gr.Error(f"Result folder provided by backend doesn't exist: <PATH>{result_folder}")
42
+ try:
43
+ for video_path in stream_simulation_results(result_folder, task_id):
44
+ if video_path:
45
+ yield video_path, history
46
+ except Exception as e:
47
+ log_submission(scene, prompt, model, user_ip, str(e))
48
+ raise gr.Error(f"流式输出过程中出错: {str(e)}")
49
+ status = get_task_status(task_id)
50
+ if status.get("status") == "completed":
51
+ video_path = os.path.join(status.get("result"), "output.mp4")
52
+ video_path = convert_to_h264(video_path)
53
+ new_entry = {
54
+ "timestamp": timestamp,
55
+ "scene": scene,
56
+ "model": model,
57
+ "mode": mode,
58
+ "prompt": prompt,
59
+ "video_path": video_path
60
+ }
61
+ updated_history = history + [new_entry]
62
+ if len(updated_history) > 10:
63
+ updated_history = updated_history[:10]
64
+ log_submission(scene, prompt, model, user_ip, "success")
65
+ gr.Info("Simulation completed successfully!")
66
+ yield None, updated_history
67
+ elif status.get("status") == "failed":
68
+ log_submission(scene, prompt, model, user_ip, status.get('result', 'backend error'))
69
+ raise gr.Error(f"任务执行失败: {status.get('result', 'backend 未知错误')}")
70
+ yield None, history
71
+ elif status.get("status") == "terminated":
72
+ log_submission(scene, prompt, model, user_ip, "terminated")
73
+ video_path = os.path.join(result_folder, "output.mp4")
74
+ if os.path.exists(video_path):
75
+ return f"⚠️ 任务 {task_id} 被终止,已生成部分结果", video_path, history
76
+ else:
77
+ return f"⚠️ 任务 {task_id} 被终止,未生成结果", None, history
78
+ else:
79
+ log_submission(scene, prompt, model, user_ip, "missing task's status from backend")
80
+ raise gr.Error("missing task's status from backend")
81
+ yield None, history
82
+
83
+ def cleanup_session(request: gr.Request):
84
+ session_id = request.session_hash
85
+ task_id = SESSION_TASKS.pop(session_id, None)
86
+ from config import BACKEND_URL
87
+ import requests
88
+ if task_id:
89
+ try:
90
+ requests.post(f"{BACKEND_URL}/predict/terminate/{task_id}", timeout=3)
91
+ except Exception:
92
+ pass
93
+
94
+ def record_access(request: gr.Request):
95
+ user_ip = request.client.host if request else "unknown"
96
+ user_agent = request.headers.get("user-agent", "unknown")
97
+ log_access(user_ip, user_agent)
98
+ return update_log_display()
99
+
100
+
101
+
102
+ custom_css = """
103
+ #simulation-panel {
104
+ border-radius: 8px;
105
+ padding: 20px;
106
+ background: #f9f9f9;
107
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
108
+ }
109
+ #result-panel {
110
+ border-radius: 8px;
111
+ padding: 20px;
112
+ background: #f0f8ff;
113
+ }
114
+ .dark #simulation-panel { background: #2a2a2a; }
115
+ .dark #result-panel { background: #1a2a3a; }
116
+ .history-container {
117
+ max-height: 600px;
118
+ overflow-y: auto;
119
+ margin-top: 20px;
120
+ }
121
+ .history-accordion {
122
+ margin-bottom: 10px;
123
+ }
124
+ """
125
+
126
+ header_html = """
127
+ <div style="display: flex; justify-content: space-between; align-items: center; width: 100%; margin-bottom: 20px; padding: 20px; background: linear-gradient(135deg, #e0e5ec 0%, #a7b5d0 100%); border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
128
+ <div style="display: flex; align-items: center;">
129
+ <img src="https://www.shlab.org.cn/static/img/index_14.685f6559.png" alt="Institution Logo" style="height: 60px; margin-right: 20px;">
130
+ <div>
131
+ <h1 style="margin: 0; color: #2c3e50; font-weight: 600;">🤖 InternManip Model Inference Demo</h1>
132
+ <p style="margin: 4px 0 0 0; color: #5d6d7e; font-size: 0.9em;">Model trained on InternManip framework</p>
133
+ </div>
134
+ </div>
135
+ <div style="display: flex; gap: 15px; align-items: center;">
136
+ <a href="https://github.com/OpenRobotLab" target="_blank" style="text-decoration: none; transition: transform 0.2s;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'">
137
+ <img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="GitHub" style="height: 30px;">
138
+ </a>
139
+ <a href="https://huggingface.co/OpenRobotLab" target="_blank" style="text-decoration: none; transition: transform 0.2s;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'">
140
+ <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="HuggingFace" style="height: 30px;">
141
+ </a>
142
+ <a href="http://123.57.187.96:55004/" target="_blank">
143
+ <button style="padding: 8px 15px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;"
144
+ onmouseover="this.style.backgroundColor='#2980b9'; this.style.transform='scale(1.05)'"
145
+ onmouseout="this.style.backgroundColor='#3498db'; this.style.transform='scale(1)'">
146
+ Go to InternManip Demo
147
+ </button>
148
+ </a>
149
+ </div>
150
+ </div>
151
+ """
152
+
153
+
154
+
155
+ with gr.Blocks(title="InternNav Model Inference Demo", css=custom_css) as demo:
156
+ gr.HTML(header_html)
157
+
158
+ history_state = gr.State([])
159
+ with gr.Row():
160
+ with gr.Column(elem_id="simulation-panel"):
161
+ gr.Markdown("### Simulation Settings")
162
+ scene_dropdown = gr.Dropdown(
163
+ label="Choose a scene",
164
+ choices=list(SCENE_CONFIGS.keys()),
165
+ value="demo1",
166
+ interactive=True
167
+ )
168
+ scene_description = gr.Markdown("")
169
+ scene_preview = gr.Image(
170
+ label="Scene Preview",
171
+ elem_classes=["scene-preview"],
172
+ interactive=False
173
+ )
174
+ prompt_input = gr.Textbox(
175
+ label="Navigation Prompt",
176
+ value="Walk past the left side of the bed and stop in the doorway.",
177
+ placeholder="e.g.: 'Walk past the left side of the bed and stop in the doorway.'",
178
+ lines=2,
179
+ max_lines=4
180
+ )
181
+ model_dropdown = gr.Dropdown(
182
+ label="Chose a pretrained model",
183
+ choices=MODEL_CHOICES,
184
+ value=MODEL_CHOICES[0],
185
+ interactive=True
186
+ )
187
+ mode_dropdown = gr.Dropdown(
188
+ label="Select Mode",
189
+ choices=MODE_CHOICES,
190
+ value=MODE_CHOICES[0],
191
+ interactive=True
192
+ )
193
+ scene_dropdown.change(
194
+ fn=lambda scene: [update_scene_display(scene)[0], update_scene_display(scene)[1], get_scene_instruction(scene)],
195
+ inputs=scene_dropdown,
196
+ outputs=[scene_description, scene_preview, prompt_input]
197
+ )
198
+
199
+ submit_btn = gr.Button("Start Navigation Simulation", variant="primary")
200
+ with gr.Column(elem_id="result-panel"):
201
+ gr.Markdown("### Latest Simulation Result")
202
+ video_output = gr.Video(
203
+ label="Live",
204
+ interactive=False,
205
+ format="mp4",
206
+ autoplay=True,
207
+ streaming=True
208
+ )
209
+ with gr.Column() as history_container:
210
+ gr.Markdown("### History")
211
+ gr.Markdown("#### History will be reset after refresh")
212
+ history_slots = []
213
+ for i in range(10):
214
+ with gr.Column(visible=False) as slot:
215
+ with gr.Accordion(visible=False, open=False) as accordion:
216
+ video = gr.Video(interactive=False)
217
+ detail_md = gr.Markdown()
218
+ history_slots.append((slot, accordion, video, detail_md))
219
+ with gr.Accordion("查看系统访问日志(DEV ONLY)", open=False):
220
+ logs_display = gr.Markdown()
221
+ refresh_logs_btn = gr.Button("刷新日志", variant="secondary")
222
+ refresh_logs_btn.click(
223
+ update_log_display,
224
+ outputs=logs_display
225
+ )
226
+ gr.Examples(
227
+ examples=[
228
+ ["demo1", "rdp", "vlnPE", "Walk past the left side of the bed and stop in the doorway."],
229
+ ["demo2", "rdp", "vlnPE", "Walk through the bathroom, past the sink and toilet. Stop in front of the counter with the two suitcase."],
230
+ ["demo3", "rdp", "vlnPE", "Do a U-turn. Walk forward through the kitchen, heading to the black door. Walk out of the door and take a right onto the deck. Walk out on to the deck and stop."],
231
+ ["demo4", "rdp", "vlnPE", "Walk out of bathroom and stand on white bath mat."],
232
+ ["demo5", "rdp", "vlnPE", "Walk straight through the double wood doors, follow the red carpet straight to the next doorway and stop where the carpet splits off."]
233
+ ],
234
+ inputs=[scene_dropdown, model_dropdown, mode_dropdown, prompt_input],
235
+ label="Navigation Task Examples"
236
+ )
237
+ submit_btn.click(
238
+ fn=run_simulation,
239
+ inputs=[scene_dropdown, model_dropdown, mode_dropdown, prompt_input, history_state],
240
+ outputs=[video_output, history_state],
241
+ queue=True,
242
+ api_name="run_simulation"
243
+ ).then(
244
+ fn=update_history_display,
245
+ inputs=history_state,
246
+ outputs=[comp for slot in history_slots for comp in slot],
247
+ queue=True
248
+ ).then(
249
+ fn=update_log_display,
250
+ outputs=logs_display,
251
+ )
252
+ demo.load(
253
+ fn=lambda: update_scene_display("demo1"),
254
+ outputs=[scene_description, scene_preview]
255
+ ).then(
256
+ fn=update_log_display,
257
+ outputs=logs_display
258
+ )
259
+ demo.load(
260
+ fn=record_access,
261
+ inputs=None,
262
+ outputs=logs_display,
263
+ queue=False
264
+ )
265
+ demo.queue(default_concurrency_limit=8)
266
+ demo.unload(fn=cleanup_session)
267
+
268
+ if __name__ == "__main__":
269
+ demo.launch(
270
+ server_name="0.0.0.0",
271
+ server_port=7860, # Hugging Face Space默认端口
272
+ share=False,
273
+ debug=False, # 生产环境建议关闭debug
274
+ allowed_paths=["./assets", "./logs"] # 修改为相对路径
275
+ )
app_demo.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+ # 主入口文件,负责启动 Gradio UI
3
+ import gradio as gr
4
+ from config import SCENE_CONFIGS, MODEL_CHOICES, MODE_CHOICES
5
+ from backend_api import submit_to_backend, get_task_status, get_task_result
6
+ from logging_utils import log_access, log_submission, is_request_allowed
7
+ from simulation import stream_simulation_results, convert_to_h264
8
+ from ui_components import update_history_display, update_scene_display, update_log_display, get_scene_instruction
9
+ import os
10
+ from datetime import datetime
11
+
12
+ SESSION_TASKS = {}
13
+
14
+ def run_simulation(scene, model, mode, prompt, history, request: gr.Request):
15
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
16
+ scene_desc = SCENE_CONFIGS.get(scene, {}).get("description", scene)
17
+ user_ip = request.client.host if request else "unknown"
18
+ session_id = request.session_hash
19
+ if not is_request_allowed(user_ip):
20
+ log_submission(scene, prompt, model, user_ip, "IP blocked temporarily")
21
+ raise gr.Error("Too many requests from this IP. Please wait and try again one minute later.")
22
+ # 传递model和mode给后端
23
+ #submission_result = submit_to_backend(scene, prompt, user=model) # 可根据后端接口调整
24
+ submission_result = submit_to_backend(scene, prompt, mode, model, user_ip)
25
+ if submission_result.get("status") != "pending":
26
+ log_submission(scene, prompt, model, user_ip, "Submission failed")
27
+ raise gr.Error(f"Submission failed: {submission_result.get('message', 'unknown issue')}")
28
+ try:
29
+ task_id = submission_result["task_id"]
30
+ SESSION_TASKS[session_id] = task_id
31
+ gr.Info(f"Simulation started, task_id: {task_id}")
32
+ import time
33
+ time.sleep(5)
34
+ status = get_task_status(task_id)
35
+ result_folder = status.get("result", "")
36
+ except Exception as e:
37
+ log_submission(scene, prompt, model, user_ip, str(e))
38
+ raise gr.Error(f"error occurred when parsing submission result from backend: {str(e)}")
39
+ if not os.path.exists(result_folder):
40
+ log_submission(scene, prompt, model, user_ip, "Result folder provided by backend doesn't exist")
41
+ raise gr.Error(f"Result folder provided by backend doesn't exist: <PATH>{result_folder}")
42
+ try:
43
+ for video_path in stream_simulation_results(result_folder, task_id):
44
+ if video_path:
45
+ yield video_path, history
46
+ except Exception as e:
47
+ log_submission(scene, prompt, model, user_ip, str(e))
48
+ raise gr.Error(f"流式输出过程中出错: {str(e)}")
49
+ status = get_task_status(task_id)
50
+ if status.get("status") == "completed":
51
+ video_path = os.path.join(status.get("result"), "output.mp4")
52
+ video_path = convert_to_h264(video_path)
53
+ new_entry = {
54
+ "timestamp": timestamp,
55
+ "scene": scene,
56
+ "model": model,
57
+ "mode": mode,
58
+ "prompt": prompt,
59
+ "video_path": video_path
60
+ }
61
+ updated_history = history + [new_entry]
62
+ if len(updated_history) > 10:
63
+ updated_history = updated_history[:10]
64
+ log_submission(scene, prompt, model, user_ip, "success")
65
+ gr.Info("Simulation completed successfully!")
66
+ yield None, updated_history
67
+ elif status.get("status") == "failed":
68
+ log_submission(scene, prompt, model, user_ip, status.get('result', 'backend error'))
69
+ raise gr.Error(f"任务执行失败: {status.get('result', 'backend 未知错误')}")
70
+ yield None, history
71
+ elif status.get("status") == "terminated":
72
+ log_submission(scene, prompt, model, user_ip, "terminated")
73
+ video_path = os.path.join(result_folder, "output.mp4")
74
+ if os.path.exists(video_path):
75
+ return f"⚠️ 任务 {task_id} 被终止,已生成部分结果", video_path, history
76
+ else:
77
+ return f"⚠️ 任务 {task_id} 被终止,未生成结果", None, history
78
+ else:
79
+ log_submission(scene, prompt, model, user_ip, "missing task's status from backend")
80
+ raise gr.Error("missing task's status from backend")
81
+ yield None, history
82
+
83
+ def cleanup_session(request: gr.Request):
84
+ session_id = request.session_hash
85
+ task_id = SESSION_TASKS.pop(session_id, None)
86
+ from config import BACKEND_URL
87
+ import requests
88
+ if task_id:
89
+ try:
90
+ requests.post(f"{BACKEND_URL}/predict/terminate/{task_id}", timeout=3)
91
+ except Exception:
92
+ pass
93
+
94
+ def record_access(request: gr.Request):
95
+ user_ip = request.client.host if request else "unknown"
96
+ user_agent = request.headers.get("user-agent", "unknown")
97
+ log_access(user_ip, user_agent)
98
+ return update_log_display()
99
+
100
+
101
+
102
+ custom_css = """
103
+ #simulation-panel {
104
+ border-radius: 8px;
105
+ padding: 20px;
106
+ background: #f9f9f9;
107
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
108
+ }
109
+ #result-panel {
110
+ border-radius: 8px;
111
+ padding: 20px;
112
+ background: #f0f8ff;
113
+ }
114
+ .dark #simulation-panel { background: #2a2a2a; }
115
+ .dark #result-panel { background: #1a2a3a; }
116
+ .history-container {
117
+ max-height: 600px;
118
+ overflow-y: auto;
119
+ margin-top: 20px;
120
+ }
121
+ .history-accordion {
122
+ margin-bottom: 10px;
123
+ }
124
+ """
125
+
126
+ header_html = """
127
+ <div style="display: flex; justify-content: space-between; align-items: center; width: 100%; margin-bottom: 20px; padding: 20px; background: linear-gradient(135deg, #e0e5ec 0%, #a7b5d0 100%); border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
128
+ <div style="display: flex; align-items: center;">
129
+ <img src="https://www.shlab.org.cn/static/img/index_14.685f6559.png" alt="Institution Logo" style="height: 60px; margin-right: 20px;">
130
+ <div>
131
+ <h1 style="margin: 0; color: #2c3e50; font-weight: 600;">🤖 InternManip Model Inference Demo</h1>
132
+ <p style="margin: 4px 0 0 0; color: #5d6d7e; font-size: 0.9em;">Model trained on InternManip framework</p>
133
+ </div>
134
+ </div>
135
+ <div style="display: flex; gap: 15px; align-items: center;">
136
+ <a href="https://github.com/OpenRobotLab" target="_blank" style="text-decoration: none; transition: transform 0.2s;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'">
137
+ <img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="GitHub" style="height: 30px;">
138
+ </a>
139
+ <a href="https://huggingface.co/OpenRobotLab" target="_blank" style="text-decoration: none; transition: transform 0.2s;" onmouseover="this.style.transform='scale(1.1)'" onmouseout="this.style.transform='scale(1)'">
140
+ <img src="https://huggingface.co/front/assets/huggingface_logo-noborder.svg" alt="HuggingFace" style="height: 30px;">
141
+ </a>
142
+ <a href="http://123.57.187.96:55004/" target="_blank">
143
+ <button style="padding: 8px 15px; background: #3498db; color: white; border: none; border-radius: 4px; cursor: pointer; font-weight: 500; transition: all 0.2s;"
144
+ onmouseover="this.style.backgroundColor='#2980b9'; this.style.transform='scale(1.05)'"
145
+ onmouseout="this.style.backgroundColor='#3498db'; this.style.transform='scale(1)'">
146
+ Go to InternManip Demo
147
+ </button>
148
+ </a>
149
+ </div>
150
+ </div>
151
+ """
152
+
153
+
154
+
155
+ with gr.Blocks(title="InternNav Model Inference Demo", css=custom_css) as demo:
156
+ gr.HTML(header_html)
157
+
158
+ history_state = gr.State([])
159
+ with gr.Row():
160
+ with gr.Column(elem_id="simulation-panel"):
161
+ gr.Markdown("### Simulation Settings")
162
+ scene_dropdown = gr.Dropdown(
163
+ label="Choose a scene",
164
+ choices=list(SCENE_CONFIGS.keys()),
165
+ value="demo1",
166
+ interactive=True
167
+ )
168
+ scene_description = gr.Markdown("")
169
+ scene_preview = gr.Image(
170
+ label="Scene Preview",
171
+ elem_classes=["scene-preview"],
172
+ interactive=False
173
+ )
174
+ prompt_input = gr.Textbox(
175
+ label="Navigation Prompt",
176
+ value="Walk past the left side of the bed and stop in the doorway.",
177
+ placeholder="e.g.: 'Walk past the left side of the bed and stop in the doorway.'",
178
+ lines=2,
179
+ max_lines=4
180
+ )
181
+ model_dropdown = gr.Dropdown(
182
+ label="Chose a pretrained model",
183
+ choices=MODEL_CHOICES,
184
+ value=MODEL_CHOICES[0],
185
+ interactive=True
186
+ )
187
+ mode_dropdown = gr.Dropdown(
188
+ label="Select Mode",
189
+ choices=MODE_CHOICES,
190
+ value=MODE_CHOICES[0],
191
+ interactive=True
192
+ )
193
+ scene_dropdown.change(
194
+ fn=lambda scene: [update_scene_display(scene)[0], update_scene_display(scene)[1], get_scene_instruction(scene)],
195
+ inputs=scene_dropdown,
196
+ outputs=[scene_description, scene_preview, prompt_input]
197
+ )
198
+
199
+ submit_btn = gr.Button("Start Navigation Simulation", variant="primary")
200
+ with gr.Column(elem_id="result-panel"):
201
+ gr.Markdown("### Latest Simulation Result")
202
+ video_output = gr.Video(
203
+ label="Live",
204
+ interactive=False,
205
+ format="mp4",
206
+ autoplay=True,
207
+ streaming=True
208
+ )
209
+ with gr.Column() as history_container:
210
+ gr.Markdown("### History")
211
+ gr.Markdown("#### History will be reset after refresh")
212
+ history_slots = []
213
+ for i in range(10):
214
+ with gr.Column(visible=False) as slot:
215
+ with gr.Accordion(visible=False, open=False) as accordion:
216
+ video = gr.Video(interactive=False)
217
+ detail_md = gr.Markdown()
218
+ history_slots.append((slot, accordion, video, detail_md))
219
+ with gr.Accordion("查看系统访问日志(DEV ONLY)", open=False):
220
+ logs_display = gr.Markdown()
221
+ refresh_logs_btn = gr.Button("刷新日志", variant="secondary")
222
+ refresh_logs_btn.click(
223
+ update_log_display,
224
+ outputs=logs_display
225
+ )
226
+ gr.Examples(
227
+ examples=[
228
+ ["demo1", "rdp", "vlnPE", "Walk past the left side of the bed and stop in the doorway."],
229
+ ["demo2", "rdp", "vlnPE", "Walk through the bathroom, past the sink and toilet. Stop in front of the counter with the two suitcase."],
230
+ ["demo3", "rdp", "vlnPE", "Do a U-turn. Walk forward through the kitchen, heading to the black door. Walk out of the door and take a right onto the deck. Walk out on to the deck and stop."],
231
+ ["demo4", "rdp", "vlnPE", "Walk out of bathroom and stand on white bath mat."],
232
+ ["demo5", "rdp", "vlnPE", "Walk straight through the double wood doors, follow the red carpet straight to the next doorway and stop where the carpet splits off."]
233
+ ],
234
+ inputs=[scene_dropdown, model_dropdown, mode_dropdown, prompt_input],
235
+ label="Navigation Task Examples"
236
+ )
237
+ submit_btn.click(
238
+ fn=run_simulation,
239
+ inputs=[scene_dropdown, model_dropdown, mode_dropdown, prompt_input, history_state],
240
+ outputs=[video_output, history_state],
241
+ queue=True,
242
+ api_name="run_simulation"
243
+ ).then(
244
+ fn=update_history_display,
245
+ inputs=history_state,
246
+ outputs=[comp for slot in history_slots for comp in slot],
247
+ queue=True
248
+ ).then(
249
+ fn=update_log_display,
250
+ outputs=logs_display,
251
+ )
252
+ demo.load(
253
+ fn=lambda: update_scene_display("demo1"),
254
+ outputs=[scene_description, scene_preview]
255
+ ).then(
256
+ fn=update_log_display,
257
+ outputs=logs_display
258
+ )
259
+ demo.load(
260
+ fn=record_access,
261
+ inputs=None,
262
+ outputs=logs_display,
263
+ queue=False
264
+ )
265
+ demo.queue(default_concurrency_limit=8)
266
+ demo.unload(fn=cleanup_session)
267
+
268
+ if __name__ == "__main__":
269
+ demo.launch(
270
+ server_name="0.0.0.0",
271
+ server_port=7860, # Hugging Face Space默认端口
272
+ share=False,
273
+ debug=False, # 生产环境建议关闭debug
274
+ allowed_paths=["./assets", "./logs"] # 修改为相对路径
275
+ )
assets/scene_1.png ADDED

Git LFS Details

  • SHA256: 0a4e9db572bd6065e3b20c82aadfbf394cbeb9505bd1da7d19210c2763092b0e
  • Pointer size: 131 Bytes
  • Size of remote file: 116 kB
backend_api.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # backend_api.py
2
+ # 后端API交互相关
3
+ import requests
4
+ import uuid
5
+ import json
6
+ from typing import Optional
7
+ from config import API_ENDPOINTS
8
+
9
+ def submit_to_backend(scene: str, prompt: str, mode: str, model_type: str, user: str = "Gradio-user") -> dict:
10
+ job_id = str(uuid.uuid4())
11
+ data = {
12
+ "model_type": model_type,
13
+ "instruction": prompt,
14
+ "episode_type": scene,
15
+ "mode": mode,
16
+ }
17
+ payload = {
18
+ "user": user,
19
+ "task": "robot_navigation",
20
+ "job_id": job_id,
21
+ "data": json.dumps(data)
22
+ }
23
+ try:
24
+ headers = {"Content-Type": "application/json"}
25
+ response = requests.post(
26
+ API_ENDPOINTS["submit_task"],
27
+ json=payload,
28
+ headers=headers,
29
+ timeout=200
30
+ )
31
+ return response.json()
32
+ except Exception as e:
33
+ return {"status": "error", "message": str(e)}
34
+
35
+ def get_task_status(task_id: str) -> dict:
36
+ try:
37
+ response = requests.get(f"{API_ENDPOINTS['query_status']}/{task_id}", timeout=5)
38
+ try:
39
+ return response.json()
40
+ except json.JSONDecodeError:
41
+ return {"status": "error", "message": response.text}
42
+ except Exception as e:
43
+ return {"status": "error", "message": str(e)}
44
+
45
+ def get_task_result(task_id: str) -> Optional[dict]:
46
+ try:
47
+ response = requests.get(
48
+ f"{API_ENDPOINTS['get_result']}/{task_id}",
49
+ timeout=5
50
+ )
51
+ return response.json()
52
+ except Exception as e:
53
+ return None
config.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # config.py
2
+ # 配置相关:API、场景等
3
+ import os
4
+
5
+ BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:8000")
6
+ API_ENDPOINTS = {
7
+ "submit_task": f"{BACKEND_URL}/predict/video",
8
+ "query_status": f"{BACKEND_URL}/predict/task",
9
+ "get_result": f"{BACKEND_URL}//predict"
10
+ }
11
+
12
+ SCENE_CONFIGS = {
13
+ "demo1": {
14
+ "description": "Demo 1",
15
+ "objects": ["bedroom", "kitchen", "living room", ""],
16
+ "preview_image": "./assets/scene_1.png",
17
+ "default_instruction": "Walk past the left side of the bed and stop in the doorway."
18
+ },
19
+ "demo2": {
20
+ "description": "Demo 2",
21
+ "objects": ["office", "meeting room", "corridor"],
22
+ "preview_image": "./assets/scene_2.png",
23
+ "default_instruction": "Walk through the bathroom, past the sink and toilet. Stop in front of the counter with the two suitcase."
24
+ },
25
+ "demo3": {
26
+ "description": "Demo 3",
27
+ "objects": ["garage", "workshop", "storage"],
28
+ "preview_image": "./assets/scene_3.png",
29
+ "default_instruction": "Do a U-turn. Walk forward through the kitchen, heading to the black door. Walk out of the door and take a right onto the deck. Walk out on to the deck and stop."
30
+ },
31
+ "demo4": {
32
+ "description": "Demo 4",
33
+ "objects": ["garden", "patio", "pool"],
34
+ "preview_image": "./assets/scene_4.png",
35
+ "default_instruction": "Walk out of bathroom and stand on white bath mat."
36
+ },
37
+ "demo5": {
38
+ "description": "Demo 5",
39
+ "objects": ["library", "hall", "lounge"],
40
+ "preview_image": "./assets/scene_5.png",
41
+ "default_instruction": "Walk straight through the double wood doors, follow the red carpet straight to the next doorway and stop where the carpet splits off."
42
+ },
43
+ }
44
+
45
+ MODEL_CHOICES = ["rdp", "cma"]
46
+ MODE_CHOICES = ["vlnPE", "vlnCE"]
logging_utils.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # logging_utils.py
2
+ # 日志相关工具
3
+ import os
4
+ import json
5
+ from datetime import datetime,timedelta
6
+ from collections import defaultdict
7
+
8
+ LOG_DIR = "/opt/nav-fronted/logs"
9
+ ACCESS_LOG = os.path.join(LOG_DIR, "access.log")
10
+ SUBMISSION_LOG = os.path.join(LOG_DIR, "submissions.log")
11
+
12
+ os.makedirs(LOG_DIR, exist_ok=True)
13
+
14
+ IP_REQUEST_RECORDS = defaultdict(list)
15
+ IP_LIMIT = 5
16
+
17
+ def is_request_allowed(ip: str) -> bool:
18
+ now = datetime.now()
19
+ IP_REQUEST_RECORDS[ip] = [t for t in IP_REQUEST_RECORDS[ip] if now - t < timedelta(minutes=1)]
20
+ if len(IP_REQUEST_RECORDS[ip]) < IP_LIMIT:
21
+ IP_REQUEST_RECORDS[ip].append(now)
22
+ return True
23
+ return False
24
+
25
+ def log_access(user_ip: str = None, user_agent: str = None):
26
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
27
+ log_entry = {
28
+ "timestamp": timestamp,
29
+ "type": "access",
30
+ "user_ip": user_ip or "unknown",
31
+ "user_agent": user_agent or "unknown"
32
+ }
33
+ with open(ACCESS_LOG, "a") as f:
34
+ f.write(json.dumps(log_entry) + "\n")
35
+
36
+ def log_submission(scene: str, prompt: str, model: str, user: str = "anonymous", res: str = "unknown"):
37
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
38
+ log_entry = {
39
+ "timestamp": timestamp,
40
+ "type": "submission",
41
+ "user": user,
42
+ "scene": scene,
43
+ "prompt": prompt,
44
+ "model": model,
45
+ "res": res
46
+ }
47
+ with open(SUBMISSION_LOG, "a") as f:
48
+ f.write(json.dumps(log_entry) + "\n")
49
+
50
+ def read_logs(log_type: str = "all", max_entries: int = 50) -> list:
51
+ logs = []
52
+ if log_type in ["all", "access"]:
53
+ try:
54
+ with open(ACCESS_LOG, "r") as f:
55
+ for line in f:
56
+ logs.append(json.loads(line.strip()))
57
+ except FileNotFoundError:
58
+ pass
59
+ if log_type in ["all", "submission"]:
60
+ try:
61
+ with open(SUBMISSION_LOG, "r") as f:
62
+ for line in f:
63
+ logs.append(json.loads(line.strip()))
64
+ except FileNotFoundError:
65
+ pass
66
+ logs.sort(key=lambda x: x["timestamp"], reverse=True)
67
+ return logs[:max_entries]
68
+
69
+ def format_logs_for_display(logs: list) -> str:
70
+ if not logs:
71
+ return "No log record"
72
+ markdown = "### System Access Log\n\n"
73
+ markdown += "| Time | Type | User/IP | Details |\n"
74
+ markdown += "|------|------|---------|----------|\n"
75
+ for log in logs:
76
+ timestamp = log.get("timestamp", "unknown")
77
+ log_type = "Access" if log.get("type") == "access" else "Submission"
78
+ if log_type == "Access":
79
+ user = log.get("user_ip", "unknown")
80
+ details = f"User-Agent: {log.get('user_agent', 'unknown')}"
81
+ else:
82
+ user = log.get("user", "anonymous")
83
+ result = log.get('res', 'unknown')
84
+ if result != "success":
85
+ if len(result) > 40:
86
+ result = f"{result[:20]}...{result[-20:]}"
87
+ details = f"Scene: {log.get('scene', 'unknown')}, Prompt: {log.get('prompt', '')}, Model: {log.get('model', 'unknown')}, result: {result}"
88
+ markdown += f"| {timestamp} | {log_type} | {user} | {details} |\n"
89
+ return markdown
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ requests>=2.28.0
3
+ opencv-python>=4.6.0
4
+ numpy>=1.21.0
simulation.py ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # simulation.py
2
+ # 仿真与视频相关
3
+ import os
4
+ import time
5
+ import uuid
6
+ import cv2
7
+ import numpy as np
8
+ from typing import List
9
+ import gradio as gr
10
+ from backend_api import get_task_status
11
+
12
+ def stream_simulation_results(result_folder: str, task_id: str, fps: int = 6):
13
+ result_folder = os.path.join(result_folder, "images")
14
+ os.makedirs(result_folder, exist_ok=True)
15
+ frame_buffer: List[np.ndarray] = []
16
+ frames_per_segment = fps * 2
17
+ processed_files = set()
18
+ width, height = 0, 0
19
+ last_status_check = 0
20
+ status_check_interval = 5
21
+ max_time = 240
22
+ while max_time > 0:
23
+ max_time -= 1
24
+ current_time = time.time()
25
+ if current_time - last_status_check > status_check_interval:
26
+ status = get_task_status(task_id)
27
+ if status.get("status") == "completed":
28
+ process_remaining_images(result_folder, processed_files, frame_buffer)
29
+ if frame_buffer:
30
+ yield create_video_segment(frame_buffer, fps, width, height)
31
+ break
32
+ elif status.get("status") == "failed":
33
+ raise gr.Error(f"任务执行失败: {status.get('result', '未知错误')}")
34
+ elif status.get("status") == "terminated":
35
+ break
36
+ last_status_check = current_time
37
+ current_files = sorted(
38
+ [f for f in os.listdir(result_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg'))],
39
+ key=lambda x: os.path.splitext(x)[0]
40
+ )
41
+ new_files = [f for f in current_files if f not in processed_files]
42
+ has_new_frames = False
43
+ for filename in new_files:
44
+ try:
45
+ img_path = os.path.join(result_folder, filename)
46
+ frame = cv2.imread(img_path)
47
+ if frame is not None:
48
+ if width == 0:
49
+ height, width = frame.shape[:2]
50
+ frame_buffer.append(frame)
51
+ processed_files.add(filename)
52
+ has_new_frames = True
53
+ except Exception:
54
+ pass
55
+ if has_new_frames and len(frame_buffer) >= frames_per_segment:
56
+ segment_frames = frame_buffer[:frames_per_segment]
57
+ frame_buffer = frame_buffer[frames_per_segment:]
58
+ yield create_video_segment(segment_frames, fps, width, height)
59
+ time.sleep(1)
60
+ if max_time <= 0:
61
+ raise gr.Error("timeout 240s")
62
+
63
+ def create_video_segment(frames: List[np.ndarray], fps: int, width: int, height: int) -> str:
64
+ os.makedirs("/opt/gradio_demo/tasks/video_chunk", exist_ok=True)
65
+ segment_name = f"/opt/gradio_demo/tasks/video_chunk/output_{uuid.uuid4()}.mp4"
66
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
67
+ out = cv2.VideoWriter(segment_name, fourcc, fps, (width, height))
68
+ for frame in frames:
69
+ out.write(frame)
70
+ out.release()
71
+ return segment_name
72
+
73
+ def process_remaining_images(result_folder: str, processed_files: set, frame_buffer: List[np.ndarray]):
74
+ current_files = sorted(
75
+ [f for f in os.listdir(result_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg'))],
76
+ key=lambda x: os.path.splitext(x)[0]
77
+ )
78
+ new_files = [f for f in current_files if f not in processed_files]
79
+ for filename in new_files:
80
+ try:
81
+ img_path = os.path.join(result_folder, filename)
82
+ frame = cv2.imread(img_path)
83
+ if frame is not None:
84
+ frame_buffer.append(frame)
85
+ processed_files.add(filename)
86
+ except Exception:
87
+ pass
88
+
89
+ def convert_to_h264(video_path):
90
+ import shutil
91
+ base, ext = os.path.splitext(video_path)
92
+ video_path_h264 = f"{base}_h264.mp4"
93
+ ffmpeg_bin = "/root/anaconda3/envs/gradio/bin/ffmpeg"
94
+ if not os.path.exists(ffmpeg_bin):
95
+ ffmpeg_bin = shutil.which("ffmpeg")
96
+ if ffmpeg_bin is None:
97
+ raise RuntimeError("❌ 找不到 ffmpeg,请确保其已安装并在 PATH 中")
98
+ ffmpeg_cmd = [
99
+ ffmpeg_bin,
100
+ "-i", video_path,
101
+ "-c:v", "libx264",
102
+ "-preset", "slow",
103
+ "-crf", "23",
104
+ "-c:a", "aac",
105
+ "-movflags", "+faststart",
106
+ video_path_h264
107
+ ]
108
+ import subprocess
109
+ try:
110
+ result = subprocess.run(ffmpeg_cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
111
+ if not os.path.exists(video_path_h264):
112
+ raise FileNotFoundError(f"⚠️ H.264 文件未生成: {video_path_h264}")
113
+ return video_path_h264
114
+ except Exception as e:
115
+ raise
ui_components.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui_components.py
2
+ # Gradio界面相关和辅助函数
3
+ import gradio as gr
4
+ from config import SCENE_CONFIGS
5
+ from logging_utils import read_logs, format_logs_for_display
6
+
7
+ def update_history_display(history: list) -> list:
8
+ updates = []
9
+ for i in range(10):
10
+ if i < len(history):
11
+ entry = history[i]
12
+ label_text = f"Simulation {i+1} scene: {entry['scene']}, model: {entry.get('model','')}, mode: {entry.get('mode','')}, prompt: {entry['prompt']}"
13
+ updates.extend([
14
+ gr.update(visible=True),
15
+ gr.update(visible=True, label=label_text, open=False),
16
+ gr.update(value=entry['video_path'], visible=True),
17
+ gr.update(value=f"{entry['timestamp']}")
18
+ ])
19
+ else:
20
+ updates.extend([
21
+ gr.update(visible=False),
22
+ gr.update(visible=False),
23
+ gr.update(value=None, visible=False),
24
+ gr.update(value="")
25
+ ])
26
+ return updates
27
+
28
+ def update_scene_display(scene: str):
29
+ config = SCENE_CONFIGS.get(scene, {})
30
+ desc = config.get("description", "No Description")
31
+ objects = "、".join(config.get("objects", []))
32
+ image = config.get("preview_image", None)
33
+ markdown = f"**{desc}** \nPlaces Included: {objects}"
34
+ return markdown, image
35
+
36
+ def get_scene_instruction(scene: str):
37
+ """根据场景获取默认指令"""
38
+ config = SCENE_CONFIGS.get(scene, {})
39
+ return config.get("default_instruction", "")
40
+
41
+ def update_log_display():
42
+ logs = read_logs()
43
+ return format_logs_for_display(logs)