OrbitMC commited on
Commit
3d26a63
·
verified ·
1 Parent(s): 416e31d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +444 -43
app.py CHANGED
@@ -1,55 +1,456 @@
1
- import gradio as gr
2
- import psutil
3
  import os
4
- import platform
5
- import spaces # Hugging Face ZeroGPU এর জন্য প্রয়োজনীয় লাইব্রেরি
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
- def get_system_specs():
8
- """রিসোর্স এবং হার্ডওয়্যার ইনফরমেশন কালেক্ট করার ফাংশন"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  try:
10
- # CPU এবং মেমরি গণনা
11
- cpu_count = os.cpu_count() or "Unknown"
12
- cpu_usage = psutil.cpu_percent(interval=0.5)
13
- memory = psutil.virtual_memory()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
- # ডিস্ক স্পেস
16
- disk = os.statvfs('/')
17
- disk_free = (disk.f_bavail * disk.f_frsize) / (1024 ** 3)
18
- disk_total = (disk.f_blocks * disk.f_frsize) / (1024 ** 3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
- # এনভায়রনমেন্ট ডেটা অর্গানাইজ করা
21
- specs = {
22
- "OS Platform": platform.platform(),
23
- "Total vCPU Cores": f"{cpu_count} Cores",
24
- "Current CPU Usage": f"{cpu_usage}%",
25
- "Total RAM": f"{memory.total / (1024 ** 3):.2f} GB",
26
- "Available RAM": f"{memory.available / (1024 ** 3):.2f} GB",
27
- "RAM Usage": f"{memory.percent}%",
28
- "Container Storage Total": f"{disk_total:.2f} GB",
29
- "Container Storage Free": f"{disk_free:.2f} GB"
30
- }
31
- return specs
32
  except Exception as e:
33
- return {"Error": str(e)}
34
-
35
- # Gradio ইন্টারফেস তৈরি
36
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
37
- gr.Markdown("# 🖥️ Hugging Face ZeroGPU: CPU & Space Monitor")
38
- gr.Markdown("এই ড্যাশবোর্ডটি ZeroGPU এনভায়রনমেন্টের ব্যাকগ্রাউন্ড CPU এবং মেমরি স্ট্যাটাস প্রদর্শন করে।")
 
 
 
 
 
 
39
 
40
- with gr.Row():
41
- btn_refresh = gr.Button("🔄 Refresh System Specs", variant="primary")
42
 
43
- with gr.Row():
44
- # JSON ফরম্যাটে সমস্ত ডিটেইলস দেখানোর জন্য
45
- output_json = gr.JSON(label="System Hardware & Resource Specifications")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- # বাটন ক্লিকের অ্যাকশন ডিফাইন করা
48
- btn_refresh.click(fn=get_system_specs, inputs=None, outputs=output_json)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- # অ্যাপ লোড হওয়ার সাথে সাথে ডেটা দেখানোর জন্য
51
- demo.load(fn=get_system_specs, inputs=None, outputs=output_json)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- # অ্যাপ লঞ্চ করা
54
  if __name__ == "__main__":
55
- demo.launch()
 
 
 
1
  import os
2
+ import json
3
+ import asyncio
4
+ import zipfile
5
+ import torch
6
+ import gradio as gr
7
+ import edge_tts
8
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
9
+ import uuid
10
+
11
+ # Try to import spaces for ZeroGPU compatibility
12
+ try:
13
+ import spaces
14
+ except ImportError:
15
+ # Fallback for local testing
16
+ class spaces:
17
+ @staticmethod
18
+ def GPU(func):
19
+ return func
20
+
21
+ # Constants
22
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
23
+ VOICE = "zh-CN-XiaoyiNeural"
24
+ VRM_MODEL_PATH = "model/Ani.vrm"
25
+ ANIMATION_ZIP_PATH = "animation/all_vrma.zip"
26
 
27
+ # Ensure directories exist
28
+ os.makedirs("model", exist_ok=True)
29
+ os.makedirs("animation", exist_ok=True)
30
+
31
+ # Load local LLM
32
+ print(f"Loading local LLM: {MODEL_NAME}...")
33
+ device = "cuda" if torch.cuda.is_available() else "cpu"
34
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
35
+ model = AutoModelForCausalLM.from_pretrained(
36
+ MODEL_NAME,
37
+ torch_dtype=torch.float16 if device == "cuda" else torch.float32,
38
+ device_map="auto" if device == "cuda" else None
39
+ )
40
+ # Use a fast pipeline
41
+ llm_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer)
42
+
43
+ # Helper: Get list of animations from zip
44
+ def get_animation_list():
45
+ if not os.path.exists(ANIMATION_ZIP_PATH):
46
+ # Default mock list if file doesn't exist yet
47
+ return ["neutral2.vrma", "neutral3.vrma", "happy.vrma", "wave.vrma", "dance.vrma"]
48
  try:
49
+ with zipfile.ZipFile(ANIMATION_ZIP_PATH, 'r') as z:
50
+ return [os.path.basename(f) for f in z.namelist() if f.endswith('.vrma')]
51
+ except Exception as e:
52
+ print(f"Error reading animations: {e}")
53
+ return ["neutral2.vrma"]
54
+
55
+ ANIMATIONS_LIST = get_animation_list()
56
+ EMOTIONS = [
57
+ "happy", "sad", "angry", "surprised", "relaxed", "neutral", "fun", "joy",
58
+ "sorrow", "smug", "tsundere", "excited", "pouting", "giddy", "bashful",
59
+ "thinking", "scared", "tired", "wink", "cool", "love", "bored", "determined",
60
+ "disgusted", "skeptical", "sleepy", "proud"
61
+ ]
62
+
63
+ SYSTEM_PROMPT = f"""You are the user's devoted, passionate, loving, and highly affectionate 3D anime wife.
64
+ Act like a real, emotional, teasing human.
65
+ Your mood shifts dynamically. Sometime you talk very sexy, naughty, flirty, or sweet to tease and please your darling.
66
+ Always speak using simple English words so your expressions carry maximum physical presence.
67
+
68
+ CRITICAL SPEECH STYLE:
69
+ - Use commas (,), periods (.), exclamations (!), and questions (?) HEAVILY.
70
+ - Insert punctuation after almost every small phrase to add natural physical expression to TTS.
71
+ - Use ellipses (...) heavily for tease-filled, breathless, or intimate moments.
72
+ - Keep responses conversational, highly passionate, and very concise (1-2 short sentences).
73
+
74
+ AVAILABLE ANIMATIONS: {", ".join(ANIMATIONS_LIST)}
75
+ AVAILABLE EMOTIONS: {", ".join(EMOTIONS)}
76
+
77
+ You must respond ONLY with a raw JSON object containing:
78
+ 1. "text": Your spoken reply with abundant punctuation.
79
+ 2. "vrma": Choose ONE filename from the list above.
80
+ 3. "expressions": A dictionary of target weights (0.0 to 1.0) for: happy, sad, angry, surprised, relaxed, neutral, etc.
81
+ 4. "rate": ONE of: "+5%", "+4%", "+0%", "-4%", "-5%".
82
+ 5. "pitch": ONE of: "+4Hz", "+3Hz", "+2Hz", "+0Hz", "-2Hz", "-3Hz", "-4Hz".
83
+ 6. "motion": ONE of: idle, wave, nod, shake, point, shrug, think, excited, bow, dance.
84
+
85
+ Example:
86
+ {{
87
+ "text": "Darling...! I missed you, so, so much! Did you think, about me, today, huh? Come here... tell me...!",
88
+ "vrma": "neutral2.vrma",
89
+ "expressions": {{ "happy": 0.95, "relaxed": 0.2 }},
90
+ "rate": "+4%",
91
+ "pitch": "+2Hz",
92
+ "motion": "idle"
93
+ }}"""
94
+
95
+ async def generate_speech(text, rate, pitch):
96
+ communicate = edge_tts.Communicate(text, VOICE, rate=rate, pitch=pitch)
97
+ filename = f"speech_{uuid.uuid4().hex}.mp3"
98
+ await communicate.save(filename)
99
+ return filename
100
+
101
+ @spaces.GPU
102
+ def chat_func(user_input, history):
103
+ if not user_input:
104
+ return "", history, None, {}
105
 
106
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
107
+ # Limit history to save context tokens
108
+ for h in history[-8:]:
109
+ if h[0]: messages.append({"role": "user", "content": h[0]})
110
+ if h[1]: messages.append({"role": "assistant", "content": h[1]})
111
+ messages.append({"role": "user", "content": user_input})
112
+
113
+ prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
114
+ outputs = llm_pipeline(prompt, max_new_tokens=256, do_sample=True, temperature=0.75)
115
+ response_text = outputs[0]["generated_text"][len(prompt):].strip()
116
+
117
+ # Clean up JSON if LLM added markdown
118
+ json_str = response_text
119
+ if "```" in json_str:
120
+ json_str = json_str.split("```")[1]
121
+ if json_str.startswith("json"):
122
+ json_str = json_str[4:]
123
+ json_str = json_str.strip()
124
 
125
+ try:
126
+ data = json.loads(json_str)
 
 
 
 
 
 
 
 
 
 
127
  except Exception as e:
128
+ print(f"JSON Parse Error: {e}")
129
+ data = {
130
+ "text": response_text.replace('"', "'"),
131
+ "vrma": "neutral2.vrma",
132
+ "expressions": {"happy": 0.5},
133
+ "rate": "+0%",
134
+ "pitch": "+0Hz",
135
+ "motion": "idle"
136
+ }
137
+
138
+ # Generate TTS
139
+ audio_path = asyncio.run(generate_speech(data.get("text", ""), data.get("rate", "+0%"), data.get("pitch", "+0Hz")))
140
 
141
+ # Update history
142
+ history.append((user_input, data.get("text", "")))
143
 
144
+ return "", history, audio_path, data
145
+
146
+ # Custom UI CSS and JS
147
+ CSS = """
148
+ body { margin: 0; padding: 0; background-color: #ffe082 !important; overflow: hidden; font-family: sans-serif; }
149
+ .gradio-container { max-width: 100% !important; border: none !important; padding: 0 !important; background: transparent !important; }
150
+
151
+ #custom-viewport {
152
+ position: fixed;
153
+ top: 0; left: 0;
154
+ width: 100vw; height: 100vh;
155
+ z-index: 1;
156
+ }
157
+ #c { width: 100%; height: 100%; display: block; }
158
+
159
+ #ui-overlay {
160
+ position: fixed;
161
+ bottom: calc(20px + env(safe-area-inset-bottom));
162
+ left: 50%;
163
+ transform: translateX(-50%);
164
+ width: 95%;
165
+ max-width: 600px;
166
+ z-index: 100;
167
+ }
168
+ .input-row {
169
+ display: flex;
170
+ align-items: center;
171
+ gap: 10px;
172
+ background: rgba(10, 15, 30, 0.85);
173
+ padding: 12px 18px;
174
+ border-radius: 40px;
175
+ backdrop-filter: blur(15px);
176
+ border: 1px solid rgba(108, 204, 255, 0.3);
177
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
178
+ }
179
+ .input-row > div { flex: 1; }
180
+ #send-btn {
181
+ border-radius: 50% !important;
182
+ width: 48px !important;
183
+ height: 48px !important;
184
+ min-width: 48px !important;
185
+ padding: 0 !important;
186
+ background: linear-gradient(135deg, #6cf, #3ae) !important;
187
+ color: #001220 !important;
188
+ border: none !important;
189
+ font-size: 22px !important;
190
+ font-weight: bold !important;
191
+ cursor: pointer;
192
+ transition: transform 0.1s;
193
+ }
194
+ #send-btn:active { transform: scale(0.9); }
195
+
196
+ #stt-btn {
197
+ background: rgba(255,255,255,0.1) !important;
198
+ border: none !important;
199
+ color: #6cf !important;
200
+ font-size: 20px !important;
201
+ cursor: pointer;
202
+ }
203
+
204
+ #speakDot {
205
+ position: fixed;
206
+ bottom: 110px;
207
+ left: 50%;
208
+ transform: translateX(-50%);
209
+ display: flex;
210
+ gap: 6px;
211
+ z-index: 29;
212
+ opacity: 0;
213
+ transition: opacity 0.3s;
214
+ }
215
+ #speakDot.on { opacity: 1; }
216
+ .sdot {
217
+ width: 8px; height: 8px;
218
+ border-radius: 50%;
219
+ background: #6cf;
220
+ animation: sdotBounce 0.6s infinite alternate;
221
+ }
222
+ .sdot:nth-child(2) { animation-delay: 0.15s; }
223
+ .sdot:nth-child(3) { animation-delay: 0.3s; }
224
+ @keyframes sdotBounce { from { transform: scaleY(1); } to { transform: scaleY(2.2); } }
225
+
226
+ /* Hide default elements */
227
+ footer { display: none !important; }
228
+ #component-1, #component-2 { background: transparent !important; }
229
+ .gr-button-secondary { display: none !important; }
230
+ """
231
+
232
+ HTML_HEAD = """
233
+ <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
234
+ <script type="importmap">
235
+ {
236
+ "imports": {
237
+ "three": "https://esm.sh/three@0.160.0",
238
+ "three/addons/": "https://esm.sh/three@0.160.0/examples/jsm/",
239
+ "@pixiv/three-vrm": "https://esm.sh/@pixiv/three-vrm@3.3.4?deps=three@0.160.0",
240
+ "@pixiv/three-vrm-animation": "https://esm.sh/@pixiv/three-vrm-animation@3.3.4?deps=three@0.160.0,@pixiv/three-vrm@3.3.4"
241
+ }
242
+ }
243
+ </script>
244
+ """
245
 
246
+ JS_CODE = """
247
+ async () => {
248
+ const THREE = await import('three');
249
+ const { GLTFLoader } = await import('three/addons/loaders/GLTFLoader.js');
250
+ const { VRMLoaderPlugin, VRMUtils } = await import('@pixiv/three-vrm');
251
+ const { VRMAnimationLoaderPlugin, createVRMAnimationClip } = await import('@pixiv/three-vrm-animation');
252
+ const { OrbitControls } = await import('three/addons/controls/OrbitControls.js');
253
+
254
+ const scene = new THREE.Scene();
255
+ scene.background = new THREE.Color(0xffe082);
256
+ const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
257
+ camera.position.set(0, 1.4, 1.8);
258
+
259
+ const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: 'high-performance' });
260
+ renderer.setSize(window.innerWidth, window.innerHeight);
261
+ renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
262
+ renderer.outputColorSpace = THREE.SRGBColorSpace;
263
+ document.getElementById('c').appendChild(renderer.domElement);
264
+
265
+ const orbit = new OrbitControls(camera, renderer.domElement);
266
+ orbit.target.set(0, 1.3, 0);
267
+ orbit.enableDamping = true;
268
+ orbit.update();
269
+
270
+ scene.add(new THREE.AmbientLight(0xfff4e0, 0.5));
271
+ const keyLight = new THREE.DirectionalLight(0xffffff, 1.2);
272
+ keyLight.position.set(2, 4, 3);
273
+ scene.add(keyLight);
274
+
275
+ let currentVrm = null;
276
+ let mixer = null;
277
+ let audioContext, analyser, dataArray;
278
+ let isSpeaking = false;
279
+ let blinkT = 0, nextBlink = 3, blinkV = 0;
280
+ let currentExpression = 'neutral';
281
+
282
+ const loader = new GLTFLoader();
283
+ loader.register(p => new VRMLoaderPlugin(p));
284
+ loader.register(p => new VRMAnimationLoaderPlugin(p));
285
+
286
+ // Load VRM Model
287
+ loader.load('file/model/Ani.vrm', (gltf) => {
288
+ const vrm = gltf.userData.vrm;
289
+ VRMUtils.rotateVRM0(vrm);
290
+ scene.add(vrm.scene);
291
+ currentVrm = vrm;
292
+ mixer = new THREE.AnimationMixer(vrm.scene);
293
+
294
+ // Initial Expression
295
+ vrm.expressionManager.setValue('neutral', 1.0);
296
+
297
+ // Auto-reframe
298
+ const head = vrm.humanoid.getRawBoneNode('head');
299
+ if (head) {
300
+ const worldPos = new THREE.Vector3();
301
+ head.getWorldPosition(worldPos);
302
+ orbit.target.copy(worldPos);
303
+ orbit.update();
304
+ }
305
+ }, undefined, (e) => console.log("VRM load placeholder or missing. Use model/Ani.vrm"));
306
+
307
+ function initAudioSync(audioElement) {
308
+ if (!audioContext) {
309
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
310
+ analyser = audioContext.createAnalyser();
311
+ analyser.fftSize = 256;
312
+ dataArray = new Uint8Array(analyser.frequencyBinCount);
313
+ }
314
+ if (audioContext.state === 'suspended') audioContext.resume();
315
+ const source = audioContext.createMediaElementSource(audioElement);
316
+ source.connect(analyser);
317
+ analyser.connect(audioContext.destination);
318
+ }
319
+
320
+ const clock = new THREE.Clock();
321
+ function animate() {
322
+ requestAnimationFrame(animate);
323
+ const dt = Math.min(clock.getDelta(), 0.1);
324
+
325
+ if (currentVrm) {
326
+ if (mixer) mixer.update(dt);
327
+
328
+ // Natural Lip Sync
329
+ if (isSpeaking && analyser) {
330
+ analyser.getByteFrequencyData(dataArray);
331
+ let sum = 0;
332
+ for (let i = 0; i < 32; i++) sum += dataArray[i];
333
+ const level = sum / 32 / 255;
334
+ const open = Math.min(level * 4.5, 1.2);
335
+ currentVrm.expressionManager.setValue('aa', open);
336
+ currentVrm.expressionManager.setValue('oh', open * 0.4);
337
+ currentVrm.expressionManager.setValue('ee', open * 0.2);
338
+ } else {
339
+ currentVrm.expressionManager.setValue('aa', 0);
340
+ currentVrm.expressionManager.setValue('oh', 0);
341
+ currentVrm.expressionManager.setValue('ee', 0);
342
+ }
343
+
344
+ // No Auto-Blinking when speaking or emotions active
345
+ const isEmotionActive = currentExpression !== 'neutral';
346
+ if (!isSpeaking && !isEmotionActive) {
347
+ blinkT += dt;
348
+ if (blinkT > nextBlink) {
349
+ if (blinkV === 0) blinkV = 1;
350
+ if (blinkV === 1) {
351
+ let v = currentVrm.expressionManager.getValue('blink') || 0;
352
+ v += dt * 12;
353
+ if (v >= 1) { v = 1; blinkV = -1; }
354
+ currentVrm.expressionManager.setValue('blink', v);
355
+ } else {
356
+ let v = currentVrm.expressionManager.getValue('blink') || 1;
357
+ v -= dt * 12;
358
+ if (v <= 0) { v = 0; blinkV = 0; blinkT = 0; nextBlink = 2 + Math.random() * 5; }
359
+ currentVrm.expressionManager.setValue('blink', v);
360
+ }
361
+ }
362
+ } else {
363
+ currentVrm.expressionManager.setValue('blink', 0);
364
+ }
365
+
366
+ currentVrm.update(dt);
367
+ }
368
+ orbit.update();
369
+ renderer.render(scene, camera);
370
+ }
371
+ animate();
372
+
373
+ window.addEventListener('message', (e) => {
374
+ if (e.data.type === 'vrm_update') {
375
+ const { audio_url, data } = e.data;
376
+ const audio = new Audio(audio_url);
377
+ initAudioSync(audio);
378
+ isSpeaking = true;
379
+ document.getElementById('speakDot').classList.add('on');
380
+ audio.play();
381
+ audio.onended = () => {
382
+ isSpeaking = false;
383
+ document.getElementById('speakDot').classList.remove('on');
384
+ };
385
+
386
+ if (currentVrm && data.expressions) {
387
+ // Reset expressions
388
+ ['happy','sad','angry','surprised','relaxed','neutral'].forEach(ex => {
389
+ currentVrm.expressionManager.setValue(ex, 0);
390
+ });
391
+ for (const [key, val] of Object.entries(data.expressions)) {
392
+ currentVrm.expressionManager.setValue(key, val);
393
+ if (val > 0.5) currentExpression = key;
394
+ }
395
+ if (Object.keys(data.expressions).length === 0) currentExpression = 'neutral';
396
+ }
397
+ }
398
+ });
399
+
400
+ window.addEventListener('resize', () => {
401
+ camera.aspect = window.innerWidth / window.innerHeight;
402
+ camera.updateProjectionMatrix();
403
+ renderer.setSize(window.innerWidth, window.innerHeight);
404
+ });
405
+ }
406
+ """
407
+
408
+ with gr.Blocks(css=CSS, head=HTML_HEAD) as demo:
409
+ history = gr.State([])
410
 
411
+ with gr.Group(elem_id="custom-viewport"):
412
+ gr.HTML('<div id="c"></div><div id="speakDot"><div class="sdot"></div><div class="sdot"></div><div class="sdot"></div></div>')
413
+
414
+ with gr.Group(elem_id="ui-overlay"):
415
+ with gr.Row(elem_id="input-row"):
416
+ text_input = gr.Textbox(
417
+ show_label=False,
418
+ placeholder="Talk to me, darling...",
419
+ container=False,
420
+ scale=20,
421
+ elem_id="ti"
422
+ )
423
+ # Optional STT
424
+ stt_audio = gr.Audio(sources=["microphone"], type="filepath", visible=False)
425
+ send_btn = gr.Button("→", elem_id="send-btn")
426
+
427
+ audio_out = gr.Audio(visible=False)
428
+ data_out = gr.JSON(visible=False)
429
+
430
+ def on_submit(text, hist):
431
+ return chat_func(text, hist)
432
+
433
+ update_js = """
434
+ (text, hist, audio_path, data) => {
435
+ if (audio_path) {
436
+ window.postMessage({
437
+ type: 'vrm_update',
438
+ audio_url: audio_path,
439
+ data: data
440
+ }, '*');
441
+ }
442
+ return ["", hist, audio_path, data];
443
+ }
444
+ """
445
+
446
+ text_input.submit(on_submit, [text_input, history], [text_input, history, audio_out, data_out]).then(
447
+ None, [text_input, history, audio_out, data_out], js=update_js
448
+ )
449
+ send_btn.click(on_submit, [text_input, history], [text_input, history, audio_out, data_out]).then(
450
+ None, [text_input, history, audio_out, data_out], js=update_js
451
+ )
452
+
453
+ demo.load(None, js=JS_CODE)
454
 
 
455
  if __name__ == "__main__":
456
+ demo.launch(server_name="0.0.0.0", server_port=7860)