mirror of
https://github.com/zhenxun-org/zhenxun_bot.git
synced 2026-10-11 15:00:00 +08:00
✨ feat(llm): 全面重构LLM服务模块,增强多模态与工具支持 (#1953)
* ✨ feat(llm): 全面重构LLM服务模块,增强多模态与工具支持 🚀 核心功能增强 - 多模型链式调用:新增 `pipeline_chat` 支持复杂任务流处理 - 扩展提供商支持:新增 ARK(火山方舟)、SiliconFlow(硅基流动) 适配器 - 多模态处理增强:支持URL媒体文件下载转换,提升输入灵活性 - 历史对话支持:AI.analyze 方法支持历史消息上下文和可选 UniMessage 参数 - 文本嵌入功能:新增 `embed`、`analyze_multimodal`、`search_multimodal` 等API - 模型能力系统:新增 `ModelCapabilities` 统一管理模型特性(多模态、工具调用等) 🔧 架构重构与优化 - MCP工具系统重构:配置独立化至 `data/llm/mcp_tools.json`,预置常用工具 - API调用逻辑统一:提取通用 `_perform_api_call` 方法,消除代码重复 - 跨平台兼容:Windows平台MCP工具npx命令自动包装处理 - HTTP客户端增强:兼容不同版本httpx代理配置(0.28+版本适配) 🛠️ API与配置完善 - 统一返回类型:`AI.analyze` 统一返回 `LLMResponse` 类型 - 消息转换工具:新增 `message_to_unimessage` 转换函数 - Gemini适配器增强:URL图片下载编码、动态安全阈值配置 - 缓存管理:新增模型实例缓存和管理功能 - 配置预设:扩展 CommonOverrides 预设配置选项 - 历史管理优化:支持多模态内容占位符替换,提升效率 📚 文档与开发体验 - README全面重写:新增完整使用指南、API参考和架构概览 - 文档内容扩充:补充嵌入模型、缓存管理、工具注册等功能说明 - 日志记录增强:支持详细调试信息输出 - API简化:移除冗余函数,优化接口设计 * 🎨 feat(llm): 统一LLM服务函数文档格式 * ✨ feat(llm): 添加新模型并简化提供者配置加载 * 🚨 auto fix by pre-commit hooks --------- Co-authored-by: webjoin111 <455457521@qq.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
webjoin111
pre-commit-ci[bot]
parent
1e7ae38684
commit
48cbb2bf1d
@@ -17,6 +17,7 @@ from .config.providers import AI_CONFIG_GROUP, PROVIDERS_CONFIG_KEY, get_ai_conf
|
||||
from .core import http_client_manager, key_store
|
||||
from .service import LLMModel
|
||||
from .types import LLMErrorCode, LLMException, ModelDetail, ProviderConfig
|
||||
from .types.capabilities import get_model_capabilities
|
||||
|
||||
DEFAULT_MODEL_NAME_KEY = "default_model_name"
|
||||
PROXY_KEY = "proxy"
|
||||
@@ -115,57 +116,30 @@ def get_default_api_base_for_type(api_type: str) -> str | None:
|
||||
|
||||
|
||||
def get_configured_providers() -> list[ProviderConfig]:
|
||||
"""从配置中获取Provider列表 - 简化版本"""
|
||||
"""从配置中获取Provider列表 - 简化和修正版本"""
|
||||
ai_config = get_ai_config()
|
||||
providers_raw = ai_config.get(PROVIDERS_CONFIG_KEY, [])
|
||||
if not isinstance(providers_raw, list):
|
||||
providers = ai_config.get(PROVIDERS_CONFIG_KEY, [])
|
||||
|
||||
if not isinstance(providers, list):
|
||||
logger.error(
|
||||
f"配置项 {AI_CONFIG_GROUP}.{PROVIDERS_CONFIG_KEY} 不是一个列表,"
|
||||
f"配置项 {AI_CONFIG_GROUP}.{PROVIDERS_CONFIG_KEY} 的值不是一个列表,"
|
||||
f"将使用空列表。"
|
||||
)
|
||||
return []
|
||||
|
||||
valid_providers = []
|
||||
for i, item in enumerate(providers_raw):
|
||||
if not isinstance(item, dict):
|
||||
logger.warning(f"配置文件中第 {i + 1} 项不是字典格式,已跳过。")
|
||||
continue
|
||||
|
||||
try:
|
||||
if not item.get("name"):
|
||||
logger.warning(f"Provider {i + 1} 缺少 'name' 字段,已跳过。")
|
||||
continue
|
||||
|
||||
if not item.get("api_key"):
|
||||
logger.warning(
|
||||
f"Provider '{item['name']}' 缺少 'api_key' 字段,已跳过。"
|
||||
)
|
||||
continue
|
||||
|
||||
if "api_type" not in item or not item["api_type"]:
|
||||
provider_name = item.get("name", "").lower()
|
||||
if "glm" in provider_name or "zhipu" in provider_name:
|
||||
item["api_type"] = "zhipu"
|
||||
elif "gemini" in provider_name or "google" in provider_name:
|
||||
item["api_type"] = "gemini"
|
||||
else:
|
||||
item["api_type"] = "openai"
|
||||
|
||||
if "api_base" not in item or not item["api_base"]:
|
||||
api_type = item.get("api_type")
|
||||
if api_type:
|
||||
default_api_base = get_default_api_base_for_type(api_type)
|
||||
if default_api_base:
|
||||
item["api_base"] = default_api_base
|
||||
|
||||
if "models" not in item:
|
||||
item["models"] = [{"model_name": item.get("name", "default")}]
|
||||
|
||||
provider_conf = ProviderConfig(**item)
|
||||
valid_providers.append(provider_conf)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"解析配置文件中 Provider {i + 1} 时出错: {e},已跳过。")
|
||||
for i, item in enumerate(providers):
|
||||
if isinstance(item, ProviderConfig):
|
||||
if not item.api_base:
|
||||
default_api_base = get_default_api_base_for_type(item.api_type)
|
||||
if default_api_base:
|
||||
item.api_base = default_api_base
|
||||
valid_providers.append(item)
|
||||
else:
|
||||
logger.warning(
|
||||
f"配置文件中第 {i + 1} 项未能正确解析为 ProviderConfig 对象,"
|
||||
f"已跳过。实际类型: {type(item)}"
|
||||
)
|
||||
|
||||
return valid_providers
|
||||
|
||||
@@ -173,14 +147,15 @@ def get_configured_providers() -> list[ProviderConfig]:
|
||||
def find_model_config(
|
||||
provider_name: str, model_name: str
|
||||
) -> tuple[ProviderConfig, ModelDetail] | None:
|
||||
"""在配置中查找指定的 Provider 和 ModelDetail
|
||||
"""
|
||||
在配置中查找指定的 Provider 和 ModelDetail
|
||||
|
||||
Args:
|
||||
参数:
|
||||
provider_name: 提供商名称
|
||||
model_name: 模型名称
|
||||
|
||||
Returns:
|
||||
找到的 (ProviderConfig, ModelDetail) 元组,未找到则返回 None
|
||||
返回:
|
||||
tuple[ProviderConfig, ModelDetail] | None: 找到的配置元组,未找到则返回 None
|
||||
"""
|
||||
providers = get_configured_providers()
|
||||
|
||||
@@ -221,10 +196,11 @@ def _get_model_identifiers(provider_name: str, model_detail: ModelDetail) -> lis
|
||||
|
||||
|
||||
def list_model_identifiers() -> dict[str, list[str]]:
|
||||
"""列出所有模型的可用标识符
|
||||
"""
|
||||
列出所有模型的可用标识符
|
||||
|
||||
Returns:
|
||||
字典,键为模型的完整名称,值为该模型的所有可用标识符列表
|
||||
返回:
|
||||
dict[str, list[str]]: 字典,键为模型的完整名称,值为该模型的所有可用标识符列表
|
||||
"""
|
||||
providers = get_configured_providers()
|
||||
result = {}
|
||||
@@ -248,7 +224,16 @@ async def get_model_instance(
|
||||
provider_model_name: str | None = None,
|
||||
override_config: dict[str, Any] | None = None,
|
||||
) -> LLMModel:
|
||||
"""根据 'ProviderName/ModelName' 字符串获取并实例化 LLMModel (异步版本)"""
|
||||
"""
|
||||
根据 'ProviderName/ModelName' 字符串获取并实例化 LLMModel (异步版本)
|
||||
|
||||
参数:
|
||||
provider_model_name: 模型名称,格式为 'ProviderName/ModelName'。
|
||||
override_config: 覆盖配置字典。
|
||||
|
||||
返回:
|
||||
LLMModel: 模型实例。
|
||||
"""
|
||||
cache_key = _make_cache_key(provider_model_name, override_config)
|
||||
cached_model = _get_cached_model(cache_key)
|
||||
if cached_model:
|
||||
@@ -292,6 +277,10 @@ async def get_model_instance(
|
||||
|
||||
provider_config_found, model_detail_found = config_tuple_found
|
||||
|
||||
capabilities = get_model_capabilities(model_detail_found.model_name)
|
||||
|
||||
model_detail_found.is_embedding_model = capabilities.is_embedding_model
|
||||
|
||||
ai_config = get_ai_config()
|
||||
global_proxy_setting = ai_config.get(PROXY_KEY)
|
||||
default_timeout = (
|
||||
@@ -322,6 +311,7 @@ async def get_model_instance(
|
||||
model_detail=model_detail_found,
|
||||
key_store=key_store,
|
||||
http_client=shared_http_client,
|
||||
capabilities=capabilities,
|
||||
)
|
||||
|
||||
if override_config:
|
||||
@@ -357,7 +347,15 @@ def get_global_default_model_name() -> str | None:
|
||||
|
||||
|
||||
def set_global_default_model_name(provider_model_name: str | None) -> bool:
|
||||
"""设置全局默认模型名称"""
|
||||
"""
|
||||
设置全局默认模型名称
|
||||
|
||||
参数:
|
||||
provider_model_name: 模型名称,格式为 'ProviderName/ModelName'。
|
||||
|
||||
返回:
|
||||
bool: 设置是否成功。
|
||||
"""
|
||||
if provider_model_name:
|
||||
prov_name, mod_name = parse_provider_model_string(provider_model_name)
|
||||
if not prov_name or not mod_name or not find_model_config(prov_name, mod_name):
|
||||
@@ -377,7 +375,12 @@ def set_global_default_model_name(provider_model_name: str | None) -> bool:
|
||||
|
||||
|
||||
async def get_key_usage_stats() -> dict[str, Any]:
|
||||
"""获取所有Provider的Key使用统计"""
|
||||
"""
|
||||
获取所有Provider的Key使用统计
|
||||
|
||||
返回:
|
||||
dict[str, Any]: 包含所有Provider的Key使用统计信息。
|
||||
"""
|
||||
providers = get_configured_providers()
|
||||
stats = {}
|
||||
|
||||
@@ -400,7 +403,16 @@ async def get_key_usage_stats() -> dict[str, Any]:
|
||||
|
||||
|
||||
async def reset_key_status(provider_name: str, api_key: str | None = None) -> bool:
|
||||
"""重置指定Provider的Key状态"""
|
||||
"""
|
||||
重置指定Provider的Key状态
|
||||
|
||||
参数:
|
||||
provider_name: 提供商名称。
|
||||
api_key: 要重置的特定API密钥,如果为None则重置所有密钥。
|
||||
|
||||
返回:
|
||||
bool: 重置是否成功。
|
||||
"""
|
||||
providers = get_configured_providers()
|
||||
target_provider = None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user