Showing
3 changed files
with
134 additions
and
66 deletions
@@ -45,6 +45,8 @@ import asyncio | @@ -45,6 +45,8 @@ import asyncio | ||
45 | import torch | 45 | import torch |
46 | from typing import Dict | 46 | from typing import Dict |
47 | from logger import logger | 47 | from logger import logger |
48 | +from pydub import AudioSegment | ||
49 | +from io import BytesIO | ||
48 | 50 | ||
49 | 51 | ||
50 | app = Flask(__name__) | 52 | app = Flask(__name__) |
@@ -156,8 +158,10 @@ async def humanaudio(request): | @@ -156,8 +158,10 @@ async def humanaudio(request): | ||
156 | try: | 158 | try: |
157 | params = await request.json() | 159 | params = await request.json() |
158 | sessionid = int(params.get('sessionid', 0)) | 160 | sessionid = int(params.get('sessionid', 0)) |
159 | - fileobj = params['file_url'] | ||
160 | - if fileobj.startswith("http"): | 161 | + fileobj = params.get('file_url') |
162 | + | ||
163 | + # 获取音频文件数据 | ||
164 | + if isinstance(fileobj, str) and fileobj.startswith("http"): | ||
161 | async with aiohttp.ClientSession() as session: | 165 | async with aiohttp.ClientSession() as session: |
162 | async with session.get(fileobj) as response: | 166 | async with session.get(fileobj) as response: |
163 | if response.status == 200: | 167 | if response.status == 200: |
@@ -167,10 +171,19 @@ async def humanaudio(request): | @@ -167,10 +171,19 @@ async def humanaudio(request): | ||
167 | content_type="application/json", | 171 | content_type="application/json", |
168 | text=json.dumps({"code": -1, "msg": "Error downloading file"}) | 172 | text=json.dumps({"code": -1, "msg": "Error downloading file"}) |
169 | ) | 173 | ) |
174 | + # 根据 URL 后缀判断是否为 MP3 文件 | ||
175 | + is_mp3 = fileobj.lower().endswith('.mp3') | ||
170 | else: | 176 | else: |
171 | filename = fileobj.filename | 177 | filename = fileobj.filename |
172 | filebytes = fileobj.file.read() | 178 | filebytes = fileobj.file.read() |
173 | - | 179 | + is_mp3 = filename.lower().endswith('.mp3') |
180 | + | ||
181 | + if is_mp3: | ||
182 | + audio = AudioSegment.from_file(BytesIO(filebytes), format="mp3") | ||
183 | + out_io = BytesIO() | ||
184 | + audio.export(out_io, format="wav") | ||
185 | + filebytes = out_io.getvalue() | ||
186 | + | ||
174 | nerfreals[sessionid].put_audio_file(filebytes) | 187 | nerfreals[sessionid].put_audio_file(filebytes) |
175 | 188 | ||
176 | return web.Response( | 189 | return web.Response( |
@@ -8,15 +8,18 @@ def llm_response(message,nerfreal:BaseReal): | @@ -8,15 +8,18 @@ def llm_response(message,nerfreal:BaseReal): | ||
8 | from openai import OpenAI | 8 | from openai import OpenAI |
9 | client = OpenAI( | 9 | client = OpenAI( |
10 | # 如果您没有配置环境变量,请在此处用您的API Key进行替换 | 10 | # 如果您没有配置环境变量,请在此处用您的API Key进行替换 |
11 | - api_key=os.getenv("DASHSCOPE_API_KEY"), | 11 | + # api_key=os.getenv("DASHSCOPE_API_KEY"), |
12 | + api_key = "localkey", | ||
13 | + base_url="http://127.0.0.1:5000/v1" | ||
12 | # 填写DashScope SDK的base_url | 14 | # 填写DashScope SDK的base_url |
13 | - base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", | 15 | + # base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", |
14 | ) | 16 | ) |
15 | end = time.perf_counter() | 17 | end = time.perf_counter() |
16 | logger.info(f"llm Time init: {end-start}s") | 18 | logger.info(f"llm Time init: {end-start}s") |
17 | completion = client.chat.completions.create( | 19 | completion = client.chat.completions.create( |
18 | - model="qwen-plus", | ||
19 | - messages=[{'role': 'system', 'content': 'You are a helpful assistant.'}, | 20 | + model="fay-streaming", |
21 | + # model="qwen-plus", | ||
22 | + messages=[{'role': 'system', 'content': '你是小艺,是由艺云展陈开发的AI语音聊天机器人,回答风格精简。'}, | ||
20 | {'role': 'user', 'content': message}], | 23 | {'role': 'user', 'content': message}], |
21 | stream=True, | 24 | stream=True, |
22 | # 通过以下设置,在流式输出的最后一行展示token使用信息 | 25 | # 通过以下设置,在流式输出的最后一行展示token使用信息 |
@@ -267,6 +267,7 @@ | @@ -267,6 +267,7 @@ | ||
267 | 267 | ||
268 | <!-- 主内容区域 --> | 268 | <!-- 主内容区域 --> |
269 | <div id="main-content"> | 269 | <div id="main-content"> |
270 | + <input type="hidden" id="username" value="User"> | ||
270 | <div id="media"> | 271 | <div id="media"> |
271 | <h2>艺云展陈</h2> | 272 | <h2>艺云展陈</h2> |
272 | <audio id="audio" autoplay="true"></audio> | 273 | <audio id="audio" autoplay="true"></audio> |
@@ -278,10 +279,7 @@ | @@ -278,10 +279,7 @@ | ||
278 | <script type="text/javascript" src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script> | 279 | <script type="text/javascript" src="http://cdn.sockjs.org/sockjs-0.3.4.js"></script> |
279 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> | 280 | <script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script> |
280 | <script type="text/javascript" charset="utf-8"> | 281 | <script type="text/javascript" charset="utf-8"> |
281 | - var ws; | ||
282 | - var reconnectInterval = 5000; | ||
283 | - var maxReconnectAttempts = 10; | ||
284 | - var reconnectAttempts = 0; | 282 | + |
285 | $(document).ready(function() { | 283 | $(document).ready(function() { |
286 | // 侧边栏切换功能 | 284 | // 侧边栏切换功能 |
287 | $('#sidebar-toggle').click(function() { | 285 | $('#sidebar-toggle').click(function() { |
@@ -389,69 +387,123 @@ | @@ -389,69 +387,123 @@ | ||
389 | 387 | ||
390 | // WebSocket connection to Fay digital avatar (port 10002) | 388 | // WebSocket connection to Fay digital avatar (port 10002) |
391 | var ws; | 389 | var ws; |
392 | - var reconnectInterval = 5000; | ||
393 | - var maxReconnectAttempts = 10; | 390 | + var reconnectInterval = 5000; // 每5秒尝试重连一次 |
391 | + var maxReconnectAttempts = 10; // 最大重连次数 | ||
394 | var reconnectAttempts = 0; | 392 | var reconnectAttempts = 0; |
395 | 393 | ||
394 | + function generateUsername() { | ||
395 | + var username = 'User' + Math.floor(Math.random() * 10000); | ||
396 | + return username; | ||
397 | + } | ||
398 | + | ||
399 | + function setUsername() { | ||
400 | + var storedUsername = localStorage.getItem('username'); | ||
401 | + if (!storedUsername) { | ||
402 | + storedUsername = generateUsername(); | ||
403 | + localStorage.setItem('username', storedUsername); | ||
404 | + } | ||
405 | + $('#username').val(storedUsername); // Use the username as the session ID | ||
406 | + } | ||
407 | + setUsername(); | ||
396 | function connectWebSocket() { | 408 | function connectWebSocket() { |
397 | - var host = window.location.hostname; | ||
398 | - ws = new WebSocket("ws://" + host + ":10002"); | ||
399 | - | ||
400 | - ws.onopen = function() { | ||
401 | - console.log('Connected to WebSocket on port 10002'); | ||
402 | - reconnectAttempts = 0; | ||
403 | - | ||
404 | - var loginMessage = JSON.stringify({ "Username": "User" }); | ||
405 | - ws.send(loginMessage); | ||
406 | - loginMessage = JSON.stringify({ "Output": "1" }); | ||
407 | - ws.send(loginMessage); | ||
408 | - }; | ||
409 | - | ||
410 | - ws.onmessage = function(e) { | ||
411 | - var messageData = JSON.parse(e.data); | ||
412 | - | ||
413 | - if (messageData.Data && messageData.Data.Key) { | ||
414 | - if(messageData.Data.Key == "audio"){ | ||
415 | - var value = messageData.Data.HttpValue; | ||
416 | - fetch('/humanaudio', { | ||
417 | - body: JSON.stringify({ | ||
418 | - file_url: value, | ||
419 | - sessionid:parseInt(document.getElementById('sessionid').value), | ||
420 | - }), | ||
421 | - headers: { | ||
422 | - 'Content-Type': 'application/json' | ||
423 | - }, | ||
424 | - method: 'POST' | ||
425 | - }); | ||
426 | - } | ||
427 | - } | ||
428 | - | ||
429 | - }; | ||
430 | - | ||
431 | - ws.onclose = function(e) { | ||
432 | - console.log('WebSocket connection closed'); | ||
433 | - attemptReconnect(); | ||
434 | - }; | ||
435 | - | ||
436 | - ws.onerror = function(e) { | ||
437 | - console.error('WebSocket error:', e); | ||
438 | - ws.close(); | ||
439 | - }; | 409 | + var host = window.location.hostname; |
410 | + ws = new WebSocket("ws://127.0.0.1:10002"); | ||
411 | + ws.onopen = function() { | ||
412 | + console.log('Connected to WebSocket on port 10002'); | ||
413 | + reconnectAttempts = 0; // 重置重连次数 | ||
414 | + | ||
415 | + // 在连接成功后发送 {"Username": "User"} | ||
416 | + var loginMessage = JSON.stringify({ "Username": $('#username').val() }); | ||
417 | + ws.send(loginMessage); | ||
418 | + loginMessage = JSON.stringify({ "Output": "1" }); | ||
419 | + ws.send(loginMessage); | ||
420 | + }; | ||
421 | + | ||
422 | + | ||
423 | + function addMessage(text, type = "right") { | ||
424 | + const chatOverlay = document.getElementById("chatOverlay"); | ||
425 | + const messageDiv = document.createElement("div"); | ||
426 | + messageDiv.classList.add("message", type); | ||
427 | + | ||
428 | + const avatar = document.createElement("img"); | ||
429 | + avatar.classList.add("avatar"); | ||
430 | + avatar.src = type === "right" ? "images/avatar-right.png" : "images/avatar-left.png"; | ||
431 | + | ||
432 | + const textContainer = document.createElement("div"); | ||
433 | + textContainer.classList.add("text-container"); | ||
434 | + | ||
435 | + const textElement = document.createElement("div"); | ||
436 | + textElement.classList.add("text"); | ||
437 | + textElement.textContent = text; | ||
438 | + | ||
439 | + const timeElement = document.createElement("div"); | ||
440 | + timeElement.classList.add("time"); | ||
441 | + timeElement.textContent = new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); | ||
442 | + | ||
443 | + textContainer.appendChild(textElement); | ||
444 | + textContainer.appendChild(timeElement); | ||
445 | + | ||
446 | + messageDiv.appendChild(avatar); | ||
447 | + messageDiv.appendChild(textContainer); | ||
448 | + | ||
449 | + chatOverlay.appendChild(messageDiv); | ||
450 | + | ||
451 | + // 自动滚动到底部 | ||
452 | + chatOverlay.scrollTop = chatOverlay.scrollHeight; | ||
453 | + } | ||
454 | + | ||
455 | + ws.onmessage = function(e) { | ||
456 | + var messageData = JSON.parse(e.data); | ||
457 | + | ||
458 | + if (messageData.Data && messageData.Data.Key) { | ||
459 | + if(messageData.Data.Key == "audio"){ | ||
460 | + var value = messageData.Data.HttpValue; | ||
461 | + console.log('Value:', value); | ||
462 | + fetch('/humanaudio', { | ||
463 | + body: JSON.stringify({ | ||
464 | + file_url: value, | ||
465 | + sessionid:parseInt(document.getElementById('sessionid').value), | ||
466 | + }), | ||
467 | + headers: { | ||
468 | + 'Content-Type': 'application/json' | ||
469 | + }, | ||
470 | + method: 'POST' | ||
471 | + }); | ||
472 | + }else if (messageData.Data.Key == "text") { | ||
473 | + var reply = messageData.Data.Value; | ||
474 | + addMessage(reply, "left"); | ||
475 | + } | ||
476 | + } | ||
477 | + | ||
478 | + }; | ||
479 | + | ||
480 | + ws.onclose = function(e) { | ||
481 | + console.log('WebSocket connection closed'); | ||
482 | + attemptReconnect(); | ||
483 | + }; | ||
484 | + | ||
485 | + ws.onerror = function(e) { | ||
486 | + console.error('WebSocket error:', e); | ||
487 | + ws.close(); // 关闭连接并尝试重连 | ||
488 | + }; | ||
440 | } | 489 | } |
441 | 490 | ||
442 | function attemptReconnect() { | 491 | function attemptReconnect() { |
443 | - if (reconnectAttempts < maxReconnectAttempts) { | ||
444 | - reconnectAttempts++; | ||
445 | - console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ')'); | ||
446 | - setTimeout(function() { | ||
447 | - connectWebSocket(); | ||
448 | - }, reconnectInterval); | ||
449 | - } else { | ||
450 | - console.error('Maximum reconnection attempts reached. Could not reconnect to WebSocket.'); | ||
451 | - } | 492 | + if (reconnectAttempts < maxReconnectAttempts) { |
493 | + reconnectAttempts++; | ||
494 | + console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ')'); | ||
495 | + if(parseInt(document.getElementById('is_open').value) == 1){ | ||
496 | + stop() | ||
497 | + } | ||
498 | + setTimeout(function() { | ||
499 | + connectWebSocket(); | ||
500 | + }, reconnectInterval); | ||
501 | + } else { | ||
502 | + console.error('Maximum reconnection attempts reached. Could not reconnect to WebSocket.'); | ||
503 | + } | ||
452 | } | 504 | } |
453 | 505 | ||
454 | - // Initialize WebSocket connection when document is ready | 506 | + // 初始化 WebSocket 连接 |
455 | connectWebSocket(); | 507 | connectWebSocket(); |
456 | }); | 508 | }); |
457 | </script> | 509 | </script> |
-
Please register or login to post a comment