From 2c1fc30bfb2b562fea63b77ae152b99dc43ffc08 Mon Sep 17 00:00:00 2001 From: aileenfun Date: Tue, 18 Mar 2025 00:53:03 +0800 Subject: [PATCH 1/7] =?UTF-8?q?gemini=E5=8F=AF=E4=BB=A5=E5=8D=95=E7=8B=AC?= =?UTF-8?q?=E8=B5=B0=E4=BB=A3=E7=90=86=EF=BC=8C=E9=81=BF=E5=85=8D=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E4=BB=A3=E7=90=86=E3=80=82=20=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E5=AE=B9=E6=98=93=E8=AE=A9=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E4=B8=AD=E5=85=B6=E4=BB=96=E6=A8=A1=E5=9D=97=E4=B9=9F=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E4=BB=A3=E7=90=86=EF=BC=8C=E9=80=A0=E6=88=90=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=AE=BF=E9=97=AE=E7=9A=84=E9=97=AE=E9=A2=98=E3=80=82?= =?UTF-8?q?=20=E6=B5=8B=E8=AF=95=E6=9C=80=E6=96=B0=E7=9A=84=20gemini=20fla?= =?UTF-8?q?sh=202.0=E5=8F=AF=E4=BB=A5=E4=BD=BF=E7=94=A8=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 4 +- .../core/providers/llm/gemini/gemini.py | 81 +++++++++++++------ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 21f37c5b..cc2545e3 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -193,7 +193,9 @@ LLM: # token申请地址: https://aistudio.google.com/apikey # 若部署地无法访问接口,需要开启科学上网 api_key: 你的gemini web key - model_name: "gemini-1.5-pro" # gemini-1.5-pro 是免费的 + model_name: "gemini-2.0-flash" + http_proxy: "" #"http://127.0.0.1:10808" + https_proxy: "" #http://127.0.0.1:10808" CozeLLM: # 定义LLM API类型 type: coze diff --git a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py index f753ac3e..e559bd8e 100644 --- a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py +++ b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py @@ -1,14 +1,19 @@ import google.generativeai as genai from core.utils.util import check_model_key from core.providers.llm.base import LLMProviderBase - +from config.logger import setup_logging +import requests +import json +TAG = __name__ +logger = setup_logging() class LLMProvider(LLMProviderBase): def __init__(self, config): """初始化Gemini LLM Provider""" self.model_name = config.get("model_name", "gemini-1.5-pro") self.api_key = config.get("api_key") - + self.http_proxy=config.get("http_proxy") + self.https_proxy = config.get("https_proxy") have_key = check_model_key("LLM", self.api_key) if not have_key: @@ -16,6 +21,19 @@ class LLMProvider(LLMProviderBase): try: # 初始化Gemini客户端 + # 配置代理(如果提供了代理配置) + self.proxies=None + if self.http_proxy is not "" or self.https_proxy is not "": + + self.proxies = { + "http": self.http_proxy, + "https": self.https_proxy, + } + logger.bind(tag=TAG).info(f"Gemini set proxys:{self.proxies}") + # 使用猴子补丁修改 google-generativeai 库的请求会话 + + # 使用 session 对象配置 genai + genai.configure(api_key=self.api_key) self.model = genai.GenerativeModel(self.model_name) @@ -46,35 +64,48 @@ class LLMProvider(LLMProviderBase): if content: chat_history.append({ "role": role, - "parts": [content] + "parts": [{"text":content}] + }) # 获取当前消息 current_msg = dialogue[-1]["content"] - # 创建新的聊天会话 - chat = self.model.start_chat(history=chat_history) + # 构建请求体 + request_body = { + "contents": chat_history + [{"role": "user", "parts": [{"text":current_msg}]}], + "generationConfig": self.generation_config + } - # 发送消息并获取流式响应 - response = chat.send_message( - current_msg, - stream=True, - generation_config=self.generation_config - ) + # 构建请求URL + url = f"https://generativelanguage.googleapis.com/v1beta/models/{self.model_name}:generateContent?key={self.api_key}" - # 处理流式响应 - for chunk in response: - if hasattr(chunk, 'text') and chunk.text: - yield chunk.text + # 构建请求头 + headers = { + "Content-Type": "application/json", + } - except Exception as e: - error_msg = str(e) - logger.bind(tag=TAG).error(f"Gemini响应生成错误: {error_msg}") - - # 针对不同错误返回友好提示 - if "Rate limit" in error_msg: - yield "【Gemini服务请求太频繁,请稍后再试】" - elif "Invalid API key" in error_msg: - yield "【Gemini API key无效】" + # 发送POST请求,经测试手动 request 无法使用 stream 模式,但是文本消息其实也很快,stream 与否也无所谓 + if self.proxies: + response = requests.post(url, headers=headers, json=request_body, stream=False, proxies=self.proxies) else: - yield f"【Gemini服务响应异常: {error_msg}】" + response = requests.post(url, headers=headers, json=request_body, stream=False) + response.raise_for_status() + try: + data = response.json() # 直接解析JSON + if 'candidates' in data and data['candidates']: + yield data['candidates'][0]['content']['parts'][0]['text'] + else: + yield "未找到候选回复。" + except json.JSONDecodeError as e: + yield f"JSON解码错误:{e}" + except Exception as e: + yield f"发生错误:{e}" + + + except requests.exceptions.RequestException as e: + yield f"请求失败:{e}" + except json.JSONDecodeError as e: + yield f"JSON解码错误:{e}" + except Exception as e: + yield f"发生错误:{e}" From 3d97fab16d94a3374eac3c64b0baa888f9102b5a Mon Sep 17 00:00:00 2001 From: HaoDuoYu <63943547+diaoling6665@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:05:33 +0800 Subject: [PATCH 2/7] Add files via upload --- .../core/providers/llm/openai/openai.py | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/openai/openai.py b/main/xiaozhi-server/core/providers/llm/openai/openai.py index 95cd85f9..2feb4847 100644 --- a/main/xiaozhi-server/core/providers/llm/openai/openai.py +++ b/main/xiaozhi-server/core/providers/llm/openai/openai.py @@ -7,19 +7,27 @@ class LLMProvider(LLMProviderBase): def __init__(self, config): self.model_name = config.get("model_name") self.api_key = config.get("api_key") - if 'base_url' in config: - self.base_url = config.get("base_url") - else: - self.base_url = config.get("url") + self.url = config.get("url") + self.top_p = config.get("top_p") + self.top_k = config.get("top_k") + self.temperature = config.get("temperature") + self.max_tokens = config.get("max_tokens") + self.frequency_penalty = config.get("frequency_penalty") + check_model_key("LLM", self.api_key) - self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url) + self.client = openai.OpenAI(api_key=self.api_key, base_url=self.url) def response(self, session_id, dialogue): try: responses = self.client.chat.completions.create( model=self.model_name, messages=dialogue, - stream=True + stream=True, + temperature=self.temperature, + max_tokens=self.max_tokens, + top_p=self.top_p, + top_k=self.top_k, + frequency_penalty=self.frequency_penalty ) is_active = True @@ -51,6 +59,11 @@ class LLMProvider(LLMProviderBase): messages=dialogue, stream=True, tools=functions, + temperature=self.temperature, + max_tokens=self.max_tokens, + top_p=self.top_p, + top_k=self.top_k, + frequency_penalty=self.frequency_penalty ) for chunk in stream: From f5d9b478d57c4c4b07e428e052a3bc2d40ffb0cd Mon Sep 17 00:00:00 2001 From: HaoDuoYu <63943547+diaoling6665@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:07:53 +0800 Subject: [PATCH 3/7] Update config.yaml --- main/xiaozhi-server/config.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 21f37c5b..fcae7485 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -141,6 +141,8 @@ VAD: min_silence_duration_ms: 700 # 如果说话停顿比较长,可以把这个值设置大一些 LLM: + #所有openai类型均可以修改超参,以AliLLM为例 + # 当前支持的type为openai、dify、ollama,可自行适配 AliLLM: # 定义LLM API类型 @@ -149,6 +151,11 @@ LLM: base_url: https://dashscope.aliyuncs.com/compatible-mode/v1 model_name: qwen-turbo api_key: 你的deepseek web key + temperature: 0.7 # 温度值 + max_tokens: 500 # 最大生成token数 + top_p: 1 + top_k: 50 + frequency_penalty: 0 # 频率惩罚 DoubaoLLM: # 定义LLM API类型 type: openai @@ -469,4 +476,4 @@ manager: enabled: false ip: 0.0.0.0 port: 8002 -use_private_config: false \ No newline at end of file +use_private_config: false From 34fa65132d8b13e651b668093faf6d210a74477a Mon Sep 17 00:00:00 2001 From: aileenfun Date: Tue, 18 Mar 2025 21:04:21 +0800 Subject: [PATCH 4/7] =?UTF-8?q?gemini=E7=8B=AC=E7=AB=8B=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=20=E5=9C=A8=E4=BB=A3=E7=90=86=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E6=89=8B=E5=8A=A8=E7=BB=84=E6=88=90request?= =?UTF-8?q?=EF=BC=8C=E6=97=A0=E8=AE=BA=20Steam=20=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E4=B8=BA=20True=EF=BC=8C=E8=BF=94=E5=9B=9E=E7=9A=84=E5=9D=87?= =?UTF-8?q?=E6=98=AF=E9=9D=9Esteam=E5=80=BC=E3=80=82=20=E4=B8=8D=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=BB=A3=E7=90=86=E6=97=B6=E8=87=AA=E5=8A=A8=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E4=B8=BAgoogle=E7=9A=84=E5=AE=98=E6=96=B9api=EF=BC=8C?= =?UTF-8?q?=E8=B5=B0=20stream=E6=A8=A1=E5=BC=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 全局代理容易让系统中其他模块也使用代理,造成无法访问的问题。 测试最新的 gemini flash 2.0可以使用。 --- .../core/providers/llm/gemini/gemini.py | 52 ++++++++++++++----- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py index e559bd8e..298bf71e 100644 --- a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py +++ b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py @@ -85,22 +85,48 @@ class LLMProvider(LLMProviderBase): "Content-Type": "application/json", } - # 发送POST请求,经测试手动 request 无法使用 stream 模式,但是文本消息其实也很快,stream 与否也无所谓 + # 发送POST请求,经测试手动 request 无法使用 stream 模式 if self.proxies: + logger.bind(tag=TAG).info(f"Gemini response mode ") response = requests.post(url, headers=headers, json=request_body, stream=False, proxies=self.proxies) + try: + data = response.json() # 直接解析JSON + if 'candidates' in data and data['candidates']: + yield data['candidates'][0]['content']['parts'][0]['text'] + else: + yield "未找到候选回复。" + except json.JSONDecodeError as e: + yield f"JSON解码错误:{e}" + except Exception as e: + yield f"发生错误:{e}" else: - response = requests.post(url, headers=headers, json=request_body, stream=False) - response.raise_for_status() - try: - data = response.json() # 直接解析JSON - if 'candidates' in data and data['candidates']: - yield data['candidates'][0]['content']['parts'][0]['text'] - else: - yield "未找到候选回复。" - except json.JSONDecodeError as e: - yield f"JSON解码错误:{e}" - except Exception as e: - yield f"发生错误:{e}" + logger.bind(tag=TAG).info(f"Gemini stream mode ") + chat = self.model.start_chat(history=chat_history) + + # 发送消息并获取流式响应 + response = chat.send_message( + current_msg, + stream=True, + generation_config=self.generation_config + ) + # 处理流式响应 + for chunk in response: + if hasattr(chunk, 'text') and chunk.text: + yield chunk.text + + except Exception as e: + error_msg = str(e) + logger.bind(tag=TAG).error(f"Gemini响应生成错误: {error_msg}") + + # 针对不同错误返回友好提示 + if "Rate limit" in error_msg: + yield "【Gemini服务请求太频繁,请稍后再试】" + elif "Invalid API key" in error_msg: + yield "【Gemini API key无效】" + else: + yield f"【Gemini服务响应异常: {error_msg}】" + + except requests.exceptions.RequestException as e: From 9a96223d12fbd1c41beb300fe8679077465809b8 Mon Sep 17 00:00:00 2001 From: Huang <32005838+openrz@users.noreply.github.com> Date: Wed, 19 Mar 2025 22:21:11 +0800 Subject: [PATCH 5/7] =?UTF-8?q?fix:=E9=83=A8=E5=88=86=E6=94=AF=E6=8C=81ope?= =?UTF-8?q?nai=E7=9A=84LLM=E7=9A=84url=E9=85=8D=E7=BD=AE=E5=90=8D=E4=B8=8D?= =?UTF-8?q?=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/providers/llm/openai/openai.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/openai/openai.py b/main/xiaozhi-server/core/providers/llm/openai/openai.py index 2feb4847..d4e21d5f 100644 --- a/main/xiaozhi-server/core/providers/llm/openai/openai.py +++ b/main/xiaozhi-server/core/providers/llm/openai/openai.py @@ -7,7 +7,10 @@ class LLMProvider(LLMProviderBase): def __init__(self, config): self.model_name = config.get("model_name") self.api_key = config.get("api_key") - self.url = config.get("url") + if 'base_url' in config: + self.base_url = config.get("base_url") + else: + self.base_url = config.get("url") self.top_p = config.get("top_p") self.top_k = config.get("top_k") self.temperature = config.get("temperature") @@ -15,7 +18,7 @@ class LLMProvider(LLMProviderBase): self.frequency_penalty = config.get("frequency_penalty") check_model_key("LLM", self.api_key) - self.client = openai.OpenAI(api_key=self.api_key, base_url=self.url) + self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url) def response(self, session_id, dialogue): try: @@ -71,4 +74,4 @@ class LLMProvider(LLMProviderBase): except Exception as e: self.logger.bind(tag=TAG).error(f"Error in function call streaming: {e}") - yield {"type": "content", "content": f"【OpenAI服务响应异常: {e}】"} \ No newline at end of file + yield {"type": "content", "content": f"【OpenAI服务响应异常: {e}】"} From 08093f509ae6772a4b9499a4e8b8854c5c8f561f Mon Sep 17 00:00:00 2001 From: Huang <32005838+openrz@users.noreply.github.com> Date: Wed, 19 Mar 2025 22:39:23 +0800 Subject: [PATCH 6/7] =?UTF-8?q?update:=E4=BC=98=E5=8C=96=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 2b404b32..f7922c51 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -138,8 +138,7 @@ VAD: min_silence_duration_ms: 700 # 如果说话停顿比较长,可以把这个值设置大一些 LLM: - #所有openai类型均可以修改超参,以AliLLM为例 - + # 所有openai类型均可以修改超参,以AliLLM为例 # 当前支持的type为openai、dify、ollama,可自行适配 AliLLM: # 定义LLM API类型 From 80ffa7a577d476f3ad8adfb467e4253c3fc55b01 Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Thu, 20 Mar 2025 08:57:39 +0800 Subject: [PATCH 7/7] =?UTF-8?q?updtge:=E4=BC=98=E5=8C=96=E8=B6=85=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/providers/llm/gemini/gemini.py | 1 - .../core/providers/llm/openai/openai.py | 29 +++++++------------ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py index 298bf71e..93c7dcb5 100644 --- a/main/xiaozhi-server/core/providers/llm/gemini/gemini.py +++ b/main/xiaozhi-server/core/providers/llm/gemini/gemini.py @@ -87,7 +87,6 @@ class LLMProvider(LLMProviderBase): # 发送POST请求,经测试手动 request 无法使用 stream 模式 if self.proxies: - logger.bind(tag=TAG).info(f"Gemini response mode ") response = requests.post(url, headers=headers, json=request_body, stream=False, proxies=self.proxies) try: data = response.json() # 直接解析JSON diff --git a/main/xiaozhi-server/core/providers/llm/openai/openai.py b/main/xiaozhi-server/core/providers/llm/openai/openai.py index d4e21d5f..b1a9a24e 100644 --- a/main/xiaozhi-server/core/providers/llm/openai/openai.py +++ b/main/xiaozhi-server/core/providers/llm/openai/openai.py @@ -1,7 +1,11 @@ import openai +from config.logger import setup_logging from core.utils.util import check_model_key from core.providers.llm.base import LLMProviderBase +TAG = __name__ +logger = setup_logging() + class LLMProvider(LLMProviderBase): def __init__(self, config): @@ -11,12 +15,8 @@ class LLMProvider(LLMProviderBase): self.base_url = config.get("base_url") else: self.base_url = config.get("url") - self.top_p = config.get("top_p") - self.top_k = config.get("top_k") - self.temperature = config.get("temperature") - self.max_tokens = config.get("max_tokens") - self.frequency_penalty = config.get("frequency_penalty") - + self.max_tokens = config.get("max_tokens", 500) + check_model_key("LLM", self.api_key) self.client = openai.OpenAI(api_key=self.api_key, base_url=self.base_url) @@ -26,13 +26,9 @@ class LLMProvider(LLMProviderBase): model=self.model_name, messages=dialogue, stream=True, - temperature=self.temperature, max_tokens=self.max_tokens, - top_p=self.top_p, - top_k=self.top_k, - frequency_penalty=self.frequency_penalty ) - + is_active = True for chunk in responses: try: @@ -61,17 +57,12 @@ class LLMProvider(LLMProviderBase): model=self.model_name, messages=dialogue, stream=True, - tools=functions, - temperature=self.temperature, - max_tokens=self.max_tokens, - top_p=self.top_p, - top_k=self.top_k, - frequency_penalty=self.frequency_penalty + tools=functions ) - + for chunk in stream: yield chunk.choices[0].delta.content, chunk.choices[0].delta.tool_calls - + except Exception as e: self.logger.bind(tag=TAG).error(f"Error in function call streaming: {e}") yield {"type": "content", "content": f"【OpenAI服务响应异常: {e}】"}