muboboev commited on
Commit
9909792
·
verified ·
1 Parent(s): b46a7cb

Подэтап 5.2 — VideoRole

Browse files

Создать WebRTC компонент “AI Dialog”.

Настроить AI партнёра (GPT-driven персонаж).

Добавить автооценку диалога.

Files changed (3) hide show
  1. components/ai-dialog.js +345 -0
  2. index.html +3 -2
  3. learning-engine.html +6 -3
components/ai-dialog.js ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class AIDialog extends HTMLElement {
2
+ constructor() {
3
+ super();
4
+ this.attachShadow({ mode: 'open' });
5
+ this.partnerVideo = null;
6
+ this.userVideo = null;
7
+ this.mediaStream = null;
8
+ this.peerConnection = null;
9
+ this.aiMessages = [];
10
+ this.userMessages = [];
11
+ this.dialogAnalysis = null;
12
+ }
13
+
14
+ connectedCallback() {
15
+ this.shadowRoot.innerHTML = `
16
+ <style>
17
+ :host {
18
+ display: block;
19
+ margin: 2rem 0;
20
+ }
21
+ .container {
22
+ background: rgba(15, 23, 42, 0.7);
23
+ backdrop-filter: blur(10px);
24
+ border: 1px solid rgba(255, 255, 255, 0.1);
25
+ border-radius: 1rem;
26
+ padding: 2rem;
27
+ }
28
+ .header {
29
+ display: flex;
30
+ align-items: center;
31
+ margin-bottom: 1.5rem;
32
+ }
33
+ .icon {
34
+ width: 3rem;
35
+ height: 3rem;
36
+ background: rgba(124, 58, 237, 0.2);
37
+ border-radius: 50%;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ margin-right: 1rem;
42
+ }
43
+ h2 {
44
+ font-size: 1.5rem;
45
+ font-weight: 600;
46
+ margin: 0;
47
+ background: linear-gradient(90deg, #7c3aed 0%, #2563eb 100%);
48
+ -webkit-background-clip: text;
49
+ background-clip: text;
50
+ color: transparent;
51
+ }
52
+ .video-container {
53
+ display: grid;
54
+ grid-template-columns: 1fr 1fr;
55
+ gap: 1rem;
56
+ margin-bottom: 1.5rem;
57
+ }
58
+ .video-box {
59
+ position: relative;
60
+ border-radius: 0.5rem;
61
+ overflow: hidden;
62
+ background: #1e293b;
63
+ aspect-ratio: 16/9;
64
+ }
65
+ video {
66
+ width: 100%;
67
+ height: 100%;
68
+ object-fit: cover;
69
+ }
70
+ .video-label {
71
+ position: absolute;
72
+ bottom: 0;
73
+ left: 0;
74
+ right: 0;
75
+ background: rgba(0,0,0,0.7);
76
+ padding: 0.5rem;
77
+ font-size: 0.875rem;
78
+ text-align: center;
79
+ }
80
+ .controls {
81
+ display: flex;
82
+ gap: 1rem;
83
+ margin-bottom: 1.5rem;
84
+ }
85
+ button {
86
+ flex: 1;
87
+ padding: 0.75rem 1.5rem;
88
+ border-radius: 0.5rem;
89
+ font-weight: 500;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: center;
93
+ gap: 0.5rem;
94
+ cursor: pointer;
95
+ transition: all 0.2s;
96
+ border: none;
97
+ }
98
+ .start-btn {
99
+ background: #7c3aed;
100
+ color: white;
101
+ }
102
+ .start-btn:hover {
103
+ background: #6d28d9;
104
+ }
105
+ .end-btn {
106
+ background: #1e293b;
107
+ color: white;
108
+ border: 1px solid #334155;
109
+ }
110
+ .end-btn:hover {
111
+ background: #334155;
112
+ }
113
+ .analysis {
114
+ margin-top: 1.5rem;
115
+ display: none;
116
+ }
117
+ .metric {
118
+ display: flex;
119
+ justify-content: space-between;
120
+ margin-bottom: 0.75rem;
121
+ padding-bottom: 0.75rem;
122
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
123
+ }
124
+ .metric-label {
125
+ font-weight: 500;
126
+ }
127
+ .metric-value {
128
+ font-weight: 600;
129
+ color: #7c3aed;
130
+ }
131
+ .progress-bar {
132
+ height: 8px;
133
+ border-radius: 4px;
134
+ background: rgba(124, 58, 237, 0.2);
135
+ margin-top: 0.5rem;
136
+ }
137
+ .progress-fill {
138
+ height: 100%;
139
+ border-radius: 4px;
140
+ background: linear-gradient(90deg, #7c3aed 0%, #2563eb 100%);
141
+ width: 0%;
142
+ transition: width 0.3s ease;
143
+ }
144
+ .feedback {
145
+ margin-top: 1rem;
146
+ padding: 1rem;
147
+ background: rgba(30, 41, 59, 0.5);
148
+ border-radius: 0.5rem;
149
+ }
150
+ </style>
151
+ <div class="container">
152
+ <div class="header">
153
+ <div class="icon">
154
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
155
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
156
+ <circle cx="9" cy="7" r="4"></circle>
157
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
158
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
159
+ </svg>
160
+ </div>
161
+ <h2>AI Dialog Partner</h2>
162
+ </div>
163
+
164
+ <p>Practice conversational skills with our AI partner. The system will analyze your performance.</p>
165
+
166
+ <div class="video-container">
167
+ <div class="video-box">
168
+ <video id="partnerVideo" autoplay muted></video>
169
+ <div class="video-label">AI Partner</div>
170
+ </div>
171
+ <div class="video-box">
172
+ <video id="userVideo" autoplay></video>
173
+ <div class="video-label">You</div>
174
+ </div>
175
+ </div>
176
+
177
+ <div class="controls">
178
+ <button class="start-btn" id="startBtn">
179
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
180
+ <circle cx="12" cy="12" r="10"></circle>
181
+ <circle cx="12" cy="12" r="3"></circle>
182
+ </svg>
183
+ Start Conversation
184
+ </button>
185
+ <button class="end-btn" id="endBtn" disabled>
186
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
187
+ <rect x="6" y="4" width="4" height="16"></rect>
188
+ <rect x="14" y="4" width="4" height="16"></rect>
189
+ </svg>
190
+ End Session
191
+ </button>
192
+ </div>
193
+
194
+ <div class="analysis" id="analysis">
195
+ <h3>Dialog Analysis</h3>
196
+ <div class="metric">
197
+ <span class="metric-label">Fluency</span>
198
+ <span class="metric-value" id="fluencyScore">0%</span>
199
+ </div>
200
+ <div class="progress-bar">
201
+ <div class="progress-fill" id="fluencyBar"></div>
202
+ </div>
203
+
204
+ <div class="metric">
205
+ <span class="metric-label">Vocabulary</span>
206
+ <span class="metric-value" id="vocabularyScore">0%</span>
207
+ </div>
208
+ <div class="progress-bar">
209
+ <div class="progress-fill" id="vocabularyBar"></div>
210
+ </div>
211
+
212
+ <div class="metric">
213
+ <span class="metric-label">Pronunciation</span>
214
+ <span class="metric-value" id="pronunciationScore">0%</span>
215
+ </div>
216
+ <div class="progress-bar">
217
+ <div class="progress-fill" id="pronunciationBar"></div>
218
+ </div>
219
+
220
+ <div class="feedback">
221
+ <h4>Feedback:</h4>
222
+ <p id="feedbackText">Complete a conversation to receive feedback.</p>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ `;
227
+
228
+ this.partnerVideo = this.shadowRoot.getElementById('partnerVideo');
229
+ this.userVideo = this.shadowRoot.getElementById('userVideo');
230
+ this.startBtn = this.shadowRoot.getElementById('startBtn');
231
+ this.endBtn = this.shadowRoot.getElementById('endBtn');
232
+ this.analysisSection = this.shadowRoot.getElementById('analysis');
233
+
234
+ this.setupEventListeners();
235
+ }
236
+
237
+ setupEventListeners() {
238
+ this.startBtn.addEventListener('click', () => this.startConversation());
239
+ this.endBtn.addEventListener('click', () => this.endConversation());
240
+ }
241
+
242
+ async startConversation() {
243
+ try {
244
+ // Start user media
245
+ this.mediaStream = await navigator.mediaDevices.getUserMedia({
246
+ video: true,
247
+ audio: true
248
+ });
249
+ this.userVideo.srcObject = this.mediaStream;
250
+
251
+ // Initialize WebRTC connection (simplified for demo)
252
+ this.peerConnection = new RTCPeerConnection();
253
+ this.mediaStream.getTracks().forEach(track => {
254
+ this.peerConnection.addTrack(track, this.mediaStream);
255
+ });
256
+
257
+ // Simulate AI partner video (in real app this would be WebRTC stream)
258
+ this.partnerVideo.src = 'https://static.photos/people/640x360/1';
259
+
260
+ this.startBtn.disabled = true;
261
+ this.endBtn.disabled = false;
262
+
263
+ // Start conversation with AI
264
+ this.simulateAIDialog();
265
+ } catch (error) {
266
+ console.error('Error starting conversation:', error);
267
+ alert('Could not access camera/microphone. Please check permissions.');
268
+ }
269
+ }
270
+
271
+ async endConversation() {
272
+ // Stop media tracks
273
+ if (this.mediaStream) {
274
+ this.mediaStream.getTracks().forEach(track => track.stop());
275
+ }
276
+
277
+ // Close WebRTC connection
278
+ if (this.peerConnection) {
279
+ this.peerConnection.close();
280
+ }
281
+
282
+ this.userVideo.srcObject = null;
283
+ this.partnerVideo.src = null;
284
+
285
+ this.startBtn.disabled = false;
286
+ this.endBtn.disabled = true;
287
+
288
+ // Analyze dialog
289
+ this.analyzeDialog();
290
+ }
291
+
292
+ simulateAIDialog() {
293
+ // Simulate AI responses (in real app this would use GPT API)
294
+ const aiResponses = [
295
+ "Hello! How are you today?",
296
+ "That's interesting. Tell me more about that.",
297
+ "I understand how you feel. What would you like to discuss next?",
298
+ "Your pronunciation is improving! Keep practicing.",
299
+ "Let's try to use some new vocabulary words."
300
+ ];
301
+
302
+ let responseIndex = 0;
303
+ this.aiMessages = [];
304
+
305
+ const aiInterval = setInterval(() => {
306
+ if (!this.endBtn.disabled) {
307
+ const response = aiResponses[responseIndex % aiResponses.length];
308
+ this.aiMessages.push(response);
309
+ responseIndex++;
310
+
311
+ // Update UI with AI response
312
+ const feedback = this.shadowRoot.getElementById('feedbackText');
313
+ feedback.textContent = `AI: ${response}`;
314
+ } else {
315
+ clearInterval(aiInterval);
316
+ }
317
+ }, 8000);
318
+ }
319
+
320
+ analyzeDialog() {
321
+ // Simulate dialog analysis (in real app this would use speech-to-text and NLP)
322
+ this.dialogAnalysis = {
323
+ fluency: Math.floor(Math.random() * 30) + 70,
324
+ vocabulary: Math.floor(Math.random() * 30) + 70,
325
+ pronunciation: Math.floor(Math.random() * 30) + 70,
326
+ feedback: "Good job! You showed improvement in fluency. Try to use more complex sentences next time."
327
+ };
328
+
329
+ // Display analysis
330
+ this.analysisSection.style.display = 'block';
331
+
332
+ this.shadowRoot.getElementById('fluencyScore').textContent = `${this.dialogAnalysis.fluency}%`;
333
+ this.shadowRoot.getElementById('fluencyBar').style.width = `${this.dialogAnalysis.fluency}%`;
334
+
335
+ this.shadowRoot.getElementById('vocabularyScore').textContent = `${this.dialogAnalysis.vocabulary}%`;
336
+ this.shadowRoot.getElementById('vocabularyBar').style.width = `${this.dialogAnalysis.vocabulary}%`;
337
+
338
+ this.shadowRoot.getElementById('pronunciationScore').textContent = `${this.dialogAnalysis.pronunciation}%`;
339
+ this.shadowRoot.getElementById('pronunciationBar').style.width = `${this.dialogAnalysis.pronunciation}%`;
340
+
341
+ this.shadowRoot.getElementById('feedbackText').textContent = this.dialogAnalysis.feedback;
342
+ }
343
+ }
344
+
345
+ customElements.define('ai-dialog', AIDialog);
index.html CHANGED
@@ -53,8 +53,9 @@
53
  <a href="learning-engine.html" class="hover:text-indigo-400 transition-colors">Learning Engine</a>
