Merge pull request #106 from huanshang141/Azure-api-support

增加了对AzureAI的支持
This commit is contained in:
IdleCloud
2025-02-15 11:56:33 +08:00
committed by GitHub
3 changed files with 61 additions and 4 deletions
+55
View File
@@ -5,6 +5,9 @@ from typing import Optional
from langchain_openai import ChatOpenAI, AzureChatOpenAI from langchain_openai import ChatOpenAI, AzureChatOpenAI
from google import genai from google import genai
from google.genai import types 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: def ensure_openai_base_url_has_v1(url: str) -> str:
import re import re
@@ -201,6 +204,56 @@ class MLStudioAdapter(BaseLLMAdapter):
return "" return ""
return response.content 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://<endpoint>.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( def create_llm_adapter(
interface_format: str, interface_format: str,
base_url: str, base_url: str,
@@ -220,6 +273,8 @@ def create_llm_adapter(
return OpenAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) return OpenAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout)
elif fmt == "azure openai": elif fmt == "azure openai":
return AzureOpenAIAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) 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": elif fmt == "ollama":
return OllamaAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout) return OllamaAdapter(api_key, base_url, model_name, max_tokens, temperature, timeout)
elif fmt == "ml studio": elif fmt == "ml studio":
BIN
View File
Binary file not shown.
+6 -4
View File
@@ -205,7 +205,7 @@ class NovelGeneratorGUI:
# ----------------- 主Tab布局 ----------------- # ----------------- 主Tab布局 -----------------
def build_main_tab(self): def build_main_tab(self):
""" """
主Tab包含左侧的“本章内容”编辑框和输出日志,以及右侧的主要操作和参数设置区 主Tab包含左侧的"本章内容"编辑框和输出日志,以及右侧的主要操作和参数设置区
""" """
self.main_tab.rowconfigure(0, weight=1) self.main_tab.rowconfigure(0, weight=1)
self.main_tab.columnconfigure(0, weight=1) self.main_tab.columnconfigure(0, weight=1)
@@ -321,7 +321,7 @@ class NovelGeneratorGUI:
self.build_ai_config_tab() self.build_ai_config_tab()
self.build_embeddings_config_tab() self.build_embeddings_config_tab()
# 底部的保存配置”和“加载配置按钮 # 底部的"保存配置"和"加载配置"按钮
self.btn_frame_config = ctk.CTkFrame(self.config_frame) 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.grid(row=1, column=0, padx=5, pady=5, sticky="ew")
self.btn_frame_config.columnconfigure(0, weight=1) 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, def create_label_with_help(self, parent, label_text, tooltip_key, row, column,
font=None, sticky="e", padx=5, pady=5): font=None, sticky="e", padx=5, pady=5):
""" """
封装一个带“?”按钮的Label,用于展示提示信息。 封装一个带"?"按钮的Label,用于展示提示信息。
""" """
frame = ctk.CTkFrame(parent) frame = ctk.CTkFrame(parent)
frame.grid(row=row, column=column, padx=padx, pady=pady, sticky=sticky) 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") self.base_url_var.set("https://api.deepseek.com/v1")
elif new_value == "Gemini": elif new_value == "Gemini":
self.base_url_var.set("") # Gemini 通常不需要 Base URL,可以设置为空 self.base_url_var.set("") # Gemini 通常不需要 Base URL,可以设置为空
elif new_value == "Azure AI":
self.base_url_var.set("https://<your-endpoint>.services.ai.azure.com/models/chat/completions?api-version=2024-05-01-preview")
for i in range(7): for i in range(7):
self.ai_config_tab.grid_rowconfigure(i, weight=0) self.ai_config_tab.grid_rowconfigure(i, weight=0)
@@ -415,7 +417,7 @@ class NovelGeneratorGUI:
column=0, column=0,
font=("Microsoft YaHei", 12) 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( interface_dropdown = ctk.CTkOptionMenu(
self.ai_config_tab, self.ai_config_tab,
values=interface_options, values=interface_options,