diff --git a/AI/WxMini/CallAi.py b/AI/WxMini/CallAi.py index 8af855ae..f14e88d8 100644 --- a/AI/WxMini/CallAi.py +++ b/AI/WxMini/CallAi.py @@ -1,9 +1,6 @@ from openai import OpenAI +from TtsConfig import * -# 阿里云中用来调用 deepseek v3 的密钥 -MODEL_API_KEY = "sk-01d13a39e09844038322108ecdbd1bbc" -MODEL_NAME = "qwen-plus" -#MODEL_NAME = "deepseek-v3" # 初始化 OpenAI 客户端 client = OpenAI( diff --git a/AI/WxMini/GetToken.py b/AI/WxMini/GetToken.py index 9b7c1697..32f96bf0 100644 --- a/AI/WxMini/GetToken.py +++ b/AI/WxMini/GetToken.py @@ -1,20 +1,19 @@ #! /usr/bin/env python # coding=utf-8 import json +from datetime import datetime from aliyunsdkcore.client import AcsClient from aliyunsdkcore.request import CommonRequest - -from Config import * +from TtsConfig import * def getToken(): # 创建AcsClient实例 client = AcsClient( - 'LTAI5t5jxkgJtRK8wew8fnbq', 'b8HXNGz7IkI3Dhv7BZx9BNBEZy1uku', - "cn-shanghai" + ACCESS_KEY_ID, ACCESS_KEY_SECRET, "cn-shanghai" ) - # 创建request,并设置参数。 + # 创建request,并设置参数 request = CommonRequest() request.set_method('POST') request.set_domain('nls-meta.cn-shanghai.aliyuncs.com') @@ -23,17 +22,23 @@ def getToken(): try: response = client.do_action_with_exception(request) - #print(response) - jss = json.loads(response) if 'Token' in jss and 'Id' in jss['Token']: token = jss['Token']['Id'] - #expireTime = jss['Token']['ExpireTime'] - #print("token = " + token) + expireTime = jss['Token']['ExpireTime'] + # 转换为本地时间 + expire_date = datetime.fromtimestamp(expireTime) + # 格式化输出 + formatted_date = expire_date.strftime("%Y-%m-%d %H:%M:%S") + print("过期时间:", formatted_date) + # 计算时间差(秒数) + now = datetime.now() + time_diff = (expire_date - now).total_seconds() + print("距离过期还有(秒):", time_diff) return token - #print("expireTime = " + str(expireTime)) except Exception as e: print(e) + if __name__ == '__main__': - print(getToken()) \ No newline at end of file + print(getToken()) diff --git a/AI/WxMini/OssUtil.py b/AI/WxMini/OssUtil.py new file mode 100644 index 00000000..9c7b6ad4 --- /dev/null +++ b/AI/WxMini/OssUtil.py @@ -0,0 +1,22 @@ +import oss2 +from TtsConfig import * + +# 初始化 OSS Bucket +auth = oss2.Auth(ACCESS_KEY_ID, ACCESS_KEY_SECRET) +bucket = oss2.Bucket(auth, ENDPOINT, BUCKET_NAME) + + +# 上传 MP3 文件到 OSS +def upload_mp3_to_oss(file_path, oss_object_name): + """ + 上传 MP3 文件到 OSS + :param file_path: 本地 MP3 文件路径 + :param oss_object_name: OSS 中存储的文件名 + """ + try: + # 上传文件 + with open(file_path, 'rb') as file: + bucket.put_object(oss_object_name, file) + print(f"文件 {file_path} 已成功上传到 OSS,存储为 {oss_object_name}") + except Exception as e: + print(f"上传失败: {e}") \ No newline at end of file diff --git a/AI/WxMini/RedisUtil.py b/AI/WxMini/RedisUtil.py new file mode 100644 index 00000000..62a5bc4d --- /dev/null +++ b/AI/WxMini/RedisUtil.py @@ -0,0 +1,65 @@ +import redis +from AlyConfig import * + +# 初始化 Redis 连接池 +pool = redis.ConnectionPool(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB, password=REDIS_PASSWORD) + +def set_tts_token(token, expire_time=750): + """ + 设置 TTS Token 到 Redis,并设置过期时间 + :param token: TTS Token 字符串 + :param expire_time: 过期时间(秒),默认 750 秒 + """ + try: + # 从连接池获取 Redis 连接 + r = redis.Redis(connection_pool=pool) + # 设置键值对,并设置过期时间 + r.set('tts_token', token, ex=expire_time) + print(f"TTS Token 已成功设置,过期时间为 {expire_time} 秒") + except Exception as e: + print(f"设置 TTS Token 失败: {e}") + + +def get_tts_token(): + """ + 从 Redis 获取 TTS Token + :return: TTS Token 字符串(如果不存在,返回 None) + """ + try: + # 从连接池获取 Redis 连接 + r = redis.Redis(connection_pool=pool) + # 获取 TTS Token + token = r.get('tts_token') + if token: + return token.decode('utf-8') # 将 bytes 转换为字符串 + else: + print("TTS Token 不存在或已过期") + return None + except Exception as e: + print(f"获取 TTS Token 失败: {e}") + return None + + +def close_redis_connection(): + """ + 显式关闭 Redis 连接池 + """ + try: + pool.disconnect() + print("Redis 连接池已关闭") + except Exception as e: + print(f"关闭 Redis 连接池失败: {e}") + + +if __name__ == '__main__': + # 示例:设置 TTS Token + tts_token = "your_tts_token_here" # 替换为你的 TTS Token + set_tts_token(tts_token) + + # 示例:获取 TTS Token + retrieved_token = get_tts_token() + if retrieved_token: + print("获取到的 TTS Token:", retrieved_token) + + # 显式关闭 Redis 连接池 + close_redis_connection() \ No newline at end of file diff --git a/AI/WxMini/Start.py b/AI/WxMini/Start.py new file mode 100644 index 00000000..f902013c --- /dev/null +++ b/AI/WxMini/Start.py @@ -0,0 +1,68 @@ +import os +import uuid + +from fastapi import FastAPI, Form, HTTPException +from openai import OpenAI +from TtsConfig import * +from WxMini.OssUtil import upload_mp3_to_oss +from WxMini.TTS import TTS + +# 初始化 FastAPI 应用 +app = FastAPI() + +# 初始化 OpenAI 客户端 +client = OpenAI( + api_key=MODEL_API_KEY, + base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", +) + + +@app.post("/reply") +async def reply(prompt: str = Form(...)): + """ + 接收用户输入的 prompt,调用大模型并返回结果 + :param prompt: 用户输入的 prompt + :return: 大模型的回复 + """ + try: + # 调用大模型 + response = client.chat.completions.create( + model=MODEL_NAME, + messages=[ + {"role": "system", "content": "你是一个非常好的聊天伙伴,可以疏导用户,帮他解压,一句控制在20字以内。"}, + {"role": "user", "content": prompt} + ], + max_tokens=500 + ) + + # 提取生成的回复 + if response.choices and response.choices[0].message.content: + result = response.choices[0].message.content.strip() + # 调用tts进行生成mp3 + # 生成一个uuid的文件名 + uuid_str = str(uuid.uuid4()) + tts_file = "audio/" + uuid_str + ".mp3" + t = TTS(tts_file) + t.start(result) + # 文件上传到oss + upload_mp3_to_oss(tts_file, tts_file) + # 删除临时文件 + try: + os.remove(tts_file) + print(f"临时文件 {tts_file} 已删除") + except Exception as e: + print(f"删除临时文件失败: {e}") + # 完整的url + url = 'https://ylt.oss-cn-hangzhou.aliyuncs.com/' + tts_file + return {"success": True, "url": url} + else: + raise HTTPException(status_code=500, detail="大模型未返回有效结果") + except Exception as e: + raise HTTPException(status_code=500, detail=f"调用大模型失败: {str(e)}") + + +# 运行 FastAPI 应用 +if __name__ == "__main__": + import uvicorn + + uvicorn.run(app, host="0.0.0.0", port=5500) diff --git a/AI/WxMini/TtsConfig.py b/AI/WxMini/TtsConfig.py new file mode 100644 index 00000000..bd57b404 --- /dev/null +++ b/AI/WxMini/TtsConfig.py @@ -0,0 +1,18 @@ +# 驿来特账号的AK,SK +ACCESS_KEY_ID = 'LTAI5t5jxkgJtRK8wew8fnbq' +ACCESS_KEY_SECRET = 'b8HXNGz7IkI3Dhv7BZx9BNBEZy1uku' + +ENDPOINT = 'https://oss-cn-hangzhou.aliyuncs.com' +BUCKET_NAME = 'ylt' + + +# Redis 配置 +REDIS_HOST = '10.10.14.14' # Redis 服务器地址 +REDIS_PORT = 18890 # Redis 端口 +REDIS_DB = 0 # Redis 数据库编号 +REDIS_PASSWORD = None # Redis 密码(如果没有密码,设置为 None) + +# 阿里云中用来调用 deepseek v3 的密钥 +MODEL_API_KEY = "sk-01d13a39e09844038322108ecdbd1bbc" +MODEL_NAME = "qwen-plus" +#MODEL_NAME = "deepseek-v3" \ No newline at end of file diff --git a/AI/WxMini/__pycache__/GetToken.cpython-310.pyc b/AI/WxMini/__pycache__/GetToken.cpython-310.pyc index 35337dd4..62caaaf7 100644 Binary files a/AI/WxMini/__pycache__/GetToken.cpython-310.pyc and b/AI/WxMini/__pycache__/GetToken.cpython-310.pyc differ diff --git a/AI/WxMini/__pycache__/OssUtil.cpython-310.pyc b/AI/WxMini/__pycache__/OssUtil.cpython-310.pyc new file mode 100644 index 00000000..c9a4253d Binary files /dev/null and b/AI/WxMini/__pycache__/OssUtil.cpython-310.pyc differ diff --git a/AI/WxMini/__pycache__/TTS.cpython-310.pyc b/AI/WxMini/__pycache__/TTS.cpython-310.pyc new file mode 100644 index 00000000..d8a9ba14 Binary files /dev/null and b/AI/WxMini/__pycache__/TTS.cpython-310.pyc differ diff --git a/AI/WxMini/__pycache__/TtsConfig.cpython-310.pyc b/AI/WxMini/__pycache__/TtsConfig.cpython-310.pyc new file mode 100644 index 00000000..ccbbcd7a Binary files /dev/null and b/AI/WxMini/__pycache__/TtsConfig.cpython-310.pyc differ diff --git a/AI/WxMini/audio/9eeb73db-e0af-44f6-8e9d-d25f896ec841.mp3 b/AI/WxMini/audio/9eeb73db-e0af-44f6-8e9d-d25f896ec841.mp3 new file mode 100644 index 00000000..88fd6ffb Binary files /dev/null and b/AI/WxMini/audio/9eeb73db-e0af-44f6-8e9d-d25f896ec841.mp3 differ diff --git a/AI/WxMini/audio/f3062647-9282-408e-a7b9-3145b9d84dd6.mp3 b/AI/WxMini/audio/f3062647-9282-408e-a7b9-3145b9d84dd6.mp3 new file mode 100644 index 00000000..491861ca Binary files /dev/null and b/AI/WxMini/audio/f3062647-9282-408e-a7b9-3145b9d84dd6.mp3 differ diff --git a/AI/WxMini/tests/test_tts.mp3 b/AI/WxMini/tests/test_tts.mp3 deleted file mode 100644 index bebdc693..00000000 Binary files a/AI/WxMini/tests/test_tts.mp3 and /dev/null differ diff --git a/AI/WxMini/tts.py b/AI/WxMini/tts.py index 03602845..3450d794 100644 --- a/AI/WxMini/tts.py +++ b/AI/WxMini/tts.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import threading import nls import os from GetToken import * @@ -9,10 +8,8 @@ TOKEN = getToken() # 参考https://help.aliyun.com/document_detail/450255.html APPKEY = "90RJcqjlN4ZqymGd" # 获取Appkey请前往控制台:https://nls-portal.console.aliyun.com/applist - class TTS: - def __init__(self, _file): - self._th = threading.Thread(target=self._run) + def __init__(self, _file): self._file = _file self._f = None @@ -21,17 +18,8 @@ class TTS: # 确保目录存在 os.makedirs(os.path.dirname(self._file), exist_ok=True) self._f = open(self._file, "wb") - self._th.start() - - def on_close(self, *args): - if self._f: - self._f.close() - - def on_data(self, data, *args): - if self._f: - self._f.write(data) - def _run(self): + # 初始化 TTS tts = nls.NlsSpeechSynthesizer( url=URL, token=TOKEN, @@ -39,10 +27,21 @@ class TTS: on_data=self.on_data, on_close=self.on_close ) + + # 同步执行 TTS 生成 tts.start(self._text, voice="xiaobei", aformat="mp3") + def on_close(self, *args): + if self._f: + self._f.close() + print("TTS 生成完成,文件已关闭") + + def on_data(self, data, *args): + if self._f: + self._f.write(data) + if __name__ == '__main__': - TEXT = '大壮正想去摘取花瓣,谁知阿丽和阿强突然内讧' + TEXT = '你好,听到你心情不好我很抱歉。可以告诉我是什么让你感到如此糟糕吗?有时候把烦恼说出来会让我们感觉好一些。无论发生了什么,我都会在这里认真倾听,和你一起面对。记住,每个困难都会过去,明天总是新的开始。' t = TTS("tests/test_tts.mp3") - t.start(TEXT) + t.start(TEXT) \ No newline at end of file