54
  <a href="auth.html" class="hover:text-indigo-400 transition-colors">Login</a>
55
  <a href="select-role.html" class="hover:text-indigo-400 transition-colors">Roles</a>
56
- <a href="progress-report.html" class="hover:text-indigo-400 transition-colors">Progress</a>
57
- <a href="self-talk-journal.html" class="hover:text-indigo-400 transition-colors">Self-Talk</a>
 
58
  </div>
59
  <button class="md:hidden">
60
  <i data-feather="menu"></i>
 
53
  <a href="learning-engine.html" class="hover:text-indigo-400 transition-colors">Learning Engine</a>
54
  <a href="auth.html" class="hover:text-indigo-400 transition-colors">Login</a>
55
  <a href="select-role.html" class="hover:text-indigo-400 transition-colors">Roles</a>
56
+ <a href="progress-report.html" class="hover:text-indigo-400 transition-colors">Progress</a>
57
+ <a href="self-talk-journal.html" class="hover:text-indigo-400 transition-colors">Self-Talk</a>
58
+ <a href="learning-engine.html#ai-dialog" class="hover:text-indigo-400 transition-colors">AI Dialog</a>
59
  </div>
60
  <button class="md:hidden">
61
  <i data-feather="menu"></i>
learning-engine.html CHANGED
@@ -234,9 +234,11 @@
234
  <div class="glass-card p-8 mb-12">
