冯杨

对接本地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,9 +171,18 @@ async def humanaudio(request): @@ -167,9 +171,18 @@ 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()
  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()
173 186
174 nerfreals[sessionid].put_audio_file(filebytes) 187 nerfreals[sessionid].put_audio_file(filebytes)
175 188
@@ -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,30 +387,78 @@ @@ -389,30 +387,78 @@
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; 409 var host = window.location.hostname;
398 - ws = new WebSocket("ws://" + host + ":10002");  
399 - 410 + ws = new WebSocket("ws://127.0.0.1:10002");
400 ws.onopen = function() { 411 ws.onopen = function() {
401 console.log('Connected to WebSocket on port 10002'); 412 console.log('Connected to WebSocket on port 10002');
402 - reconnectAttempts = 0; 413 + reconnectAttempts = 0; // 重置重连次数
403 414
404 - var loginMessage = JSON.stringify({ "Username": "User" }); 415 + // 在连接成功后发送 {"Username": "User"}
  416 + var loginMessage = JSON.stringify({ "Username": $('#username').val() });
405 ws.send(loginMessage); 417 ws.send(loginMessage);
406 loginMessage = JSON.stringify({ "Output": "1" }); 418 loginMessage = JSON.stringify({ "Output": "1" });
407 ws.send(loginMessage); 419 ws.send(loginMessage);
408 }; 420 };
409 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 +
410 ws.onmessage = function(e) { 455 ws.onmessage = function(e) {
411 var messageData = JSON.parse(e.data); 456 var messageData = JSON.parse(e.data);
412 457
413 if (messageData.Data && messageData.Data.Key) { 458 if (messageData.Data && messageData.Data.Key) {
414 if(messageData.Data.Key == "audio"){ 459 if(messageData.Data.Key == "audio"){
415 var value = messageData.Data.HttpValue; 460 var value = messageData.Data.HttpValue;
  461 + console.log('Value:', value);
416 fetch('/humanaudio', { 462 fetch('/humanaudio', {
417 body: JSON.stringify({ 463 body: JSON.stringify({
418 file_url: value, 464 file_url: value,
@@ -423,6 +469,9 @@ @@ -423,6 +469,9 @@
423 }, 469 },
424 method: 'POST' 470 method: 'POST'
425 }); 471 });
  472 + }else if (messageData.Data.Key == "text") {
  473 + var reply = messageData.Data.Value;
  474 + addMessage(reply, "left");
426 } 475 }
427 } 476 }
428 477
@@ -435,7 +484,7 @@ @@ -435,7 +484,7 @@
435 484
436 ws.onerror = function(e) { 485 ws.onerror = function(e) {
437 console.error('WebSocket error:', e); 486 console.error('WebSocket error:', e);
438 - ws.close(); 487 + ws.close(); // 关闭连接并尝试重连
439 }; 488 };
440 } 489 }
441 490
@@ -443,6 +492,9 @@ @@ -443,6 +492,9 @@
443 if (reconnectAttempts < maxReconnectAttempts) { 492 if (reconnectAttempts < maxReconnectAttempts) {
444 reconnectAttempts++; 493 reconnectAttempts++;
445 console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ')'); 494 console.log('Attempting to reconnect... (Attempt ' + reconnectAttempts + ')');
  495 + if(parseInt(document.getElementById('is_open').value) == 1){
  496 + stop()
  497 + }
446 setTimeout(function() { 498 setTimeout(function() {
447 connectWebSocket(); 499 connectWebSocket();
448 }, reconnectInterval); 500 }, reconnectInterval);
@@ -451,7 +503,7 @@ @@ -451,7 +503,7 @@
451 } 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>