diff --git a/llm_adapters.py b/llm_adapters.py index f09d06b..525b117 100644 --- a/llm_adapters.py +++ b/llm_adapters.py @@ -3,6 +3,8 @@ import logging from typing import Optional from langchain_openai import ChatOpenAI, AzureChatOpenAI +from google import genai +from google.genai import types def ensure_openai_base_url_has_v1(url: str) -> str: import re @@ -77,6 +79,38 @@ class OpenAIAdapter(BaseLLMAdapter): return "" return response.content +class GeminiAdapter(BaseLLMAdapter): + """ + 适配 Google Gemini 接口 + """ + def __init__(self, api_key: str, model_name: str, max_tokens: int, temperature: float = 0.7, timeout: Optional[int] = 600): + self.api_key = api_key + self.model_name = model_name + self.max_tokens = max_tokens + self.temperature = temperature + self.timeout = timeout + + self._client = genai.Client(api_key=self.api_key) + + def invoke(self, prompt: str) -> str: + try: + response = self._client.models.generate_content( + model = self.model_name, + contents = prompt, + config = types.GenerateContentConfig( + max_output_tokens=self.max_tokens, + temperature=self.temperature, + ) + ) + if response and response.text: + return response.text + else: + logging.warning("No text response from Gemini API.") + return "" + except Exception as e: + logging.error(f"Gemini API 调用失败: {e}") + return "" + class AzureOpenAIAdapter(BaseLLMAdapter): """ 适配 Azure OpenAI 接口(使用 langchain.ChatOpenAI) @@ -190,5 +224,7 @@ def create_llm_adapter( return OllamaAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) elif interface_format.lower() == "ml studio": return MLStudioAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) + elif interface_format.lower() == "gemini": + return GeminiAdapter(api_key, model_name, max_tokens, temperature, timeout) else: raise ValueError(f"Unknown interface_format: {interface_format}") diff --git a/requirements.txt b/requirements.txt index 981d39b..3c699d8 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/tooltips.py b/tooltips.py index e62db78..f901ed2 100644 --- a/tooltips.py +++ b/tooltips.py @@ -3,8 +3,8 @@ tooltips = { "api_key": "在这里填写你的API Key。如果使用OpenAI官方接口,请在 https://platform.openai.com/account/api-keys 获取。", - "base_url": "模型的接口地址。若使用OpenAI官方:https://api.openai.com/v1。若使用Ollama本地部署,则类似 http://localhost:11434/v1。", - "interface_format": "指定LLM接口兼容格式,可选DeepSeek、OpenAI、Ollama、ML Studio等。\n\n注意:"+ + "base_url": "模型的接口地址。若使用OpenAI官方:https://api.openai.com/v1。若使用Ollama本地部署,则类似 http://localhost:11434/v1。调用Gemini模型则无需填写。", + "interface_format": "指定LLM接口兼容格式,可选DeepSeek、OpenAI、Ollama、ML Studio、Gemini等。\n\n注意:"+ "OpenAI 兼容是指的可以通过该标准请求的任何接口,不是只允许使用api.openai.com接口\n"+ "例如Ollama接口格式也兼容OpenAI,可以无需修改直接使用\n"+ "ML Studio接口格式与OpenAI接口格式也一致。", diff --git a/ui.py b/ui.py index 7538b97..8cfa772 100644 --- a/ui.py +++ b/ui.py @@ -373,6 +373,8 @@ class NovelGeneratorGUI: self.base_url_var.set("https://[az].openai.azure.com/openai/deployments/[model]/chat/completions?api-version=2024-08-01-preview") elif new_value == "DeepSeek": self.base_url_var.set("https://api.deepseek.com/v1") + elif new_value == "Gemini": + self.base_url_var.set("") # Gemini 通常不需要 Base URL,可以设置为空 for i in range(7): self.ai_config_tab.grid_rowconfigure(i, weight=0) @@ -413,7 +415,7 @@ class NovelGeneratorGUI: column=0, font=("Microsoft YaHei", 12) ) - interface_options = ["DeepSeek", "OpenAI", "Azure OpenAI", "Ollama", "ML Studio"] + interface_options = ["DeepSeek", "OpenAI", "Azure OpenAI", "Ollama", "ML Studio", "Gemini"] interface_dropdown = ctk.CTkOptionMenu( self.ai_config_tab, values=interface_options,