Signed-off-by: sairate <sairate@sina.cn>
This commit is contained in:
commit
a20d8bfbb5
|
@ -0,0 +1,8 @@
|
||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.12 (AIchat)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GitToolBoxBlameSettings">
|
||||||
|
<option name="version" value="2" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.12 (AIchat)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (AIchat)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/AIchat.iml" filepath="$PROJECT_DIR$/.idea/AIchat.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,24 @@
|
||||||
|
from flask import Flask, render_template
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
# 连接数据库
|
||||||
|
conn = sqlite3.connect('conversation.db')
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# 从数据库中获取数据
|
||||||
|
c.execute("SELECT * FROM conversation")
|
||||||
|
conversations = c.fetchall()
|
||||||
|
|
||||||
|
# 关闭数据库连接
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
return render_template("./index.html", conversations=conversations)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(debug=True)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Conversation Table</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Conversation Table</h1>
|
||||||
|
<table border="1">
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Message</th>
|
||||||
|
<!-- 添加其他列标题 -->
|
||||||
|
</tr>
|
||||||
|
{% for conversation in conversations %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ conversation[0] }}</td>
|
||||||
|
<td>{{ conversation[1] }}</td>
|
||||||
|
<!-- 添加其他列数据 -->
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,223 @@
|
||||||
|
|
||||||
|
import pyaudio
|
||||||
|
import wave
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import edge_tts
|
||||||
|
import asyncio
|
||||||
|
import pygame
|
||||||
|
import openai
|
||||||
|
import uuid # 用于生成唯一的文件名
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
def create_connection(db_file='conversation.db'):
|
||||||
|
# 连接到数据库(如果不存在,则会被创建)
|
||||||
|
conn = sqlite3.connect('conversation.db')
|
||||||
|
|
||||||
|
# 创建一个游标对象,用于执行SQL语句
|
||||||
|
c = conn.cursor()
|
||||||
|
|
||||||
|
# 创建一个名为conversation的表
|
||||||
|
c.execute('''CREATE TABLE IF NOT EXISTS conversation
|
||||||
|
(id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
question TEXT,
|
||||||
|
answer TEXT,
|
||||||
|
audio_path TEXT)''')
|
||||||
|
|
||||||
|
def insert_data(question, answer, audio_path):
|
||||||
|
conn = sqlite3.connect('conversation.db')
|
||||||
|
cursor = conn.cursor()
|
||||||
|
cursor.execute("INSERT INTO conversation (question, answer, audio_path) VALUES (?, ?, ?)",
|
||||||
|
(question, answer, audio_path))
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
# 1.录音
|
||||||
|
# 用Pyaudio录制音频(生成wav文件)
|
||||||
|
def audio_record(rec_time, filename):
|
||||||
|
"""
|
||||||
|
:param rec_time : 音频录制时间
|
||||||
|
:param filename : 输出音频文件
|
||||||
|
:返回值:在当前目录输出一个音频文件
|
||||||
|
"""
|
||||||
|
|
||||||
|
CHUNK = 1024 # 定义数据流块
|
||||||
|
FORMAT = pyaudio.paInt16 # 16bit编码格式
|
||||||
|
CHANNELS = 1 # 单声道
|
||||||
|
RATE = 16000 # 16000采样频率
|
||||||
|
|
||||||
|
# 创建一个音频对象
|
||||||
|
p = pyaudio.PyAudio()
|
||||||
|
|
||||||
|
# 创建音频数据流
|
||||||
|
stream = p.open(format=FORMAT,
|
||||||
|
channels=CHANNELS,
|
||||||
|
rate=RATE,
|
||||||
|
input=True,
|
||||||
|
frames_per_buffer=CHUNK)
|
||||||
|
print('Start recording...')
|
||||||
|
frames = list() # 空列表用于保存录制的音频流
|
||||||
|
# 录制音频数据
|
||||||
|
for i in range(0, int(RATE / CHUNK * rec_time)):
|
||||||
|
data = stream.read(CHUNK)
|
||||||
|
frames.append(data)
|
||||||
|
# 录制完成
|
||||||
|
# print(frames)
|
||||||
|
# 停止数据流
|
||||||
|
stream.stop_stream()
|
||||||
|
stream.close()
|
||||||
|
# 关闭pyaudio
|
||||||
|
p.terminate()
|
||||||
|
print('recording done...')
|
||||||
|
|
||||||
|
# 保存音频文件
|
||||||
|
with wave.open(filename, 'wb') as f:
|
||||||
|
f.setnchannels(CHANNELS) # 设置音频声道数
|
||||||
|
f.setsampwidth(p.get_sample_size(FORMAT)) # 以字节为样本返回样本宽度
|
||||||
|
f.setframerate(RATE) # 设置采样频率
|
||||||
|
f.writeframes(b''.join(frames))
|
||||||
|
f.close()
|
||||||
|
|
||||||
|
|
||||||
|
# 2 获取token
|
||||||
|
|
||||||
|
API_KEY = "7myE5M0cY5gjyKbxcFQqWmZE" # 这里请替换为你的API_KEY
|
||||||
|
SECRET_KEY = "A2AtUqbqVLdo0kgfiwITWUlB0fxwCA3w" # 这里请替换为你的SECRET_KEY
|
||||||
|
|
||||||
|
|
||||||
|
def get_access_token():
|
||||||
|
"""
|
||||||
|
使用 AK,SK 生成鉴权签名(Access Token)
|
||||||
|
:return: access_token,或是None(如果错误)
|
||||||
|
"""
|
||||||
|
url = "https://aip.baidubce.com/oauth/2.0/token"
|
||||||
|
params = {"grant_type": "client_credentials", "client_id": API_KEY, "client_secret": SECRET_KEY}
|
||||||
|
return str(requests.post(url, params=params).json().get("access_token"))
|
||||||
|
|
||||||
|
|
||||||
|
# 3.上传录音文件
|
||||||
|
def BaiduYuYin(file_url, token):
|
||||||
|
"""
|
||||||
|
:param file_url: 录音文件路径
|
||||||
|
:param token: 获取的access token
|
||||||
|
:return: 录音识别出来的文本
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
RATE = '16000'
|
||||||
|
FORMAT = 'wav'
|
||||||
|
CUID = 'rvs7K414cquxm4f62jtasIRi6iNRNXR6'
|
||||||
|
DEV_PID = '1536' # 普通话,支持简单的英文识别
|
||||||
|
|
||||||
|
file_url = file_url
|
||||||
|
token = token
|
||||||
|
# 以字节格式读取文件之后进行编码
|
||||||
|
with open(file_url, 'rb') as f:
|
||||||
|
speech = base64.b64encode(f.read()).decode('utf-8')
|
||||||
|
size = os.path.getsize(file_url) # 语音文件的字节数
|
||||||
|
headers = {'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'} # json格式post上传本地文件
|
||||||
|
url = 'https://vop.baidu.com/server_api'
|
||||||
|
data = {
|
||||||
|
"format": FORMAT, # 格式
|
||||||
|
"rate": RATE, # 取样频率,固定值16000
|
||||||
|
"dev_pid": DEV_PID, # 语音识别类型
|
||||||
|
"speech": speech, # 本地语音文件的二进制数据,需要进行base64编码
|
||||||
|
"cuid": CUID, # 用户唯一标识,用来区分用户 建议填写能区分用户的机器MAC地址或IMEI码,长度为60字符以内。
|
||||||
|
"len": size, # 语音文件的字节数
|
||||||
|
"channel": 1, # 声道数,仅支持单声道,固定值为1
|
||||||
|
"token": token,
|
||||||
|
}
|
||||||
|
req = requests.request("POST", url, data=json.dumps(data),
|
||||||
|
headers=headers) # request.post 改为requests.request("POST"……)
|
||||||
|
data_dict = json.loads(req.text)
|
||||||
|
# print(data_dict['result'][0])
|
||||||
|
return data_dict['result'][0] # 返回文本
|
||||||
|
except:
|
||||||
|
return '识别不清楚'
|
||||||
|
|
||||||
|
|
||||||
|
from ollama import Client
|
||||||
|
def get_completion(prompt, model="solar"):
|
||||||
|
client = Client(host='http://8.130.118.164:11434/')
|
||||||
|
response = client.chat(model, messages=[
|
||||||
|
{
|
||||||
|
'role': 'user',
|
||||||
|
'content': prompt,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
return response['message']['content']
|
||||||
|
|
||||||
|
|
||||||
|
# 5.文本转语音TTS:edge-tts
|
||||||
|
|
||||||
|
async def generate_audio_from_text(text, file_url):
|
||||||
|
"""
|
||||||
|
:param text:需要进行转换的文本
|
||||||
|
:file_url:转换后输出的音频文件地址
|
||||||
|
:return:无
|
||||||
|
"""
|
||||||
|
voice = 'zh-CN-YunxiNeural'
|
||||||
|
output = file_url
|
||||||
|
rate = '-4%'
|
||||||
|
volume = '+0%'
|
||||||
|
tts = edge_tts.Communicate(text=text, voice=voice, rate=rate, volume=volume)
|
||||||
|
await tts.save(output)
|
||||||
|
|
||||||
|
|
||||||
|
# 6.播放音频文件:pygame
|
||||||
|
def play_mp3(mp3_file):
|
||||||
|
"""
|
||||||
|
:param mp3_file:需要播放的录音文件地址
|
||||||
|
:return:无
|
||||||
|
"""
|
||||||
|
pygame.init() # 初始化pygame
|
||||||
|
pygame.mixer.init() # 初始化音频混合器
|
||||||
|
pygame.mixer.music.load(mp3_file) # 加载指定MP3文件
|
||||||
|
pygame.mixer.music.play() # 播放
|
||||||
|
clock = pygame.time.Clock()
|
||||||
|
while pygame.mixer.music.get_busy(): # 使用一个循环来等待音频播放完毕,保证程序不会在播放结束前退出
|
||||||
|
clock.tick(3)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
create_connection()
|
||||||
|
while True:
|
||||||
|
# 1. 提示用户发言
|
||||||
|
print('请发言,谢谢!')
|
||||||
|
# 2. 录制音频
|
||||||
|
audio_record(5, 'user_audio.wav')
|
||||||
|
|
||||||
|
print('结束发言')
|
||||||
|
# 3. 获取百度语音识别的access token
|
||||||
|
baidu_token = get_access_token()
|
||||||
|
print('Baidu access token obtained.')
|
||||||
|
|
||||||
|
# 4. 上传录音文件并进行语音识别
|
||||||
|
baidu_result = BaiduYuYin('./user_audio.wav', baidu_token)
|
||||||
|
print('Baidu speech recognition result:', baidu_result)
|
||||||
|
|
||||||
|
# 5. 调用大语言模型进行文本生成
|
||||||
|
model_response = get_completion(baidu_result)
|
||||||
|
print('Model response:', model_response)
|
||||||
|
|
||||||
|
# 6. 将文本转换为语音,保存到唯一的文件名
|
||||||
|
unique_audio_filename = "./audio/"+str(uuid.uuid4()) + '.mp3' # 保存为不同的文件名以避免访问冲突
|
||||||
|
asyncio.run(generate_audio_from_text(model_response, unique_audio_filename))
|
||||||
|
|
||||||
|
insert_data(baidu_result, model_response,unique_audio_filename)# 插入数据库
|
||||||
|
# 7. 播放生成的语音
|
||||||
|
play_mp3(unique_audio_filename)
|
||||||
|
|
||||||
|
# 8. 提示用户继续对话或退出
|
||||||
|
user_input = input('继续对话或输入"退出"退出: ')
|
||||||
|
if user_input == '退出':
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,11 @@
|
||||||
|
from ollama import Client
|
||||||
|
def get_completion(prompt, model="solar"):
|
||||||
|
client = Client(host='http://8.130.118.164:11434/')
|
||||||
|
response = client.chat(model, messages=[
|
||||||
|
{
|
||||||
|
'role': 'user',
|
||||||
|
'content': prompt,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
return response['message']['content']
|
||||||
|
print(get_completion("你好,我是小明。"))
|
Binary file not shown.
Loading…
Reference in New Issue