feat(model): 引入 is_v2_spec_format 函数以优化模型规格判断逻辑
This commit is contained in:
parent
2e0b4a8358
commit
a8eb5ab6fe
@ -5,6 +5,7 @@ from langchain.chat_models import BaseChatModel, init_chat_model
|
||||
from pydantic import SecretStr
|
||||
|
||||
from yuxi import config
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.utils import get_docker_safe_url
|
||||
from yuxi.utils.logging_config import logger
|
||||
|
||||
@ -64,8 +65,8 @@ def load_chat_model(fully_specified_name: str, **kwargs) -> BaseChatModel:
|
||||
"""
|
||||
Load a chat model from a fully specified name.
|
||||
"""
|
||||
# v2 判断:如果 spec 包含冒号,尝试走 v2 路径
|
||||
if ":" in fully_specified_name:
|
||||
# v2 判断:第一个特殊字符为冒号则走 v2 路径
|
||||
if is_v2_spec_format(fully_specified_name):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
info = model_cache.get_model_info(fully_specified_name)
|
||||
|
||||
@ -5,6 +5,7 @@ from openai import AsyncOpenAI
|
||||
from tenacity import before_sleep_log, retry, retry_if_exception_type, stop_after_attempt, wait_exponential
|
||||
|
||||
from yuxi import config
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.utils import logger
|
||||
|
||||
|
||||
@ -126,13 +127,16 @@ def select_model_v2(spec: str) -> OpenAIBase:
|
||||
|
||||
def select_model(model_provider=None, model_name=None, model_spec=None):
|
||||
"""根据模型提供者选择模型"""
|
||||
# v2 判断:如果 spec 包含冒号且在缓存中存在,走 v2 路径
|
||||
if model_spec and ":" in model_spec:
|
||||
if model_spec and is_v2_spec_format(model_spec):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
if model_cache.is_v2_spec(model_spec):
|
||||
return select_model_v2(model_spec)
|
||||
|
||||
available = model_cache.get_all_specs("chat")
|
||||
available_ids = [s.spec for s in available[:10]]
|
||||
raise ValueError(f"未找到 V2 模型: '{model_spec}'。可用聊天模型 ({len(available)}): {available_ids}")
|
||||
|
||||
logger.warning(
|
||||
f"旧版本的模型选择逻辑已废弃,建议尽快迁移至新的模型配置;"
|
||||
f"当前模型选择参数: provider={model_provider}, model_name={model_name}, spec={model_spec}"
|
||||
|
||||
@ -7,6 +7,7 @@ import httpx
|
||||
import requests
|
||||
|
||||
from yuxi import config
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.utils import get_docker_safe_url, hashstr, logger
|
||||
|
||||
|
||||
@ -288,7 +289,7 @@ def get_embedding_model_info_by_id(model_id: str) -> dict:
|
||||
api_key 已从环境变量解析为实际值。
|
||||
"""
|
||||
# V2 spec 检测
|
||||
if isinstance(model_id, str) and ":" in model_id:
|
||||
if isinstance(model_id, str) and is_v2_spec_format(model_id):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
info = model_cache.get_model_info(model_id)
|
||||
@ -321,8 +322,8 @@ def select_embedding_model(model_id):
|
||||
V1 格式: provider/model_name(斜杠分隔)
|
||||
V2 格式: provider_id:model_id(冒号分隔)
|
||||
"""
|
||||
# V2 spec 检测:包含 ":" 且存在于缓存中
|
||||
if isinstance(model_id, str) and ":" in model_id:
|
||||
# V2 spec 检测:第一个特殊字符为冒号且存在于缓存中
|
||||
if isinstance(model_id, str) and is_v2_spec_format(model_id):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
if model_cache.is_v2_spec(model_id):
|
||||
@ -385,7 +386,7 @@ async def test_embedding_model_status_by_spec(spec: str) -> dict:
|
||||
V2 spec 格式: provider_id:model_id(冒号分隔)
|
||||
"""
|
||||
try:
|
||||
if ":" in spec:
|
||||
if is_v2_spec_format(spec):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
if model_cache.is_v2_spec(spec):
|
||||
|
||||
@ -22,6 +22,22 @@ _DEFAULT_REDIS_URL = "redis://redis:6379/0"
|
||||
_CACHE_TTL_SECONDS = 5
|
||||
|
||||
|
||||
def is_v2_spec_format(spec: str) -> bool:
|
||||
"""判断 spec 是否为 V2 格式(基于格式特征而非缓存查找)。
|
||||
|
||||
V2 格式的特征是第一个特殊字符为冒号(provider_id:model_id),
|
||||
V1 格式的特征是第一个特殊字符为斜杠(provider/model_name)。
|
||||
Provider ID 中不包含斜杠,因此第一个特殊字符决定了格式类型。
|
||||
"""
|
||||
colon_pos = spec.find(":")
|
||||
slash_pos = spec.find("/")
|
||||
if colon_pos == -1:
|
||||
return False
|
||||
if slash_pos == -1:
|
||||
return True
|
||||
return colon_pos < slash_pos
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ModelInfo:
|
||||
"""不可变的模型信息,供运行时使用。"""
|
||||
@ -245,8 +261,8 @@ def resolve_model_spec(spec: str) -> ModelInfo:
|
||||
if not spec:
|
||||
raise ValueError("spec 不能为空")
|
||||
|
||||
# V2: 优先查找缓存
|
||||
if ":" in spec:
|
||||
# V2: 第一个特殊字符为冒号则走 v2 路径
|
||||
if is_v2_spec_format(spec):
|
||||
info = model_cache.get_model_info(spec)
|
||||
if info:
|
||||
return info
|
||||
|
||||
@ -15,6 +15,7 @@ from yuxi.repositories.model_provider_repository import (
|
||||
update_model_provider,
|
||||
)
|
||||
from yuxi.config.builtin_providers import BUILTIN_PROVIDERS
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.storage.postgres.models_business import ModelProvider
|
||||
|
||||
VALID_MODEL_TYPES = {"chat", "embedding", "rerank"}
|
||||
@ -375,7 +376,7 @@ async def test_model_status_by_spec(spec: str) -> dict:
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
# V2: 从缓存识别模型类型并分派
|
||||
if ":" in spec:
|
||||
if is_v2_spec_format(spec):
|
||||
info = model_cache.get_model_info(spec)
|
||||
if info:
|
||||
if info.model_type == "embedding":
|
||||
|
||||
@ -18,6 +18,7 @@ from yuxi.plugins.parser import Parser, SUPPORTED_FILE_EXTENSIONS, is_supported_
|
||||
from yuxi.knowledge.utils import calculate_content_hash
|
||||
from yuxi.knowledge.utils.kb_utils import parse_minio_url
|
||||
from yuxi.models.embed import test_all_embedding_models_status, test_embedding_model_status
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.storage.postgres.models_business import User
|
||||
from yuxi.storage.minio.client import MinIOClient, StorageError, aupload_file_to_minio, get_minio_client
|
||||
from yuxi.utils import logger
|
||||
@ -149,8 +150,8 @@ async def create_database(
|
||||
if not embed_model_name:
|
||||
raise HTTPException(status_code=400, detail="embed_model_name 不能为空")
|
||||
|
||||
# V2 embedding model (spec 格式: provider_id:model_id,使用冒号分隔)
|
||||
if ":" in embed_model_name:
|
||||
# V2 embedding model (spec 格式: provider_id:model_id,第一个特殊字符为冒号)
|
||||
if is_v2_spec_format(embed_model_name):
|
||||
from yuxi.services.model_cache import model_cache
|
||||
|
||||
info = model_cache.get_model_info(embed_model_name)
|
||||
@ -443,7 +444,9 @@ async def add_documents(
|
||||
db_id, file_id, indexing_params, operator_id=current_user.user_id
|
||||
)
|
||||
# 2. 执行入库(传入 indexing_params 确保使用的参数与用户设置一致)
|
||||
result = await knowledge_base.index_file(db_id, file_id, operator_id=current_user.user_id, params=indexing_params)
|
||||
result = await knowledge_base.index_file(
|
||||
db_id, file_id, operator_id=current_user.user_id, params=indexing_params
|
||||
)
|
||||
processed_items.append(result)
|
||||
except Exception as index_error:
|
||||
logger.error(f"自动入库失败 {item} (file_id={file_id}): {index_error}")
|
||||
|
||||
@ -1063,7 +1063,7 @@ const confirmDeleteConfig = async () => {
|
||||
flex-shrink: 0;
|
||||
|
||||
&.open {
|
||||
width: 400px;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
@ -1074,7 +1074,7 @@ const confirmDeleteConfig = async () => {
|
||||
border-bottom: 1px solid var(--gray-150);
|
||||
background: var(--gray-0);
|
||||
flex-shrink: 0;
|
||||
min-width: 400px;
|
||||
min-width: 360px;
|
||||
z-index: 10;
|
||||
|
||||
.header-top-row {
|
||||
@ -1150,7 +1150,7 @@ const confirmDeleteConfig = async () => {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px 12px 8px;
|
||||
min-width: 400px;
|
||||
min-width: 360px;
|
||||
|
||||
.agent-info {
|
||||
.agent-basic-info {
|
||||
|
||||
@ -293,7 +293,7 @@ const renameChat = async (chatId) => {
|
||||
.conversation-actions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 8px;
|
||||
right: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
BarChart3,
|
||||
ClipboardList,
|
||||
Blocks,
|
||||
Cpu,
|
||||
Box,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
MessageCirclePlus
|
||||
@ -154,8 +154,8 @@ const mainList = computed(() => {
|
||||
items.push({
|
||||
name: '模型配置',
|
||||
path: '/model-config',
|
||||
icon: Cpu,
|
||||
activeIcon: Cpu
|
||||
icon: Box,
|
||||
activeIcon: Box
|
||||
})
|
||||
|
||||
items.push({
|
||||
|
||||
@ -661,14 +661,6 @@ onMounted(loadProviders)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty State -->
|
||||
<a-empty v-if="!filteredProviders.length" :image="false" description="暂无供应商">
|
||||
<a-button type="primary" class="lucide-icon-btn" @click="openCreateProviderModal">
|
||||
<Plus :size="14" />
|
||||
新增供应商
|
||||
</a-button>
|
||||
</a-empty>
|
||||
</div>
|
||||
|
||||
<!-- Provider Edit Modal -->
|
||||
@ -1182,7 +1174,7 @@ onMounted(loadProviders)
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
background: var(--main-50);
|
||||
background: #e1f6fb; // 固定使用浅色背景,确保图标清晰可见
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user