mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
186 lines
6.5 KiB
Python
186 lines
6.5 KiB
Python
import json
|
|
import logging
|
|
import openai
|
|
import requests
|
|
from datetime import datetime
|
|
from core.utils.util import is_segment
|
|
from core.utils.util import get_string_no_punctuation_or_emoji
|
|
from core.utils.util import read_config, get_project_dir
|
|
from abc import ABC, abstractmethod
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class LLM(ABC):
|
|
def __init__(self, config):
|
|
api_key = config.get("api_key")
|
|
# 如果api_key包含中文“你”
|
|
if "你" in api_key:
|
|
logger.error("你还没配置LLM的密钥,请在配置文件中配置密钥,否则无法正常工作")
|
|
pass
|
|
|
|
@abstractmethod
|
|
def response(self, session_id, dialogue):
|
|
"""LLM response generator"""
|
|
pass
|
|
|
|
|
|
class DeepSeekLLM(LLM):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.model_name = config.get("model_name")
|
|
self.api_key = config.get("api_key")
|
|
self.base_url = config.get("url")
|
|
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
|
|
def response(self, session_id, dialogue):
|
|
logger.info(f"Generating response using {dialogue}")
|
|
try:
|
|
responses = self.client.chat.completions.create(
|
|
model=self.model_name,
|
|
messages=dialogue,
|
|
stream=True
|
|
)
|
|
for chunk in responses:
|
|
# 检查是否存在有效的choice且content不为空
|
|
if chunk.choices and len(chunk.choices) > 0:
|
|
delta = chunk.choices[0].delta
|
|
content = getattr(delta, 'content', '')
|
|
if content: # 仅在content非空时生成
|
|
yield content
|
|
except Exception as e:
|
|
logger.error(f"Error in response generation: {e}")
|
|
|
|
|
|
class ChatGLMLLM(LLM):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.model_name = config.get("model_name")
|
|
self.api_key = config.get("api_key")
|
|
self.base_url = config.get("url")
|
|
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
|
|
def response(self, session_id, dialogue):
|
|
try:
|
|
responses = self.client.chat.completions.create(
|
|
model=self.model_name,
|
|
messages=dialogue,
|
|
stream=True
|
|
)
|
|
for chunk in responses:
|
|
# 检查是否存在有效的choice且content不为空
|
|
if chunk.choices and len(chunk.choices) > 0:
|
|
delta = chunk.choices[0].delta
|
|
content = getattr(delta, 'content', '')
|
|
if content: # 仅在content非空时生成
|
|
yield content
|
|
except Exception as e:
|
|
logger.error(f"Error in response generation: {e}")
|
|
|
|
class AliLLM(LLM):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.model_name = config.get("model_name")
|
|
self.api_key = config.get("api_key")
|
|
self.base_url = config.get("base_url")
|
|
self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url)
|
|
|
|
def response(self, session_id, dialogue):
|
|
try:
|
|
responses = self.client.chat.completions.create(
|
|
model=self.model_name,
|
|
messages=dialogue,
|
|
stream=True
|
|
)
|
|
for chunk in responses:
|
|
# 检查是否存在有效的choice且content不为空
|
|
if chunk.choices and len(chunk.choices) > 0:
|
|
delta = chunk.choices[0].delta
|
|
content = getattr(delta, 'content', '')
|
|
if content: # 仅在content非空时生成
|
|
yield content
|
|
except Exception as e:
|
|
logger.error(f"Error in response generation: {e}")
|
|
|
|
class DifyLLM(LLM):
|
|
def __init__(self, config):
|
|
super().__init__(config)
|
|
self.api_key = config["api_key"]
|
|
self.base_url = config.get("base_url", "https://api.dify.ai/v1").rstrip('/')
|
|
|
|
def response(self, session_id, dialogue):
|
|
try:
|
|
# 取最后一条用户消息
|
|
last_msg = next(m for m in reversed(dialogue) if m["role"] == "user")
|
|
|
|
# 发起流式请求
|
|
with requests.post(
|
|
f"{self.base_url}/chat-messages",
|
|
headers={"Authorization": f"Bearer {self.api_key}"},
|
|
json={
|
|
"query": last_msg["content"],
|
|
"response_mode": "streaming",
|
|
"user": session_id,
|
|
"inputs": {}
|
|
},
|
|
stream=True
|
|
) as r:
|
|
for line in r.iter_lines():
|
|
if line.startswith(b'data: '):
|
|
event = json.loads(line[6:])
|
|
if event.get('answer'):
|
|
yield event['answer']
|
|
|
|
except Exception:
|
|
yield "【服务响应异常】"
|
|
|
|
|
|
def create_instance(class_name, *args, **kwargs):
|
|
# 获取类对象
|
|
cls_map = {
|
|
"DeepSeekLLM": DeepSeekLLM,
|
|
"ChatGLMLLM": ChatGLMLLM,
|
|
"DifyLLM": DifyLLM,
|
|
"AliLLM": AliLLM,
|
|
# 可扩展其他LLM实现
|
|
}
|
|
|
|
if cls := cls_map.get(class_name):
|
|
return cls(*args, **kwargs)
|
|
raise ValueError(f"不支持的LLM类型: {class_name}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
"""
|
|
响应速度测试
|
|
"""
|
|
config = read_config(get_project_dir() + "config.yaml")
|
|
llm = create_instance(
|
|
config["selected_module"]["LLM"],
|
|
config["LLM"][config["selected_module"]["LLM"]]
|
|
)
|
|
|
|
start_time = datetime.now()
|
|
|
|
dialogue = []
|
|
dialogue.append({"role": "system", "content": config.get("prompt")})
|
|
dialogue.append({"role": "user", "content": "你好小智"})
|
|
llm_responses = llm.response("test", dialogue)
|
|
response_message = []
|
|
first_text = None
|
|
start = 0
|
|
|
|
for content in llm_responses:
|
|
response_message.append(content)
|
|
|
|
if is_segment(response_message):
|
|
segment_text = "".join(response_message[start:])
|
|
segment_text = get_string_no_punctuation_or_emoji(segment_text)
|
|
if len(segment_text) > 0:
|
|
if first_text is None:
|
|
first_text = segment_text
|
|
print("大模型首次返回耗时:" + str(datetime.now() - start_time))
|
|
start = len(response_message)
|
|
|
|
print("大模型返回总耗时:" + str(datetime.now() - start_time))
|