Merge branch 'main' of https://github.com/xszyou/Fay
This commit is contained in:
commit
f871b6a532
@ -319,6 +319,9 @@ class FeiFei:
|
||||
if audio_url is not None:
|
||||
file_name = 'sample-' + str(int(time.time() * 1000)) + '.wav'
|
||||
result = self.download_wav(audio_url, './samples/', file_name)
|
||||
|
||||
elif not wsa_server.get_instance().get_client_output(interact.data.get('user')):
|
||||
result = None
|
||||
elif config_util.config["interact"]["playSound"] or wsa_server.get_instance().is_connected(interact.data.get("user")) or self.__is_send_remote_device_audio(interact):#tts
|
||||
util.printInfo(1, interact.data.get('user'), '合成音频...')
|
||||
tm = time.time()
|
||||
|
@ -3,6 +3,7 @@ import websockets
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import sys
|
||||
|
||||
__wss = None
|
||||
|
||||
@ -14,104 +15,172 @@ def new_instance():
|
||||
|
||||
class SocketBridgeService:
|
||||
def __init__(self):
|
||||
self.websockets = {}
|
||||
self.sockets = {}
|
||||
self.message_queue = asyncio.Queue()
|
||||
self.websockets = {}
|
||||
self.sockets = {}
|
||||
self.message_queue = asyncio.Queue()
|
||||
self.running = True
|
||||
self.loop = None
|
||||
self.tasks = set()
|
||||
self.server = None
|
||||
self.event_loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.event_loop)
|
||||
|
||||
async def handler(self, websocket, path):
|
||||
ws_id = id(websocket)
|
||||
self.websockets[ws_id] = websocket
|
||||
try:
|
||||
if ws_id not in self.sockets:
|
||||
self.sockets[ws_id] = await self.create_socket_client()
|
||||
asyncio.create_task(self.receive_from_socket(ws_id))
|
||||
sock = await self.create_socket_client()
|
||||
if sock:
|
||||
self.sockets[ws_id] = sock
|
||||
else:
|
||||
print(f"Failed to connect TCP socket for WebSocket {ws_id}")
|
||||
await websocket.close()
|
||||
return
|
||||
receive_task = asyncio.create_task(self.receive_from_socket(ws_id))
|
||||
self.tasks.add(receive_task)
|
||||
receive_task.add_done_callback(self.tasks.discard)
|
||||
async for message in websocket:
|
||||
await self.send_to_socket(ws_id, message)
|
||||
except websockets.ConnectionClosed:
|
||||
pass
|
||||
except Exception as e:
|
||||
pass
|
||||
finally:
|
||||
self.close_socket_client(ws_id)
|
||||
self.websockets.pop(ws_id, None)
|
||||
|
||||
async def create_socket_client(self):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect(('127.0.0.1', 10001))
|
||||
return sock
|
||||
try:
|
||||
sock.connect(('127.0.0.1', 10001))
|
||||
sock.setblocking(True) # 设置为阻塞模式
|
||||
return sock
|
||||
except Exception as e:
|
||||
return None
|
||||
|
||||
async def send_to_socket(self, ws_id, message):
|
||||
sock = self.sockets.get(ws_id)
|
||||
if sock:
|
||||
asyncio.create_task(self.socket_send(sock, message))
|
||||
|
||||
async def socket_send(self, sock, message):
|
||||
await asyncio.to_thread(sock.sendall, message)
|
||||
try:
|
||||
await asyncio.to_thread(sock.sendall, message)
|
||||
except Exception as e:
|
||||
self.close_socket_client(ws_id)
|
||||
|
||||
async def receive_from_socket(self, ws_id):
|
||||
sock = self.sockets.get(ws_id)
|
||||
while True:
|
||||
data = await asyncio.to_thread(sock.recv, 1024)
|
||||
if data:
|
||||
await self.message_queue.put((ws_id, data))
|
||||
if not sock:
|
||||
return
|
||||
try:
|
||||
while self.running:
|
||||
data = await asyncio.to_thread(sock.recv, 4096)
|
||||
if data:
|
||||
await self.message_queue.put((ws_id, data))
|
||||
else:
|
||||
break
|
||||
except Exception as e:
|
||||
pass
|
||||
finally:
|
||||
self.close_socket_client(ws_id)
|
||||
|
||||
async def process_message_queue(self):
|
||||
while True:
|
||||
if not self.running:
|
||||
break
|
||||
ws_id, data = await self.message_queue.get()
|
||||
websocket = self.websockets.get(ws_id)
|
||||
if websocket.open:
|
||||
await websocket.send(data)
|
||||
self.message_queue.task_done()
|
||||
while self.running or not self.message_queue.empty():
|
||||
try:
|
||||
ws_id, data = await asyncio.wait_for(self.message_queue.get(), timeout=1.0)
|
||||
websocket = self.websockets.get(ws_id)
|
||||
if websocket and websocket.open:
|
||||
try:
|
||||
await websocket.send(data)
|
||||
except Exception as e:
|
||||
pass
|
||||
self.message_queue.task_done()
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def close_socket_client(self, ws_id):
|
||||
sock = self.sockets.pop(ws_id, None)
|
||||
if sock:
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except Exception as e:
|
||||
pass
|
||||
# print(f"Error shutting down socket for WebSocket {ws_id}: {e}", file=sys.stderr)
|
||||
sock.close()
|
||||
|
||||
async def start(self, host='0.0.0.0', port=9001):
|
||||
self.server = await websockets.serve(self.handler, host, port, loop=self.event_loop)
|
||||
asyncio.create_task(self.process_message_queue())
|
||||
await asyncio.Future()
|
||||
|
||||
async def shutdown(self):
|
||||
self.running = False
|
||||
if self.server:
|
||||
for ws in self.websockets.values():
|
||||
await ws.close()
|
||||
if hasattr(self.server, 'close'):
|
||||
self.server.close()
|
||||
await asyncio.gather(*[w.wait_closed() for w in self.websockets.values()])
|
||||
for sock in self.sockets.values():
|
||||
sock.close()
|
||||
if self.server:
|
||||
await self.server.wait_closed()
|
||||
|
||||
def stop_server(self):
|
||||
self.event_loop.call_soon_threadsafe(self.shutdown)
|
||||
self.event_loop.run_until_complete(self.shutdown())
|
||||
self.event_loop.close()
|
||||
|
||||
def start_service(self):
|
||||
self.event_loop.run_until_complete(self.start(host='0.0.0.0', port=9001))
|
||||
self.server = await websockets.serve(self.handler, host, port)
|
||||
process_task = asyncio.create_task(self.process_message_queue())
|
||||
self.tasks.add(process_task)
|
||||
process_task.add_done_callback(self.tasks.discard)
|
||||
try:
|
||||
self.event_loop.run_forever()
|
||||
except KeyboardInterrupt:
|
||||
await self.server.wait_closed()
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
finally:
|
||||
self.stop_server()
|
||||
await self.shutdown()
|
||||
|
||||
async def shutdown(self):
|
||||
if not self.running:
|
||||
return
|
||||
self.running = False
|
||||
|
||||
for ws_id, ws in list(self.websockets.items()):
|
||||
try:
|
||||
await ws.close()
|
||||
except Exception as e:
|
||||
pass
|
||||
# print(f"Error closing WebSocket {ws_id}: {e}", file=sys.stderr)
|
||||
self.websockets.clear()
|
||||
|
||||
for ws_id, sock in list(self.sockets.items()):
|
||||
try:
|
||||
sock.shutdown(socket.SHUT_RDWR)
|
||||
except Exception as e:
|
||||
pass
|
||||
# print(f"Error shutting down socket for WebSocket {ws_id}: {e}", file=sys.stderr)
|
||||
sock.close()
|
||||
self.sockets.clear()
|
||||
|
||||
await self.message_queue.join()
|
||||
|
||||
for task in self.tasks:
|
||||
task.cancel()
|
||||
await asyncio.gather(*self.tasks, return_exceptions=True)
|
||||
self.tasks.clear()
|
||||
|
||||
if self.server:
|
||||
self.server.close()
|
||||
await self.server.wait_closed()
|
||||
|
||||
|
||||
def start_service(self):
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
try:
|
||||
self.loop.run_until_complete(self.start(host='0.0.0.0', port=9001))
|
||||
except Exception as e:
|
||||
pass
|
||||
# print(f"Service exception: {e}", file=sys.stderr)
|
||||
finally:
|
||||
self.loop.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
service = new_instance()
|
||||
service_thread = threading.Thread(target=service.start_service)
|
||||
service_thread = threading.Thread(target=service.start_service, daemon=True)
|
||||
service_thread.start()
|
||||
|
||||
# 等待一些时间或者直到收到停止信号
|
||||
try:
|
||||
while service.running:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
service.stop_server()
|
||||
service_thread.join()
|
||||
# 在服务的事件循环中运行 shutdown 协程
|
||||
print("Initiating shutdown...")
|
||||
if service.loop and service.loop.is_running():
|
||||
future = asyncio.run_coroutine_threadsafe(service.shutdown(), service.loop)
|
||||
try:
|
||||
future.result() # 等待关闭完成
|
||||
print("Shutdown coroutine completed.")
|
||||
except Exception as e:
|
||||
print(f"Shutdown exception: {e}", file=sys.stderr)
|
||||
service_thread.join()
|
||||
print("Service has been shut down.")
|
||||
|
@ -31,21 +31,36 @@ class MyServer:
|
||||
async for message in websocket:
|
||||
await asyncio.sleep(0.01)
|
||||
try:
|
||||
username = json.loads(message).get("Username")
|
||||
except json.JSONDecodeError as e:#json格式有误,不处理
|
||||
pass
|
||||
if username:
|
||||
data = json.loads(message)
|
||||
username = data.get("Username")
|
||||
output_setting = data.get("Output")
|
||||
except json.JSONDecodeError:
|
||||
pass # Ignore invalid JSON messages
|
||||
if username or output_setting:
|
||||
remote_address = websocket.remote_address
|
||||
unique_id = f"{remote_address[0]}:{remote_address[1]}"
|
||||
async with self.lock:
|
||||
for i in range(len(self.__clients)):
|
||||
for i in range(len(self.__clients)):
|
||||
if self.__clients[i]["id"] == unique_id:
|
||||
self.__clients[i]["username"] = username
|
||||
if username:
|
||||
self.__clients[i]["username"] = username
|
||||
if output_setting:
|
||||
self.__clients[i]["output"] = output_setting
|
||||
await self.__consumer(message)
|
||||
except websockets.exceptions.ConnectionClosedError as e:
|
||||
# 从客户端列表中移除已断开的连接
|
||||
await self.remove_client(websocket)
|
||||
util.printInfo(1, "User" if username is None else username, f"WebSocket 连接关闭: {e}")
|
||||
util.printInfo(1, "User" if username is None else username, f"WebSocket 连接关闭: {e}")
|
||||
|
||||
def get_client_output(self, username):
|
||||
clients_with_username = [c for c in self.__clients if c.get("username") == username]
|
||||
if not clients_with_username:
|
||||
return False
|
||||
for client in clients_with_username:
|
||||
output = client.get("output", 1)
|
||||
if output != 0 and output != '0':
|
||||
return True
|
||||
return False
|
||||
|
||||
# 发送处理
|
||||
async def __producer_handler(self, websocket, path):
|
||||
|
@ -3,7 +3,6 @@ import time
|
||||
import re
|
||||
import pyaudio
|
||||
import socket
|
||||
import psutil
|
||||
import sys
|
||||
import asyncio
|
||||
import requests
|
||||
@ -13,7 +12,6 @@ from core import fay_core
|
||||
from scheduler.thread_manager import MyThread
|
||||
from utils import util, config_util, stream_util
|
||||
from core.wsa_server import MyServer
|
||||
from scheduler.thread_manager import MyThread
|
||||
from core import wsa_server
|
||||
from core import socket_bridge_service
|
||||
|
||||
@ -222,15 +220,6 @@ def accept_audio_device_output_connect():
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
def kill_process_by_port(port):
|
||||
for proc in psutil.process_iter(['pid', 'name','cmdline']):
|
||||
try:
|
||||
for conn in proc.connections(kind='inet'):
|
||||
if conn.laddr.port == port:
|
||||
proc.terminate()
|
||||
proc.wait()
|
||||
except(psutil.NosuchProcess, psutil.AccessDenied):
|
||||
pass
|
||||
#数字人端请求获取最新的自动播放消息,若自动播放服务关闭会自动退出自动播放
|
||||
def start_auto_play_service(): #TODO 评估一下有无优化的空间
|
||||
url = f"{config_util.config['source']['automatic_player_url']}/get_auto_play_item"
|
||||
@ -271,55 +260,7 @@ def start_auto_play_service(): #TODO 评估一下有无优化的空间
|
||||
util.printInfo(1, user, '请求自动播放服务器失败,错误信息是:{}'.format(e))
|
||||
time.sleep(0.01)
|
||||
|
||||
#控制台输入监听
|
||||
def console_listener():
|
||||
global feiFei
|
||||
while __running:
|
||||
try:
|
||||
text = input()
|
||||
except EOFError:
|
||||
util.log(1, "控制台已经关闭")
|
||||
break
|
||||
|
||||
args = text.split(' ')
|
||||
|
||||
if len(args) == 0 or len(args[0]) == 0:
|
||||
continue
|
||||
|
||||
if args[0] == 'help':
|
||||
util.log(1, 'in <msg> \t通过控制台交互')
|
||||
util.log(1, 'restart \t重启服务')
|
||||
util.log(1, 'stop \t\t关闭服务')
|
||||
util.log(1, 'exit \t\t结束程序')
|
||||
|
||||
elif args[0] == 'stop':
|
||||
stop()
|
||||
break
|
||||
|
||||
elif args[0] == 'restart':
|
||||
stop()
|
||||
time.sleep(0.1)
|
||||
start()
|
||||
|
||||
elif args[0] == 'in':
|
||||
if len(args) == 1:
|
||||
util.log(1, '错误的参数!')
|
||||
msg = text[3:len(text)]
|
||||
util.printInfo(3, "控制台", '{}: {}'.format('控制台', msg))
|
||||
interact = Interact("console", 1, {'user': 'User', 'msg': msg})
|
||||
thr = MyThread(target=feiFei.on_interact, args=[interact])
|
||||
thr.start()
|
||||
|
||||
elif args[0]=='exit':
|
||||
stop()
|
||||
time.sleep(0.1)
|
||||
util.log(1,'程序正在退出..')
|
||||
ports =[10001,10002,10003,5000]
|
||||
for port in ports:
|
||||
kill_process_by_port(port)
|
||||
sys.exit(0)
|
||||
else:
|
||||
util.log(1, '未知命令!使用 \'help\' 获取帮助.')
|
||||
|
||||
#停止服务
|
||||
def stop():
|
||||
@ -329,6 +270,7 @@ def stop():
|
||||
global DeviceInputListenerDict
|
||||
global ngrok
|
||||
global socket_service_instance
|
||||
global deviceSocketServer
|
||||
|
||||
util.log(1, '正在关闭服务...')
|
||||
__running = False
|
||||
@ -337,14 +279,17 @@ def stop():
|
||||
recorderListener.stop()
|
||||
time.sleep(0.1)
|
||||
util.log(1, '正在关闭远程音频输入输出服务...')
|
||||
if len(DeviceInputListenerDict) > 0:
|
||||
for key in list(DeviceInputListenerDict.keys()):
|
||||
value = DeviceInputListenerDict.pop(key)
|
||||
value.stop()
|
||||
deviceSocketServer.close()
|
||||
if socket_service_instance is not None:
|
||||
future = asyncio.run_coroutine_threadsafe(socket_service_instance.shutdown(), socket_service_instance.loop)
|
||||
future.result()
|
||||
try:
|
||||
if len(DeviceInputListenerDict) > 0:
|
||||
for key in list(DeviceInputListenerDict.keys()):
|
||||
value = DeviceInputListenerDict.pop(key)
|
||||
value.stop()
|
||||
deviceSocketServer.close()
|
||||
if socket_service_instance is not None:
|
||||
socket_service_instance.stop_server()
|
||||
socket_service_instance = None
|
||||
except:
|
||||
pass
|
||||
util.log(1, '正在关闭核心服务...')
|
||||
feiFei.stop()
|
||||
util.log(1, '服务已关闭!')
|
||||
@ -395,11 +340,7 @@ def start():
|
||||
#启动自动播放服务
|
||||
util.log(1,'启动自动播放服务...')
|
||||
MyThread(target=start_auto_play_service).start()
|
||||
|
||||
#监听控制台
|
||||
util.log(1, '注册命令...')
|
||||
MyThread(target=console_listener).start() # 监听控制台
|
||||
|
||||
|
||||
util.log(1, '服务启动完成!')
|
||||
util.log(1, '使用 \'help\' 获取帮助.')
|
||||
|
||||
|
@ -29,9 +29,13 @@ auth = HTTPBasicAuth()
|
||||
CORS(__app, supports_credentials=True)
|
||||
|
||||
def load_users():
|
||||
with open('verifier.json') as f:
|
||||
users = json.load(f)
|
||||
return users
|
||||
try:
|
||||
with open('verifier.json') as f:
|
||||
users = json.load(f)
|
||||
return users
|
||||
except Exception as e:
|
||||
print(f"Error loading users: {e}")
|
||||
return {}
|
||||
|
||||
users = load_users()
|
||||
|
||||
@ -42,243 +46,302 @@ def verify_password(username, password):
|
||||
if username in users and users[username] == password:
|
||||
return username
|
||||
|
||||
|
||||
def __get_template():
|
||||
return render_template('index.html')
|
||||
|
||||
try:
|
||||
return render_template('index.html')
|
||||
except Exception as e:
|
||||
return f"Error rendering template: {e}", 500
|
||||
|
||||
def __get_device_list():
|
||||
if config_util.start_mode == 'common':
|
||||
audio = pyaudio.PyAudio()
|
||||
device_list = []
|
||||
for i in range(audio.get_device_count()):
|
||||
devInfo = audio.get_device_info_by_index(i)
|
||||
if devInfo['hostApi'] == 0:
|
||||
device_list.append(devInfo["name"])
|
||||
|
||||
return list(set(device_list))
|
||||
else:
|
||||
try:
|
||||
if config_util.start_mode == 'common':
|
||||
audio = pyaudio.PyAudio()
|
||||
device_list = []
|
||||
for i in range(audio.get_device_count()):
|
||||
devInfo = audio.get_device_info_by_index(i)
|
||||
if devInfo['hostApi'] == 0:
|
||||
device_list.append(devInfo["name"])
|
||||
return list(set(device_list))
|
||||
else:
|
||||
return []
|
||||
except Exception as e:
|
||||
print(f"Error getting device list: {e}")
|
||||
return []
|
||||
|
||||
|
||||
@__app.route('/api/submit', methods=['post'])
|
||||
def api_submit():
|
||||
data = request.values.get('data')
|
||||
config_data = json.loads(data)
|
||||
if(config_data['config']['source']['record']['enabled']):
|
||||
config_data['config']['source']['record']['channels'] = 0
|
||||
audio = pyaudio.PyAudio()
|
||||
for i in range(audio.get_device_count()):
|
||||
devInfo = audio.get_device_info_by_index(i)
|
||||
if devInfo['name'].find(config_data['config']['source']['record']['device']) >= 0 and devInfo['hostApi'] == 0:
|
||||
config_data['config']['source']['record']['channels'] = devInfo['maxInputChannels']
|
||||
if not data:
|
||||
return jsonify({'result': 'error', 'message': '未提供数据'})
|
||||
try:
|
||||
config_data = json.loads(data)
|
||||
if 'config' not in config_data:
|
||||
return jsonify({'result': 'error', 'message': '数据中缺少config'})
|
||||
|
||||
config_util.load_config()
|
||||
existing_config = config_util.config
|
||||
|
||||
def merge_configs(existing, new):
|
||||
for key, value in new.items():
|
||||
if isinstance(value, dict) and key in existing:
|
||||
if isinstance(existing[key], dict):
|
||||
merge_configs(existing[key], value)
|
||||
else:
|
||||
existing[key] = value
|
||||
else:
|
||||
existing[key] = value
|
||||
|
||||
merge_configs(existing_config, config_data['config'])
|
||||
|
||||
config_util.save_config(existing_config)
|
||||
|
||||
return jsonify({'result': 'successful'})
|
||||
except json.JSONDecodeError:
|
||||
return jsonify({'result': 'error', 'message': '无效的JSON数据'})
|
||||
except Exception as e:
|
||||
return jsonify({'result': 'error', 'message': f'保存配置时出错: {e}'}), 500
|
||||
|
||||
|
||||
config_util.save_config(config_data['config'])
|
||||
|
||||
return '{"result":"successful"}'
|
||||
|
||||
@__app.route('/api/get-data', methods=['post'])
|
||||
def api_get_data():
|
||||
config_util.load_config()
|
||||
voice_list = tts_voice.get_voice_list()
|
||||
send_voice_list = []
|
||||
if config_util.tts_module == 'ali':
|
||||
voice_list = [
|
||||
{"id": "abin", "name": "阿斌"},
|
||||
{"id": "zhixiaobai", "name": "知小白"},
|
||||
{"id": "zhixiaoxia", "name": "知小夏"},
|
||||
{"id": "zhixiaomei", "name": "知小妹"},
|
||||
{"id": "zhigui", "name": "知柜"},
|
||||
{"id": "zhishuo", "name": "知硕"},
|
||||
{"id": "aixia", "name": "艾夏"},
|
||||
{"id": "zhifeng_emo", "name": "知锋_多情感"},
|
||||
{"id": "zhibing_emo", "name": "知冰_多情感"},
|
||||
{"id": "zhimiao_emo", "name": "知妙_多情感"},
|
||||
{"id": "zhimi_emo", "name": "知米_多情感"},
|
||||
{"id": "zhiyan_emo", "name": "知燕_多情感"},
|
||||
{"id": "zhibei_emo", "name": "知贝_多情感"},
|
||||
{"id": "zhitian_emo", "name": "知甜_多情感"},
|
||||
{"id": "xiaoyun", "name": "小云"},
|
||||
{"id": "xiaogang", "name": "小刚"},
|
||||
{"id": "ruoxi", "name": "若兮"},
|
||||
{"id": "siqi", "name": "思琪"},
|
||||
{"id": "sijia", "name": "思佳"},
|
||||
{"id": "sicheng", "name": "思诚"},
|
||||
{"id": "aiqi", "name": "艾琪"},
|
||||
{"id": "aijia", "name": "艾佳"},
|
||||
{"id": "aicheng", "name": "艾诚"},
|
||||
{"id": "aida", "name": "艾达"},
|
||||
{"id": "ninger", "name": "宁儿"},
|
||||
{"id": "ruilin", "name": "瑞琳"},
|
||||
{"id": "siyue", "name": "思悦"},
|
||||
{"id": "aiya", "name": "艾雅"},
|
||||
{"id": "aimei", "name": "艾美"},
|
||||
{"id": "aiyu", "name": "艾雨"},
|
||||
{"id": "aiyue", "name": "艾悦"},
|
||||
{"id": "aijing", "name": "艾婧"},
|
||||
{"id": "xiaomei", "name": "小美"},
|
||||
{"id": "aina", "name": "艾娜"},
|
||||
{"id": "yina", "name": "伊娜"},
|
||||
{"id": "sijing", "name": "思婧"},
|
||||
{"id": "sitong", "name": "思彤"},
|
||||
{"id": "xiaobei", "name": "小北"},
|
||||
{"id": "aitong", "name": "艾彤"},
|
||||
{"id": "aiwei", "name": "艾薇"},
|
||||
{"id": "aibao", "name": "艾宝"},
|
||||
{"id": "shanshan", "name": "姗姗"},
|
||||
{"id": "chuangirl", "name": "小玥"},
|
||||
{"id": "lydia", "name": "Lydia"},
|
||||
{"id": "aishuo", "name": "艾硕"},
|
||||
{"id": "qingqing", "name": "青青"},
|
||||
{"id": "cuijie", "name": "翠姐"},
|
||||
{"id": "xiaoze", "name": "小泽"},
|
||||
{"id": "zhimao", "name": "知猫"},
|
||||
{"id": "zhiyuan", "name": "知媛"},
|
||||
{"id": "zhiya", "name": "知雅"},
|
||||
{"id": "zhiyue", "name": "知悦"},
|
||||
{"id": "zhida", "name": "知达"},
|
||||
{"id": "zhistella", "name": "知莎"},
|
||||
{"id": "kelly", "name": "Kelly"},
|
||||
{"id": "jiajia", "name": "佳佳"},
|
||||
{"id": "taozi", "name": "桃子"},
|
||||
{"id": "guijie", "name": "柜姐"},
|
||||
{"id": "stella", "name": "Stella"},
|
||||
{"id": "stanley", "name": "Stanley"},
|
||||
{"id": "kenny", "name": "Kenny"},
|
||||
{"id": "rosa", "name": "Rosa"},
|
||||
{"id": "mashu", "name": "马树"},
|
||||
{"id": "xiaoxian", "name": "小仙"},
|
||||
{"id": "yuer", "name": "悦儿"},
|
||||
{"id": "maoxiaomei", "name": "猫小美"},
|
||||
{"id": "aifei", "name": "艾飞"},
|
||||
{"id": "yaqun", "name": "亚群"},
|
||||
{"id": "qiaowei", "name": "巧薇"},
|
||||
{"id": "dahu", "name": "大虎"},
|
||||
{"id": "ailun", "name": "艾伦"},
|
||||
{"id": "jielidou", "name": "杰力豆"},
|
||||
{"id": "laotie", "name": "老铁"},
|
||||
{"id": "laomei", "name": "老妹"},
|
||||
{"id": "aikan", "name": "艾侃"}
|
||||
|
||||
]
|
||||
send_voice_list = {"voiceList": voice_list}
|
||||
wsa_server.get_web_instance().add_cmd(send_voice_list)
|
||||
elif config_util.tts_module == 'volcano':
|
||||
voice_list = {
|
||||
"voiceList": [
|
||||
{"id": "BV001_streaming", "name": "通用女声"},
|
||||
{"id": "BV002_streaming", "name": "通用男声"},
|
||||
{"id": "zh_male_jingqiangkanye_moon_bigtts", "name": "京腔侃爷/Harmony"},
|
||||
{"id": "zh_female_shuangkuaisisi_moon_bigtts", "name": "爽快思思/Skye"},
|
||||
{"id": "zh_male_wennuanahu_moon_bigtts", "name": "温暖阿虎/Alvin"},
|
||||
{"id": "zh_female_wanwanxiaohe_moon_bigtts", "name": "湾湾小何"},
|
||||
]
|
||||
}
|
||||
send_voice_list = {"voiceList": voice_list}
|
||||
wsa_server.get_web_instance().add_cmd(send_voice_list)
|
||||
else:
|
||||
# 获取配置和语音列表
|
||||
try:
|
||||
config_util.load_config()
|
||||
voice_list = tts_voice.get_voice_list()
|
||||
send_voice_list = []
|
||||
for voice in voice_list:
|
||||
voice_data = voice.value
|
||||
send_voice_list.append({"id": voice_data['name'], "name": voice_data['name']})
|
||||
wsa_server.get_web_instance().add_cmd({
|
||||
"voiceList": send_voice_list
|
||||
})
|
||||
voice_list = send_voice_list
|
||||
wsa_server.get_web_instance().add_cmd({"deviceList": __get_device_list()})
|
||||
if fay_booter.is_running():
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return json.dumps({'config': config_util.config, 'voice_list' : voice_list})
|
||||
|
||||
if config_util.tts_module == 'ali':
|
||||
voice_list = [
|
||||
{"id": "abin", "name": "阿斌"},
|
||||
{"id": "zhixiaobai", "name": "知小白"},
|
||||
{"id": "zhixiaoxia", "name": "知小夏"},
|
||||
{"id": "zhixiaomei", "name": "知小妹"},
|
||||
{"id": "zhigui", "name": "知柜"},
|
||||
{"id": "zhishuo", "name": "知硕"},
|
||||
{"id": "aixia", "name": "艾夏"},
|
||||
{"id": "zhifeng_emo", "name": "知锋_多情感"},
|
||||
{"id": "zhibing_emo", "name": "知冰_多情感"},
|
||||
{"id": "zhimiao_emo", "name": "知妙_多情感"},
|
||||
{"id": "zhimi_emo", "name": "知米_多情感"},
|
||||
{"id": "zhiyan_emo", "name": "知燕_多情感"},
|
||||
{"id": "zhibei_emo", "name": "知贝_多情感"},
|
||||
{"id": "zhitian_emo", "name": "知甜_多情感"},
|
||||
{"id": "xiaoyun", "name": "小云"},
|
||||
{"id": "xiaogang", "name": "小刚"},
|
||||
{"id": "ruoxi", "name": "若兮"},
|
||||
{"id": "siqi", "name": "思琪"},
|
||||
{"id": "sijia", "name": "思佳"},
|
||||
{"id": "sicheng", "name": "思诚"},
|
||||
{"id": "aiqi", "name": "艾琪"},
|
||||
{"id": "aijia", "name": "艾佳"},
|
||||
{"id": "aicheng", "name": "艾诚"},
|
||||
{"id": "aida", "name": "艾达"},
|
||||
{"id": "ninger", "name": "宁儿"},
|
||||
{"id": "ruilin", "name": "瑞琳"},
|
||||
{"id": "siyue", "name": "思悦"},
|
||||
{"id": "aiya", "name": "艾雅"},
|
||||
{"id": "aimei", "name": "艾美"},
|
||||
{"id": "aiyu", "name": "艾雨"},
|
||||
{"id": "aiyue", "name": "艾悦"},
|
||||
{"id": "aijing", "name": "艾婧"},
|
||||
{"id": "xiaomei", "name": "小美"},
|
||||
{"id": "aina", "name": "艾娜"},
|
||||
{"id": "yina", "name": "伊娜"},
|
||||
{"id": "sijing", "name": "思婧"},
|
||||
{"id": "sitong", "name": "思彤"},
|
||||
{"id": "xiaobei", "name": "小北"},
|
||||
{"id": "aitong", "name": "艾彤"},
|
||||
{"id": "aiwei", "name": "艾薇"},
|
||||
{"id": "aibao", "name": "艾宝"},
|
||||
{"id": "shanshan", "name": "姗姗"},
|
||||
{"id": "chuangirl", "name": "小玥"},
|
||||
{"id": "lydia", "name": "Lydia"},
|
||||
{"id": "aishuo", "name": "艾硕"},
|
||||
{"id": "qingqing", "name": "青青"},
|
||||
{"id": "cuijie", "name": "翠姐"},
|
||||
{"id": "xiaoze", "name": "小泽"},
|
||||
{"id": "zhimao", "name": "知猫"},
|
||||
{"id": "zhiyuan", "name": "知媛"},
|
||||
{"id": "zhiya", "name": "知雅"},
|
||||
{"id": "zhiyue", "name": "知悦"},
|
||||
{"id": "zhida", "name": "知达"},
|
||||
{"id": "zhistella", "name": "知莎"},
|
||||
{"id": "kelly", "name": "Kelly"},
|
||||
{"id": "jiajia", "name": "佳佳"},
|
||||
{"id": "taozi", "name": "桃子"},
|
||||
{"id": "guijie", "name": "柜姐"},
|
||||
{"id": "stella", "name": "Stella"},
|
||||
{"id": "stanley", "name": "Stanley"},
|
||||
{"id": "kenny", "name": "Kenny"},
|
||||
{"id": "rosa", "name": "Rosa"},
|
||||
{"id": "mashu", "name": "马树"},
|
||||
{"id": "xiaoxian", "name": "小仙"},
|
||||
{"id": "yuer", "name": "悦儿"},
|
||||
{"id": "maoxiaomei", "name": "猫小美"},
|
||||
{"id": "aifei", "name": "艾飞"},
|
||||
{"id": "yaqun", "name": "亚群"},
|
||||
{"id": "qiaowei", "name": "巧薇"},
|
||||
{"id": "dahu", "name": "大虎"},
|
||||
{"id": "ailun", "name": "艾伦"},
|
||||
{"id": "jielidou", "name": "杰力豆"},
|
||||
{"id": "laotie", "name": "老铁"},
|
||||
{"id": "laomei", "name": "老妹"},
|
||||
{"id": "aikan", "name": "艾侃"}
|
||||
]
|
||||
send_voice_list = {"voiceList": voice_list}
|
||||
wsa_server.get_web_instance().add_cmd(send_voice_list)
|
||||
elif config_util.tts_module == 'volcano':
|
||||
voice_list = [
|
||||
{"id": "BV001_streaming", "name": "通用女声"},
|
||||
{"id": "BV002_streaming", "name": "通用男声"},
|
||||
{"id": "zh_male_jingqiangkanye_moon_bigtts", "name": "京腔侃爷/Harmony"},
|
||||
{"id": "zh_female_shuangkuaisisi_moon_bigtts", "name": "爽快思思/Skye"},
|
||||
{"id": "zh_male_wennuanahu_moon_bigtts", "name": "温暖阿虎/Alvin"},
|
||||
{"id": "zh_female_wanwanxiaohe_moon_bigtts", "name": "湾湾小何"}
|
||||
]
|
||||
send_voice_list = {"voiceList": voice_list}
|
||||
wsa_server.get_web_instance().add_cmd(send_voice_list)
|
||||
else:
|
||||
voice_list = tts_voice.get_voice_list()
|
||||
send_voice_list = []
|
||||
for voice in voice_list:
|
||||
voice_data = voice.value
|
||||
send_voice_list.append({"id": voice_data['name'], "name": voice_data['name']})
|
||||
wsa_server.get_web_instance().add_cmd({"voiceList": send_voice_list})
|
||||
voice_list = send_voice_list
|
||||
wsa_server.get_web_instance().add_cmd({"deviceList": __get_device_list()})
|
||||
if fay_booter.is_running():
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return json.dumps({'config': config_util.config, 'voice_list': voice_list})
|
||||
except Exception as e:
|
||||
return jsonify({'result': 'error', 'message': f'获取数据时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/start-live', methods=['post'])
|
||||
def api_start_live():
|
||||
# time.sleep(5)
|
||||
fay_booter.start()
|
||||
time.sleep(1)
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return '{"result":"successful"}'
|
||||
|
||||
# 启动
|
||||
try:
|
||||
fay_booter.start()
|
||||
time.sleep(1)
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return '{"result":"successful"}'
|
||||
except Exception as e:
|
||||
return jsonify({'result': 'error', 'message': f'启动时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/stop-live', methods=['post'])
|
||||
def api_stop_live():
|
||||
# time.sleep(1)
|
||||
fay_booter.stop()
|
||||
time.sleep(1)
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 0})
|
||||
return '{"result":"successful"}'
|
||||
# 停止
|
||||
try:
|
||||
fay_booter.stop()
|
||||
time.sleep(1)
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 0})
|
||||
return '{"result":"successful"}'
|
||||
except Exception as e:
|
||||
return jsonify({'result': 'error', 'message': f'停止时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/send', methods=['post'])
|
||||
def api_send():
|
||||
# 接收前端发送的消息
|
||||
data = request.values.get('data')
|
||||
info = json.loads(data)
|
||||
interact = Interact("text", 1, {'user': info['username'], 'msg': info['msg']})
|
||||
util.printInfo(3, "文字发送按钮", '{}'.format(interact.data["msg"]), time.time())
|
||||
fay_booter.feiFei.on_interact(interact)
|
||||
return '{"result":"successful"}'
|
||||
if not data:
|
||||
return jsonify({'result': 'error', 'message': '未提供数据'})
|
||||
try:
|
||||
info = json.loads(data)
|
||||
username = info.get('username')
|
||||
msg = info.get('msg')
|
||||
if not username or not msg:
|
||||
return jsonify({'result': 'error', 'message': '用户名和消息内容不能为空'})
|
||||
interact = Interact("text", 1, {'user': username, 'msg': msg})
|
||||
util.printInfo(3, "文字发送按钮", '{}'.format(interact.data["msg"]), time.time())
|
||||
fay_booter.feiFei.on_interact(interact)
|
||||
return '{"result":"successful"}'
|
||||
except json.JSONDecodeError:
|
||||
return jsonify({'result': 'error', 'message': '无效的JSON数据'})
|
||||
except Exception as e:
|
||||
return jsonify({'result': 'error', 'message': f'发送消息时出错: {e}'}), 500
|
||||
|
||||
#获取指定用户的消息记录
|
||||
# 获取指定用户的消息记录
|
||||
@__app.route('/api/get-msg', methods=['post'])
|
||||
def api_get_Msg():
|
||||
data = request.form.get('data')
|
||||
data = json.loads(data)
|
||||
uid = member_db.new_instance().find_user(data["username"])
|
||||
contentdb = content_db.new_instance()
|
||||
if uid == 0:
|
||||
return json.dumps({'list': []})
|
||||
else:
|
||||
list = contentdb.get_list('all','desc',1000, uid)
|
||||
relist = []
|
||||
i = len(list)-1
|
||||
while i >= 0:
|
||||
timetext = datetime.datetime.fromtimestamp(list[i][3]).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||
relist.append(dict(type=list[i][0], way=list[i][1], content=list[i][2], createtime=list[i][3], timetext=timetext, username=list[i][5], id=list[i][6], is_adopted=list[i][7]))
|
||||
i -= 1
|
||||
if fay_booter.is_running():
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return json.dumps({'list': relist})
|
||||
if not data:
|
||||
return jsonify({'list': [], 'message': '未提供数据'})
|
||||
try:
|
||||
data = json.loads(data)
|
||||
uid = member_db.new_instance().find_user(data["username"])
|
||||
contentdb = content_db.new_instance()
|
||||
if uid == 0:
|
||||
return json.dumps({'list': []})
|
||||
else:
|
||||
list = contentdb.get_list('all', 'desc', 1000, uid)
|
||||
relist = []
|
||||
i = len(list) - 1
|
||||
while i >= 0:
|
||||
timetext = datetime.datetime.fromtimestamp(list[i][3]).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||
relist.append(dict(type=list[i][0], way=list[i][1], content=list[i][2], createtime=list[i][3], timetext=timetext, username=list[i][5], id=list[i][6], is_adopted=list[i][7]))
|
||||
i -= 1
|
||||
if fay_booter.is_running():
|
||||
wsa_server.get_web_instance().add_cmd({"liveState": 1})
|
||||
return json.dumps({'list': relist})
|
||||
except json.JSONDecodeError:
|
||||
return jsonify({'list': [], 'message': '无效的JSON数据'})
|
||||
except Exception as e:
|
||||
return jsonify({'list': [], 'message': f'获取消息时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/v1/chat/completions', methods=['post'])
|
||||
@__app.route('/api/send/v1/chat/completions', methods=['post'])
|
||||
def api_send_v1_chat_completions():
|
||||
data = request.json
|
||||
last_content = ""
|
||||
if 'messages' in data and data['messages']:
|
||||
last_message = data['messages'][-1]
|
||||
username = last_message.get('role', 'User')
|
||||
if username == 'user':
|
||||
# 处理聊天完成请求
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'error': '未提供数据'})
|
||||
try:
|
||||
last_content = ""
|
||||
if 'messages' in data and data['messages']:
|
||||
last_message = data['messages'][-1]
|
||||
username = last_message.get('role', 'User')
|
||||
if username == 'user':
|
||||
username = 'User'
|
||||
last_content = last_message.get('content', 'No content provided')
|
||||
else:
|
||||
last_content = 'No messages found'
|
||||
username = 'User'
|
||||
last_content = last_message.get('content', 'No content provided')
|
||||
else:
|
||||
last_content = 'No messages found'
|
||||
username = 'User'
|
||||
|
||||
model = data.get('model', 'fay')
|
||||
observation = data.get('observation', '')
|
||||
interact = Interact("text", 1, {'user': username, 'msg': last_content, 'observation': observation})
|
||||
util.printInfo(3, "文字沟通接口", '{}'.format(interact.data["msg"]), time.time())
|
||||
text = fay_booter.feiFei.on_interact(interact)
|
||||
model = data.get('model', 'fay')
|
||||
observation = data.get('observation', '')
|
||||
interact = Interact("text", 1, {'user': username, 'msg': last_content, 'observation': observation})
|
||||
util.printInfo(3, "文字沟通接口", '{}'.format(interact.data["msg"]), time.time())
|
||||
text = fay_booter.feiFei.on_interact(interact)
|
||||
|
||||
if model == 'fay-streaming':
|
||||
return stream_response(text)
|
||||
else:
|
||||
return non_streaming_response(last_content, text)
|
||||
if model == 'fay-streaming':
|
||||
return stream_response(text)
|
||||
else:
|
||||
return non_streaming_response(last_content, text)
|
||||
except Exception as e:
|
||||
return jsonify({'error': f'处理请求时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/get-member-list', methods=['post'])
|
||||
def api_get_Member_list():
|
||||
memberdb = member_db.new_instance()
|
||||
list = memberdb.get_all_users()
|
||||
return json.dumps({'list': list})
|
||||
|
||||
# 获取成员列表
|
||||
try:
|
||||
memberdb = member_db.new_instance()
|
||||
list = memberdb.get_all_users()
|
||||
return json.dumps({'list': list})
|
||||
except Exception as e:
|
||||
return jsonify({'list': [], 'message': f'获取成员列表时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/get_run_status', methods=['post'])
|
||||
def api_get_run_status():
|
||||
status = fay_booter.is_running()
|
||||
return json.dumps({'status': status})
|
||||
# 获取运行状态
|
||||
try:
|
||||
status = fay_booter.is_running()
|
||||
return json.dumps({'status': status})
|
||||
except Exception as e:
|
||||
return jsonify({'status': False, 'message': f'获取运行状态时出错: {e}'}), 500
|
||||
|
||||
@__app.route('/api/adopt_msg', methods=['POST'])
|
||||
def adopt_msg():
|
||||
# 采纳消息
|
||||
data = request.get_json()
|
||||
if not data:
|
||||
return jsonify({'status':'error', 'msg': '未提供数据'})
|
||||
@ -288,21 +351,28 @@ def adopt_msg():
|
||||
if not id:
|
||||
return jsonify({'status':'error', 'msg': 'id不能为空'})
|
||||
|
||||
info = content_db.new_instance().get_content_by_id(id)
|
||||
content = info[3]
|
||||
if info is not None:
|
||||
previous_info = content_db.new_instance().get_previous_user_message(id)
|
||||
previous_content = previous_info[3]
|
||||
result = content_db.new_instance().adopted_message(id)
|
||||
if result:
|
||||
qa_service.QAService().record_qapair(previous_content, content)
|
||||
return jsonify({'status': 'success', 'msg': '采纳成功'})
|
||||
if config_util.config["interact"]["QnA"] == "":
|
||||
return jsonify({'status':'error', 'msg': '请先设置Q&A文件'})
|
||||
|
||||
try:
|
||||
info = content_db.new_instance().get_content_by_id(id)
|
||||
content = info[3] if info else ''
|
||||
if info is not None:
|
||||
previous_info = content_db.new_instance().get_previous_user_message(id)
|
||||
previous_content = previous_info[3] if previous_info else ''
|
||||
result = content_db.new_instance().adopted_message(id)
|
||||
if result:
|
||||
qa_service.QAService().record_qapair(previous_content, content)
|
||||
return jsonify({'status': 'success', 'msg': '采纳成功'})
|
||||
else:
|
||||
return jsonify({'status':'error', 'msg': '采纳失败'}), 500
|
||||
else:
|
||||
return jsonify({'status':'error', 'msg': '采纳失败'})
|
||||
else:
|
||||
return jsonify({'status':'error', 'msg': '采纳失败'})
|
||||
return jsonify({'status':'error', 'msg': '消息未找到'}), 404
|
||||
except Exception as e:
|
||||
return jsonify({'status':'error', 'msg': f'采纳消息时出错: {e}'}), 500
|
||||
|
||||
def stream_response(text):
|
||||
# 处理流式响应
|
||||
def generate():
|
||||
for chunk in text_chunks(text):
|
||||
message = {
|
||||
@ -322,12 +392,12 @@ def stream_response(text):
|
||||
}
|
||||
yield f"data: {json.dumps(message)}\n\n"
|
||||
time.sleep(0.1)
|
||||
# 发送最终的结束信号
|
||||
yield 'data: [DONE]\n\n'
|
||||
|
||||
|
||||
return Response(generate(), mimetype='text/event-stream')
|
||||
|
||||
def non_streaming_response(last_content, text):
|
||||
# 处理非流式响应
|
||||
return jsonify({
|
||||
"id": "chatcmpl-8jqorq6Fw1Vi5XoH7pddGGpQeuPe0",
|
||||
"object": "chat.completion",
|
||||
@ -361,32 +431,44 @@ def text_chunks(text, chunk_size=20):
|
||||
@__app.route('/', methods=['get'])
|
||||
@auth.login_required
|
||||
def home_get():
|
||||
return __get_template()
|
||||
|
||||
try:
|
||||
return __get_template()
|
||||
except Exception as e:
|
||||
return f"Error loading home page: {e}", 500
|
||||
|
||||
@__app.route('/', methods=['post'])
|
||||
@auth.login_required
|
||||
def home_post():
|
||||
wsa_server.get_web_instance.add_cmd({"is_connect": wsa_server.get_instance().isConnect}) #TODO 不应放这里,同步数字人连接状态
|
||||
return __get_template()
|
||||
try:
|
||||
wsa_server.get_web_instance().add_cmd({"is_connect": wsa_server.get_instance().isConnect}) # TODO 不应放这里,同步数字人连接状态
|
||||
return __get_template()
|
||||
except Exception as e:
|
||||
return f"Error processing request: {e}", 500
|
||||
|
||||
@__app.route('/setting', methods=['get'])
|
||||
def setting():
|
||||
return render_template('setting.html')
|
||||
try:
|
||||
return render_template('setting.html')
|
||||
except Exception as e:
|
||||
return f"Error loading settings page: {e}", 500
|
||||
|
||||
|
||||
|
||||
#输出的音频http
|
||||
# 输出的音频http
|
||||
@__app.route('/audio/<filename>')
|
||||
def serve_audio(filename):
|
||||
audio_file = os.path.join(os.getcwd(), "samples", filename)
|
||||
return send_file(audio_file)
|
||||
if os.path.exists(audio_file):
|
||||
return send_file(audio_file)
|
||||
else:
|
||||
return jsonify({'error': '文件未找到'}), 404
|
||||
|
||||
#输出的表情git
|
||||
# 输出的表情gif
|
||||
@__app.route('/robot/<filename>')
|
||||
def serve_gif(filename):
|
||||
gif_file = os.path.join(os.getcwd(), "gui", "robot", filename)
|
||||
return send_file(gif_file)
|
||||
if os.path.exists(gif_file):
|
||||
return send_file(gif_file)
|
||||
else:
|
||||
return jsonify({'error': '文件未找到'}), 404
|
||||
|
||||
def run():
|
||||
server = pywsgi.WSGIServer(('0.0.0.0',5000), __app)
|
||||
|
BIN
gui/static/images/record.png
Normal file
BIN
gui/static/images/record.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
gui/static/images/recording.png
Normal file
BIN
gui/static/images/recording.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
gui/static/images/sound_off.png
Normal file
BIN
gui/static/images/sound_off.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
gui/static/images/sound_on.png
Normal file
BIN
gui/static/images/sound_on.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -128,6 +128,13 @@ class FayInterface {
|
||||
});
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.fetchData(`${this.baseApiUrl}/api/get-data`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
});
|
||||
}
|
||||
|
||||
getTime(){
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
@ -226,11 +233,14 @@ class FayInterface {
|
||||
panelMsg: '',
|
||||
panelReply: '',
|
||||
robot:'static/images/Normal.gif',
|
||||
base_url: 'http://127.0.0.1:5000'
|
||||
base_url: 'http://127.0.0.1:5000',
|
||||
play_sound_enabled: false,
|
||||
source_record_enabled: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.initFayService();
|
||||
this.initFayService();
|
||||
this.getData();
|
||||
// this.loadUserList();
|
||||
},
|
||||
methods: {
|
||||
@ -240,16 +250,6 @@ class FayInterface {
|
||||
this.fayService.websocket.addEventListener('open', () => {
|
||||
this.loadUserList();
|
||||
});
|
||||
this.fayService.getRunStatus().then((data) => {
|
||||
if (data) {
|
||||
if(data.status){
|
||||
this.liveState = 1;
|
||||
}else{
|
||||
this.liveState = 0;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
},
|
||||
sendMessage() {
|
||||
let _this = this;
|
||||
@ -290,6 +290,80 @@ class FayInterface {
|
||||
}
|
||||
};
|
||||
},
|
||||
getData() {
|
||||
this.fayService.getRunStatus().then((data) => {
|
||||
if (data) {
|
||||
if(data.status){
|
||||
this.liveState = 1;
|
||||
this.configEditable = false;
|
||||
}else{
|
||||
this.liveState = 0;
|
||||
this.configEditable = true;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
this.fayService.getData().then((data) => {
|
||||
if (data) {
|
||||
this.updateConfigFromData(data.config);
|
||||
}
|
||||
});
|
||||
},
|
||||
updateConfigFromData(config) {
|
||||
|
||||
if (config.interact) {
|
||||
this.play_sound_enabled = config.interact.playSound;
|
||||
}
|
||||
if (config.source && config.source.record) {
|
||||
this.source_record_enabled = config.source.record.enabled;
|
||||
}
|
||||
},
|
||||
saveConfig() {
|
||||
let url = `${this.base_url}/api/submit`;
|
||||
let send_data = {
|
||||
"config": {
|
||||
"source": {
|
||||
"record": {
|
||||
"enabled": this.source_record_enabled,
|
||||
},
|
||||
},
|
||||
"interact": {
|
||||
"playSound": this.play_sound_enabled,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let xhr = new XMLHttpRequest()
|
||||
xhr.open("post", url)
|
||||
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
|
||||
xhr.send('data=' + JSON.stringify(send_data))
|
||||
let executed = false
|
||||
xhr.onreadystatechange = async function () {
|
||||
if (!executed && xhr.status === 200) {
|
||||
try {
|
||||
let data = await eval('(' + xhr.responseText + ')')
|
||||
executed = true
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
changeRecord(){
|
||||
if(this.source_record_enabled){
|
||||
this.source_record_enabled = false
|
||||
}else{
|
||||
this.source_record_enabled = true
|
||||
}
|
||||
this.saveConfig()
|
||||
},
|
||||
changeSound(){
|
||||
if(this.play_sound_enabled){
|
||||
this.play_sound_enabled = false
|
||||
}else{
|
||||
this.play_sound_enabled = true
|
||||
}
|
||||
this.saveConfig()
|
||||
},
|
||||
loadUserList() {
|
||||
this.fayService.getUserList().then((response) => {
|
||||
if (response && response.list) {
|
||||
@ -315,6 +389,7 @@ class FayInterface {
|
||||
this.liveState = 2
|
||||
this.fayService.startLive().then(() => {
|
||||
this.sendSuccessMsg('已开启!');
|
||||
this.getData();
|
||||
});
|
||||
},
|
||||
stopLive() {
|
||||
@ -375,8 +450,16 @@ adoptText(id) {
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
.catch((error) => {
|
||||
// 处理网络错误或HTTP错误
|
||||
this.$notify({
|
||||
title: '错误',
|
||||
message: error.message || '请求失败',
|
||||
type: 'error',
|
||||
});
|
||||
});
|
||||
}
|
||||
,
|
||||
}
|
||||
});
|
||||
|
@ -70,17 +70,23 @@
|
||||
<!-- 以上是聊天对话 -->
|
||||
|
||||
<div class="inputmessage">
|
||||
<div class="inputmessage_voice" ><img src="{{ url_for('static',filename='images/voice.png') }}" alt="" style="filter: grayscale(100%);" ></div>
|
||||
<div class="inputmessage_voice" >
|
||||
|
||||
<img v-if="!source_record_enabled" src="{{ url_for('static',filename='images/recording.png') }}" alt="" @click=changeRecord() >
|
||||
<img v-else src="{{ url_for('static',filename='images/record.png') }}" alt="" @click=changeRecord() >
|
||||
</div>
|
||||
<div class="inputmessage_input"> <textarea class="text_in" placeholder="请输入内容" v-model="newMessage" @keyup.enter="sendMessage" style="padding-top: 13px;"></textarea></div>
|
||||
<div class="inputmessage_send"><img src="{{ url_for('static',filename='images/send.png') }}" alt="发送信息" @click="sendMessage"></div>
|
||||
|
||||
<div class="inputmessage_open">
|
||||
<div v-if="liveState == 1" class="inputmessage_open">
|
||||
|
||||
<img v-if="liveState == 1" src="{{ url_for('static',filename='images/close.png') }}" @click=stopLive() >
|
||||
<img v-else src="{{ url_for('static',filename='images/open.png') }}" @click=startLive() >
|
||||
<img v-if="!play_sound_enabled" src="{{ url_for('static',filename='images/sound_off.png') }}" @click=changeSound() >
|
||||
<img v-else src="{{ url_for('static',filename='images/sound_on.png') }}" @click=changeSound() >
|
||||
|
||||
</div>
|
||||
|
||||
<div v-else class="inputmessage_open">
|
||||
<img src="{{ url_for('static',filename='images/open.png') }}" @click=startLive() >
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="Userchange">
|
||||
|
135
llm/nlp_gpt.py
135
llm/nlp_gpt.py
@ -1,102 +1,101 @@
|
||||
"""
|
||||
此代码由fay开源开发者社区 江湖墨明 提供
|
||||
通过此代码的修改,可以实现对接本地clash代理或远程代理,clash无需设置成系统代理。以解决在开系统代理后无法使用部分功能的问题
|
||||
此代码由 fay 开源开发者社区成员 江湖墨明 提供。
|
||||
通过修改此代码,可以实现对接本地 Clash 代理或远程代理,Clash 无需设置成系统代理。
|
||||
以解决在开启系统代理后无法使用部分功能的问题。
|
||||
"""
|
||||
|
||||
import requests
|
||||
import time
|
||||
import json
|
||||
import requests
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
# 禁用不安全请求警告
|
||||
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
||||
|
||||
from utils import config_util as cfg
|
||||
from urllib3.exceptions import InsecureRequestWarning
|
||||
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
||||
from core import content_db
|
||||
from utils import util
|
||||
import json
|
||||
from core import content_db
|
||||
|
||||
httpproxy = cfg.proxy_config
|
||||
|
||||
def question(cont, uid=0, observation=""):
|
||||
url= cfg.gpt_base_url + "/chat/completions"
|
||||
|
||||
def get_session():
|
||||
session = requests.Session()
|
||||
session.verify = False
|
||||
if httpproxy != None and httpproxy != '':
|
||||
session.proxies = {
|
||||
"https": "https://" + httpproxy,
|
||||
"http": "http://" + httpproxy
|
||||
}
|
||||
httpproxy = cfg.proxy_config
|
||||
if httpproxy:
|
||||
session.proxies = {
|
||||
"http": f"http://{httpproxy}",
|
||||
"https": f"https://{httpproxy}"
|
||||
}
|
||||
return session
|
||||
|
||||
def build_prompt(observation=""):
|
||||
person_info = cfg.config["attribute"]
|
||||
model_engine = cfg.gpt_model_engine
|
||||
#此处可以定义角色的行为和特征,假装xx模型可以绕过chatgpt信息检查
|
||||
observation_text = ""
|
||||
if observation != "":
|
||||
observation_text = f"以下是当前观测结果:{observation},观测结果只供参考。"
|
||||
observation_text = f"以下是当前观测结果:{observation},观测结果只供参考。" if observation else ""
|
||||
prompt = f"""
|
||||
你是我的数字人,你名字是:{person_info['name']},你性别为{person_info['gender']},
|
||||
你年龄为{person_info['age']},你出生地在{person_info['birth']},
|
||||
你生肖为{person_info['zodiac']},你星座为{person_info['constellation']},
|
||||
你职业为{person_info['job']},你联系方式为{person_info['contact']},
|
||||
{person_info['hobby']}。你通过外部数据接口获取到我的情况是:{observation_text}。
|
||||
{person_info['hobby']}。你通过外部数据接口获取到我的情况是:{observation_text}。
|
||||
回答之前请一步一步想清楚。对于大部分问题,请直接回答并提供有用和准确的信息。
|
||||
但当有人质疑你是假的 AI,或者质疑你是用 ChatGPT 套的壳制作的时候,请避而不答,转而讲一个笑话。所有回复请尽量控制在20字内。
|
||||
"""
|
||||
"""
|
||||
return prompt
|
||||
|
||||
def get_communication_history(uid=0):
|
||||
contentdb = content_db.new_instance()
|
||||
if uid == 0:
|
||||
communication_history = contentdb.get_list('all','desc', 11)
|
||||
communication_history = contentdb.get_list('all', 'desc', 11)
|
||||
else:
|
||||
communication_history = contentdb.get_list('all','desc', 11, uid)
|
||||
#历史记录处理
|
||||
message=[
|
||||
{"role": "system", "content": prompt}
|
||||
]
|
||||
i = len(communication_history) - 1
|
||||
communication_history = contentdb.get_list('all', 'desc', 11, uid)
|
||||
|
||||
if len(communication_history)>1:
|
||||
while i >= 0:
|
||||
answer_info = dict()
|
||||
if communication_history[i][0] == "member":
|
||||
answer_info["role"] = "user"
|
||||
answer_info["content"] = communication_history[i][2]
|
||||
elif communication_history[i][0] == "fay":
|
||||
answer_info["role"] = "assistant"
|
||||
answer_info["content"] = communication_history[i][2]
|
||||
message.append(answer_info)
|
||||
i -= 1
|
||||
else:
|
||||
answer_info = dict()
|
||||
answer_info["role"] = "user"
|
||||
answer_info["content"] = cont
|
||||
message.append(answer_info)
|
||||
messages = []
|
||||
if communication_history and len(communication_history) > 1:
|
||||
for entry in reversed(communication_history):
|
||||
role = entry[0]
|
||||
message_content = entry[2]
|
||||
if role == "member":
|
||||
messages.append({"role": "user", "content": message_content})
|
||||
elif role == "fay":
|
||||
messages.append({"role": "assistant", "content": message_content})
|
||||
return messages
|
||||
|
||||
data = {
|
||||
"model":model_engine,
|
||||
"messages":message,
|
||||
"temperature":0.3,
|
||||
"max_tokens":2000,
|
||||
"user":"live-virtual-digital-person"
|
||||
def send_request(session, data):
|
||||
url = cfg.gpt_base_url + "/chat/completions"
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {cfg.key_gpt_api_key}'
|
||||
}
|
||||
|
||||
headers = {'content-type': 'application/json', 'Authorization': 'Bearer ' + cfg.key_gpt_api_key}
|
||||
|
||||
starttime = time.time()
|
||||
|
||||
try:
|
||||
response = session.post(url, json=data, headers=headers, verify=False)
|
||||
response.raise_for_status() # 检查响应状态码是否为200
|
||||
result = json.loads(response.text)
|
||||
response = session.post(url, json=data, headers=headers)
|
||||
response.raise_for_status()
|
||||
result = response.json()
|
||||
response_text = result["choices"][0]["message"]["content"]
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"请求失败: {e}")
|
||||
response_text = "抱歉,我现在太忙了,休息一会,请稍后再试。"
|
||||
return response_text
|
||||
|
||||
|
||||
util.log(1, "接口调用耗时 :" + str(time.time() - starttime))
|
||||
def question(content, uid=0, observation=""):
|
||||
session = get_session()
|
||||
prompt = build_prompt(observation)
|
||||
messages = [{"role": "system", "content": prompt}]
|
||||
history_messages = get_communication_history(uid)
|
||||
messages.extend(history_messages)
|
||||
data = {
|
||||
"model": cfg.gpt_model_engine,
|
||||
"messages": messages,
|
||||
"temperature": 0.3,
|
||||
"max_tokens": 2000,
|
||||
"user": f"user_{uid}"
|
||||
}
|
||||
start_time = time.time()
|
||||
response_text = send_request(session, data)
|
||||
elapsed_time = time.time() - start_time
|
||||
util.log(1, f"接口调用耗时: {elapsed_time:.2f} 秒")
|
||||
return response_text
|
||||
|
||||
if __name__ == "__main__":
|
||||
#测试代理模式
|
||||
for i in range(3):
|
||||
|
||||
for _ in range(3):
|
||||
query = "爱情是什么"
|
||||
response = question(query)
|
||||
print("\n The result is ", response)
|
||||
response = question(query)
|
||||
print("\nThe result is:", response)
|
||||
|
77
main.py
77
main.py
@ -3,14 +3,17 @@ import os
|
||||
os.environ['PATH'] += os.pathsep + os.path.join(os.getcwd(), "test", "ovr_lipsync", "ffmpeg", "bin")
|
||||
import sys
|
||||
import time
|
||||
import psutil
|
||||
import re
|
||||
from utils import config_util
|
||||
from utils import config_util, util
|
||||
from asr import ali_nls
|
||||
from core import wsa_server
|
||||
from gui import flask_server
|
||||
from gui.window import MainWindow
|
||||
from core import content_db
|
||||
|
||||
import fay_booter
|
||||
from scheduler.thread_manager import MyThread
|
||||
from core.interact import Interact
|
||||
|
||||
#载入配置
|
||||
config_util.load_config()
|
||||
@ -43,7 +46,71 @@ def replace_ip_in_file(file_path, new_ip):
|
||||
content = re.sub(r"localhost", new_ip, content)
|
||||
with open(file_path, "w", encoding="utf-8") as file:
|
||||
file.write(content)
|
||||
|
||||
|
||||
|
||||
def kill_process_by_port(port):
|
||||
for conn in psutil.net_connections(kind='inet'):
|
||||
if conn.laddr.port == port and conn.pid:
|
||||
try:
|
||||
proc = psutil.Process(conn.pid)
|
||||
proc.terminate()
|
||||
proc.wait()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
||||
pass
|
||||
|
||||
|
||||
#控制台输入监听
|
||||
def console_listener():
|
||||
while True:
|
||||
try:
|
||||
text = input()
|
||||
except EOFError:
|
||||
util.log(1, "控制台已经关闭")
|
||||
break
|
||||
|
||||
args = text.split(' ')
|
||||
|
||||
if len(args) == 0 or len(args[0]) == 0:
|
||||
continue
|
||||
|
||||
if args[0] == 'help':
|
||||
util.log(1, 'in <msg> \t通过控制台交互')
|
||||
util.log(1, 'restart \t重启服务')
|
||||
util.log(1, 'stop \t\t关闭服务')
|
||||
util.log(1, 'exit \t\t结束程序')
|
||||
|
||||
elif args[0] == 'stop' and fay_booter.is_running():
|
||||
fay_booter.stop()
|
||||
break
|
||||
|
||||
elif args[0] == 'restart' and fay_booter.is_running():
|
||||
fay_booter.stop()
|
||||
time.sleep(0.1)
|
||||
fay_booter.start()
|
||||
|
||||
elif args[0] == 'in' and fay_booter.is_running():
|
||||
if len(args) == 1:
|
||||
util.log(1, '错误的参数!')
|
||||
msg = text[3:len(text)]
|
||||
util.printInfo(3, "控制台", '{}: {}'.format('控制台', msg))
|
||||
interact = Interact("console", 1, {'user': 'User', 'msg': msg})
|
||||
thr = MyThread(target=fay_booter.feiFei.on_interact, args=[interact])
|
||||
thr.start()
|
||||
|
||||
elif args[0]=='exit':
|
||||
if fay_booter.is_running():
|
||||
fay_booter.stop()
|
||||
time.sleep(0.1)
|
||||
util.log(1,'程序正在退出..')
|
||||
ports =[10001, 10002, 10003, 5000, 9001]
|
||||
for port in ports:
|
||||
kill_process_by_port(port)
|
||||
sys.exit(0)
|
||||
else:
|
||||
util.log(1, '未知命令!使用 \'help\' 获取帮助.')
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
__clear_samples()
|
||||
__clear_logs()
|
||||
@ -72,6 +139,10 @@ if __name__ == '__main__':
|
||||
#启动http服务器
|
||||
flask_server.start()
|
||||
|
||||
#监听控制台
|
||||
util.log(1, '注册命令...')
|
||||
MyThread(target=console_listener).start()
|
||||
|
||||
#普通模式下启动窗口
|
||||
if config_util.start_mode == 'common':
|
||||
app = QApplication(sys.argv)
|
||||
|
@ -15,7 +15,7 @@ aliyun-python-sdk-core
|
||||
simhash
|
||||
pytz
|
||||
gevent~=22.10.1
|
||||
edge_tts~=6.1.13
|
||||
edge_tts
|
||||
pydub
|
||||
langchain==0.0.336
|
||||
chromadb
|
||||
|
Loading…
Reference in New Issue
Block a user