seawolf2357 commited on
Commit
6b10c8f
·
verified ·
1 Parent(s): 27bbb1b

Create app.-backup.py

Browse files
Files changed (1) hide show
  1. app.-backup.py +380 -0
app.-backup.py ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import base64
3
+ import json
4
+ from pathlib import Path
5
+ import os
6
+ import numpy as np
7
+ import openai
8
+ from dotenv import load_dotenv
9
+ from fastapi import FastAPI, Request
10
+ from fastapi.responses import HTMLResponse, StreamingResponse
11
+ from fastrtc import (
12
+ AdditionalOutputs,
13
+ AsyncStreamHandler,
14
+ Stream,
15
+ get_twilio_turn_credentials,
16
+ wait_for_item,
17
+ )
18
+ from gradio.utils import get_space
19
+ from openai.types.beta.realtime import ResponseAudioTranscriptDoneEvent
20
+ import httpx
21
+ from typing import Optional, List, Dict
22
+ import gradio as gr
23
+
24
+ load_dotenv()
25
+
26
+ SAMPLE_RATE = 24000
27
+
28
+ # HTML content embedded as a string
29
+ HTML_CONTENT = """<!DOCTYPE html>
30
+ <html lang="ko">
31
+
32
+ <head>
33
+ <meta charset="UTF-8">
34
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
35
+ <title>MOUSE 음성 챗</title>
36
+ <style>
37
+ :root {
38
+ --primary-color: #6f42c1;
39
+ --secondary-color: #563d7c;
40
+ --dark-bg: #121212;
41
+ --card-bg: #1e1e1e;
42
+ --text-color: #f8f9fa;
43
+ --border-color: #333;
44
+ --hover-color: #8a5cf6;
45
+ }
46
+ body {
47
+ font-family: "SF Pro Display", -apple-system, BlinkMacSystemFont, sans-serif;
48
+ background-color: var(--dark-bg);
49
+ color: var(--text-color);
50
+ margin: 0;
51
+ padding: 0;
52
+ height: 100vh;
53
+ display: flex;
54
+ flex-direction: column;
55
+ overflow: hidden;
56
+ }
57
+ .container {
58
+ max-width: 900px;
59
+ margin: 0 auto;
60
+ padding: 20px;
61
+ flex-grow: 1;
62
+ display: flex;
63
+ flex-direction: column;
64
+ width: 100%;
65
+ height: calc(100vh - 40px);
66
+ box-sizing: border-box;
67
+ }
68
+ .header {
69
+ text-align: center;
70
+ padding: 20px 0;
71
+ border-bottom: 1px solid var(--border-color);
72
+ margin-bottom: 20px;
73
+ flex-shrink: 0;
74
+ }
75
+ .logo {
76
+ display: flex;
77
+ align-items: center;
78
+ justify-content: center;
79
+ gap: 10px;
80
+ }
81
+ .logo h1 {
82
+ margin: 0;
83
+ background: linear-gradient(135deg, var(--primary-color), #a78bfa);
84
+ -webkit-background-clip: text;
85
+ background-clip: text;
86
+ color: transparent;
87
+ font-size: 32px;
88
+ letter-spacing: 1px;
89
+ }
90
+ /* Web search toggle */
91
+ .search-toggle {
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ gap: 10px;
96
+ margin-top: 15px;
97
+ }
98
+ .toggle-switch {
99
+ position: relative;
100
+ width: 50px;
101
+ height: 26px;
102
+ background-color: #ccc;
103
+ border-radius: 13px;
104
+ cursor: pointer;
105
+ transition: background-color 0.3s;
106
+ }
107
+ .toggle-switch.active {
108
+ background-color: var(--primary-color);
109
+ }
110
+ .toggle-slider {
111
+ position: absolute;
112
+ top: 3px;
113
+ left: 3px;
114
+ width: 20px;
115
+ height: 20px;
116
+ background-color: white;
117
+ border-radius: 50%;
118
+ transition: transform 0.3s;
119
+ }
120
+ .toggle-switch.active .toggle-slider {
121
+ transform: translateX(24px);
122
+ }
123
+ .search-label {
124
+ font-size: 14px;
125
+ color: #aaa;
126
+ }
127
+ .chat-container {
128
+ border-radius: 12px;
129
+ background-color: var(--card-bg);
130
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
131
+ padding: 20px;
132
+ flex-grow: 1;
133
+ display: flex;
134
+ flex-direction: column;
135
+ border: 1px solid var(--border-color);
136
+ overflow: hidden;
137
+ min-height: 0;
138
+ }
139
+ .chat-messages {
140
+ flex-grow: 1;
141
+ overflow-y: auto;
142
+ padding: 10px;
143
+ scrollbar-width: thin;
144
+ scrollbar-color: var(--primary-color) var(--card-bg);
145
+ min-height: 0;
146
+ }
147
+ .chat-messages::-webkit-scrollbar {
148
+ width: 6px;
149
+ }
150
+ .chat-messages::-webkit-scrollbar-thumb {
151
+ background-color: var(--primary-color);
152
+ border-radius: 6px;
153
+ }
154
+ .message {
155
+ margin-bottom: 20px;
156
+ padding: 14px;
157
+ border-radius: 8px;
158
+ font-size: 16px;
159
+ line-height: 1.6;
160
+ position: relative;
161
+ max-width: 80%;
162
+ animation: fade-in 0.3s ease-out;
163
+ }
164
+ @keyframes fade-in {
165
+ from {
166
+ opacity: 0;
167
+ transform: translateY(10px);
168
+ }
169
+ to {
170
+ opacity: 1;
171
+ transform: translateY(0);
172
+ }
173
+ }
174
+ .message.user {
175
+ background: linear-gradient(135deg, #2c3e50, #34495e);
176
+ margin-left: auto;
177
+ border-bottom-right-radius: 2px;
178
+ }
179
+ .message.assistant {
180
+ background: linear-gradient(135deg, var(--secondary-color), var(--primary-color));
181
+ margin-right: auto;
182
+ border-bottom-left-radius: 2px;
183
+ }
184
+ .message.search-result {
185
+ background: linear-gradient(135deg, #1a5a3e, #2e7d32);
186
+ font-size: 14px;
187
+ padding: 10px;
188
+ margin-bottom: 10px;
189
+ }
190
+ .controls {
191
+ text-align: center;
192
+ margin-top: 20px;
193
+ display: flex;
194
+ justify-content: center;
195
+ flex-shrink: 0;
196
+ }
197
+ button {
198
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
199
+ color: white;
200
+ border: none;
201
+ padding: 14px 28px;
202
+ font-family: inherit;
203
+ font-size: 16px;
204
+ cursor: pointer;
205
+ transition: all 0.3s;
206
+ text-transform: uppercase;
207
+ letter-spacing: 1px;
208
+ border-radius: 50px;
209
+ display: flex;
210
+ align-items: center;
211
+ justify-content: center;
212
+ gap: 10px;
213
+ box-shadow: 0 4px 10px rgba(111, 66, 193, 0.3);
214
+ }
215
+ button:hover {
216
+ transform: translateY(-2px);
217
+ box-shadow: 0 6px 15px rgba(111, 66, 193, 0.5);
218
+ background: linear-gradient(135deg, var(--hover-color), var(--primary-color));
219
+ }
220
+ button:active {
221
+ transform: translateY(1px);
222
+ }
223
+ #audio-output {
224
+ display: none;
225
+ }
226
+ .icon-with-spinner {
227
+ display: flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ gap: 12px;
231
+ min-width: 180px;
232
+ }
233
+ .spinner {
234
+ width: 20px;
235
+ height: 20px;
236
+ border: 2px solid #ffffff;
237
+ border-top-color: transparent;
238
+ border-radius: 50%;
239
+ animation: spin 1s linear infinite;
240
+ flex-shrink: 0;
241
+ }
242
+ @keyframes spin {
243
+ to {
244
+ transform: rotate(360deg);
245
+ }
246
+ }
247
+ .audio-visualizer {
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: center;
251
+ gap: 5px;
252
+ min-width: 80px;
253
+ height: 25px;
254
+ }
255
+ .visualizer-bar {
256
+ width: 4px;
257
+ height: 100%;
258
+ background-color: rgba(255, 255, 255, 0.7);
259
+ border-radius: 2px;
260
+ transform-origin: bottom;
261
+ transform: scaleY(0.1);
262
+ transition: transform 0.1s ease;
263
+ }
264
+ .toast {
265
+ position: fixed;
266
+ top: 20px;
267
+ left: 50%;
268
+ transform: translateX(-50%);
269
+ padding: 16px 24px;
270
+ border-radius: 8px;
271
+ font-size: 14px;
272
+ z-index: 1000;
273
+ display: none;
274
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
275
+ }
276
+ .toast.error {
277
+ background-color: #f44336;
278
+ color: white;
279
+ }
280
+ .toast.warning {
281
+ background-color: #ff9800;
282
+ color: white;
283
+ }
284
+ .status-indicator {
285
+ display: inline-flex;
286
+ align-items: center;
287
+ margin-top: 10px;
288
+ font-size: 14px;
289
+ color: #aaa;
290
+ }
291
+ .status-dot {
292
+ width: 8px;
293
+ height: 8px;
294
+ border-radius: 50%;
295
+ margin-right: 8px;
296
+ }
297
+ .status-dot.connected {
298
+ background-color: #4caf50;
299
+ }
300
+ .status-dot.disconnected {
301
+ background-color: #f44336;
302
+ }
303
+ .status-dot.connecting {
304
+ background-color: #ff9800;
305
+ animation: pulse 1.5s infinite;
306
+ }
307
+ @keyframes pulse {
308
+ 0% {
309
+ opacity: 0.6;
310
+ }
311
+ 50% {
312
+ opacity: 1;
313
+ }
314
+ 100% {
315
+ opacity: 0.6;
316
+ }
317
+ }
318
+ .mouse-logo {
319
+ position: relative;
320
+ width: 40px;
321
+ height: 40px;
322
+ }
323
+ .mouse-ears {
324
+ position: absolute;
325
+ width: 15px;
326
+ height: 15px;
327
+ background-color: var(--primary-color);
328
+ border-radius: 50%;
329
+ }
330
+ .mouse-ear-left {
331
+ top: 0;
332
+ left: 5px;
333
+ }
334
+ .mouse-ear-right {
335
+ top: 0;
336
+ right: 5px;
337
+ }
338
+ .mouse-face {
339
+ position: absolute;
340
+ top: 10px;
341
+ left: 5px;
342
+ width: 30px;
343
+ height: 30px;
344
+ background-color: var(--secondary-color);
345
+ border-radius: 50%;
346
+ }
347
+ </style>
348
+ </head>
349
+
350
+ <body>
351
+ <div id="error-toast" class="toast"></div>
352
+ <div class="container">
353
+ <div class="header">
354
+ <div class="logo">
355
+ <div class="mouse-logo">
356
+ <div class="mouse-ears mouse-ear-left"></div>
357
+ <div class="mouse-ears mouse-ear-right"></div>
358
+ <div class="mouse-face"></div>
359
+ </div>
360
+ <h1>MOUSE 음성 챗</h1>
361
+ </div>
362
+ <div class="search-toggle">
363
+ <span class="search-label">웹 검색</span>
364
+ <div id="search-toggle" class="toggle-switch">
365
+ <div class="toggle-slider"></div>
366
+ </div>
367
+ </div>
368
+ <div class="status-indicator">
369
+ <div id="status-dot" class="status-dot disconnected"></div>
370
+ <span id="status-text">연결 대기 중</span>
371
+ </div>
372
+ </div>
373
+ <div class="chat-container">
374
+ <div class="chat-messages" id="chat-messages"></div>
375
+ </div>
376
+ <div class="controls">
377
+ <button id="start-button">대화 시작</button>
378
+ </div>
379
+ </div>
380
+ <audio id="audio-output"></audi