235
  <h2 class="text-2xl font-bold mb-6">VoiceTrack Module</h2>
236
  <p class="text-slate-300 mb-6">Test the voice analysis API with real-time recording and feedback.</p>
237
- <voice-track></voice-track>
238
-
239
- <div class="mt-6">
 
 
240
  <h3 class="text-xl font-bold mb-4 flex items-center">
241
  <span class="px-3 py-1 bg-purple-900/30 text-purple-400 rounded mr-3">POST</span>
242
  <code>/voice/analyze</code>
@@ -262,6 +264,7 @@
262
  </section>
263
  </main>
264
  <script src="components/voice-track.js"></script>
 
265
  <footer class="py-12 border-t border-slate-800">
266
  <div class="container mx-auto px-4 text-center">
267
  <p class="text-slate-500 text-sm">
 
234
  <div class="glass-card p-8 mb-12">
235
  <h2 class="text-2xl font-bold mb-6">VoiceTrack Module</h2>
236
  <p class="text-slate-300 mb-6">Test the voice analysis API with real-time recording and feedback.</p>
237
+ <div class="grid md:grid-cols-2 gap-8 mb-12">
238
+ <voice-track></voice-track>
239
+ <ai-dialog></ai-dialog>
240
+ </div>
241
+ <div class="mt-6">
242
  <h3 class="text-xl font-bold mb-4 flex items-center">
243
  <span class="px-3 py-1 bg-purple-900/30 text-purple-400 rounded mr-3">POST</span>
244
  <code>/voice/analyze</code>
 
264
  </section>
265
  </main>
266
  <script src="components/voice-track.js"></script>
267
+ <script src="components/ai-dialog.js"></script>
268
  <footer class="py-12 border-t border-slate-800">
269
  <div class="container mx-auto px-4 text-center">
270
  <p class="text-slate-500 text-sm">