冯杨

对接本地Fay控制器的时候,进行修改调试

@@ -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>