From c1d5a0458874db2e4b7bec324b2771650e3eb7cb Mon Sep 17 00:00:00 2001 From: huanshang141 <1375436987@qq.com> Date: Thu, 13 Feb 2025 23:50:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9AzureAI?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- llm_adapters.py | 55 +++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | Bin 394 -> 434 bytes ui.py | 10 +++++---- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/llm_adapters.py b/llm_adapters.py index 6437509..ab1ad16 100644 --- a/llm_adapters.py +++ b/llm_adapters.py @@ -5,6 +5,9 @@ from typing import Optional from langchain_openai import ChatOpenAI, AzureChatOpenAI from google import genai from google.genai import types +from azure.ai.inference import ChatCompletionsClient +from azure.core.credentials import AzureKeyCredential +from azure.ai.inference.models import SystemMessage, UserMessage def ensure_openai_base_url_has_v1(url: str) -> str: import re @@ -201,6 +204,56 @@ class MLStudioAdapter(BaseLLMAdapter): return "" return response.content +class AzureAIAdapter(BaseLLMAdapter): + """ + 适配 Azure AI Inference 接口,用于访问Azure AI服务部署的模型 + 使用 azure-ai-inference 库进行API调用 + """ + def __init__(self, api_key: str, base_url: str, model_name: str, max_tokens: int, temperature: float = 0.7, timeout: Optional[int] = 600): + import re + # 匹配形如 https://xxx.services.ai.azure.com/models/chat/completions?api-version=xxx 的URL + match = re.match(r'https://(.+?)\.services\.ai\.azure\.com(?:/models)?(?:/chat/completions)?(?:\?api-version=(.+))?', base_url) + if match: + # endpoint需要是形如 https://xxx.services.ai.azure.com/models 的格式 + self.endpoint = f"https://{match.group(1)}.services.ai.azure.com/models" + # 如果URL中包含api-version参数,使用它;否则使用默认值 + self.api_version = match.group(2) if match.group(2) else "2024-05-01-preview" + else: + raise ValueError("Invalid Azure AI base_url format. Expected format: https://.services.ai.azure.com/models/chat/completions?api-version=xxx") + + self.base_url = self.endpoint # 存储处理后的endpoint URL + self.api_key = api_key + self.model_name = model_name + self.max_tokens = max_tokens + self.temperature = temperature + self.timeout = timeout + + self._client = ChatCompletionsClient( + endpoint=self.endpoint, + credential=AzureKeyCredential(self.api_key), + model=self.model_name, + temperature=self.temperature, + max_tokens=self.max_tokens, + timeout=self.timeout + ) + + def invoke(self, prompt: str) -> str: + try: + response = self._client.complete( + messages=[ + SystemMessage("You are a helpful assistant."), + UserMessage(prompt) + ] + ) + if response and response.choices: + return response.choices[0].message.content + else: + logging.warning("No response from AzureAIAdapter.") + return "" + except Exception as e: + logging.error(f"Azure AI Inference API 调用失败: {e}") + return "" + def create_llm_adapter( interface_format: str, base_url: str, @@ -220,6 +273,8 @@ def create_llm_adapter( return OpenAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) elif fmt == "azure openai": return AzureOpenAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) + elif fmt == "azure ai": + return AzureAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) elif fmt == "ollama": return OllamaAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) elif fmt == "ml studio": diff --git a/requirements.txt b/requirements.txt index e9bd518d242f876ffa070eacf41d9667ff35d600..4eae4bf89df35570bc42d71be889d42b2a669f21 100644 GIT binary patch delta 48 wcmeBT-o(72i&2A@fr}xLp^BlDp@<=sK^Mr*1d^Eyc?@YlK1eK&AsI*l0RGSkw*UYD delta 7 OcmdnQ+{L`1ixB_{+5&z6 diff --git a/ui.py b/ui.py index ed88749..064d048 100644 --- a/ui.py +++ b/ui.py @@ -205,7 +205,7 @@ class NovelGeneratorGUI: # ----------------- 主Tab布局 ----------------- def build_main_tab(self): """ - 主Tab包含左侧的“本章内容”编辑框和输出日志,以及右侧的主要操作和参数设置区 + 主Tab包含左侧的"本章内容"编辑框和输出日志,以及右侧的主要操作和参数设置区 """ self.main_tab.rowconfigure(0, weight=1) self.main_tab.columnconfigure(0, weight=1) @@ -321,7 +321,7 @@ class NovelGeneratorGUI: self.build_ai_config_tab() self.build_embeddings_config_tab() - # 底部的“保存配置”和“加载配置”按钮 + # 底部的"保存配置"和"加载配置"按钮 self.btn_frame_config = ctk.CTkFrame(self.config_frame) self.btn_frame_config.grid(row=1, column=0, padx=5, pady=5, sticky="ew") self.btn_frame_config.columnconfigure(0, weight=1) @@ -336,7 +336,7 @@ class NovelGeneratorGUI: def create_label_with_help(self, parent, label_text, tooltip_key, row, column, font=None, sticky="e", padx=5, pady=5): """ - 封装一个带“?”按钮的Label,用于展示提示信息。 + 封装一个带"?"按钮的Label,用于展示提示信息。 """ frame = ctk.CTkFrame(parent) frame.grid(row=row, column=column, padx=padx, pady=pady, sticky=sticky) @@ -375,6 +375,8 @@ class NovelGeneratorGUI: self.base_url_var.set("https://api.deepseek.com/v1") elif new_value == "Gemini": self.base_url_var.set("") # Gemini 通常不需要 Base URL,可以设置为空 + elif new_value == "Azure AI": + self.base_url_var.set("https://.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview") for i in range(7): self.ai_config_tab.grid_rowconfigure(i, weight=0) @@ -415,7 +417,7 @@ class NovelGeneratorGUI: column=0, font=("Microsoft YaHei", 12) ) - interface_options = ["DeepSeek", "OpenAI", "Azure OpenAI", "Ollama", "ML Studio", "Gemini"] + interface_options = ["DeepSeek", "OpenAI", "Azure OpenAI", "Azure AI", "Ollama", "ML Studio", "Gemini"] interface_dropdown = ctk.CTkOptionMenu( self.ai_config_tab, values=interface_options,