Merge pull request #310 from ELK-milu/main

fix:修复base中创建知识库时直接将basemodel转为json时的报错问题
This commit is contained in:
Wenjie Zhang 2025-10-23 23:46:30 +08:00 committed by GitHub
commit 92a110d955
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 268 additions and 157 deletions

13
.gitignore vendored
View File

@ -35,10 +35,17 @@ cache
.cursor .cursor
.trae .trae
.pytest_cache .pytest_cache
*.nogit*
*.private* ### (企业私有代码 - 仅忽略敏感配置,不忽略代码文件)
*.local* # 移除了 *.private* 和 *_private 规则,允许 Git 本地管理
# 通过 .git/info/exclude 或本地分支管理私有代码
*.secret* *.secret*
*.nogit*
# *.local* 保留用于本地配置文件
*.local.py
*.local.js
*.local.yaml
*.pdf *.pdf
src/data src/data

View File

@ -15,7 +15,7 @@ services:
- ./saves:/app/saves - ./saves:/app/saves
- ./test:/app/test - ./test:/app/test
- ./scripts:/app/scripts - ./scripts:/app/scripts
- ./.env :/app/.env - ./.env:/app/.env
- ${MODEL_DIR:-./models}:/models # 使用默认值处理未定义的环境变量 - ${MODEL_DIR:-./models}:/models # 使用默认值处理未定义的环境变量
ports: ports:
- "5050:5050" - "5050:5050"
@ -38,8 +38,8 @@ services:
- MINIO_URI=${MINIO_URI:-http://milvus-minio:9000} - MINIO_URI=${MINIO_URI:-http://milvus-minio:9000}
- MODEL_DIR_IN_DOCKER=/models - MODEL_DIR_IN_DOCKER=/models
- RUNNING_IN_DOCKER=true - RUNNING_IN_DOCKER=true
- NO_PROXY=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio,mineru,paddlex - NO_PROXY=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio,mineru,paddlex,api.siliconflow.cn
- no_proxy=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio,mineru,paddlex - no_proxy=localhost,127.0.0.1,milvus,graph,milvus-minio,milvus-etcd-dev,etcd,minio,mineru,paddlex,api.siliconflow.cn
# endregion api_envs # endregion api_envs
command: uv run --no-dev uvicorn server.main:app --host 0.0.0.0 --port 5050 --reload command: uv run --no-dev uvicorn server.main:app --host 0.0.0.0 --port 5050 --reload
restart: unless-stopped restart: unless-stopped
@ -145,7 +145,7 @@ services:
- ETCD_SNAPSHOT_COUNT=50000 - ETCD_SNAPSHOT_COUNT=50000
volumes: volumes:
- ./docker/volumes/milvus/etcd:/etcd - ./docker/volumes/milvus/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck: healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"] test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 60s interval: 60s

View File

@ -9,7 +9,7 @@ from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware from starlette.middleware.base import BaseHTTPMiddleware
from server.routers import router from server.routers import router
from server.services.tasker import tasker from server.utils.lifespan import lifespan
from server.utils.auth_middleware import is_public_path from server.utils.auth_middleware import is_public_path
from server.utils.common_utils import setup_logging from server.utils.common_utils import setup_logging
@ -24,7 +24,7 @@ RATE_LIMIT_ENDPOINTS = {("/api/auth/token", "POST")}
_login_attempts: defaultdict[str, deque[float]] = defaultdict(deque) _login_attempts: defaultdict[str, deque[float]] = defaultdict(deque)
_attempt_lock = asyncio.Lock() _attempt_lock = asyncio.Lock()
app = FastAPI() app = FastAPI(lifespan=lifespan)
app.include_router(router, prefix="/api") app.include_router(router, prefix="/api")
# CORS 设置 # CORS 设置
@ -119,16 +119,5 @@ class AuthMiddleware(BaseHTTPMiddleware):
app.add_middleware(LoginRateLimitMiddleware) app.add_middleware(LoginRateLimitMiddleware)
app.add_middleware(AuthMiddleware) app.add_middleware(AuthMiddleware)
@app.on_event("startup")
async def start_tasker() -> None:
await tasker.start()
@app.on_event("shutdown")
async def stop_tasker() -> None:
await tasker.shutdown()
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=5050, threads=10, workers=10, reload=True) uvicorn.run(app, host="0.0.0.0", port=5050, threads=10, workers=10, reload=True)

View File

@ -112,7 +112,7 @@ async def get_agent(current_user: User = Depends(get_required_user)):
metadata = yaml.safe_load(f) metadata = yaml.safe_load(f)
return {"agents": agents, "metadata": metadata} return {"agents": agents, "metadata": metadata}
#TODO:[未完成]这个thread_id在前端是直接生成的1234最好传入thread_id时做校验只允许uuid4
@chat.post("/agent/{agent_id}") @chat.post("/agent/{agent_id}")
async def chat_agent( async def chat_agent(
agent_id: str, agent_id: str,
@ -254,6 +254,7 @@ async def chat_agent(
logger.error(f"Error saving messages from LangGraph state: {e}") logger.error(f"Error saving messages from LangGraph state: {e}")
logger.error(traceback.format_exc()) logger.error(traceback.format_exc())
#TODO:[功能建议]针对需要人工审批后再执行的工具可以使用langgraph的interrupt方法中断对话等待用户输入后再使用command跳转回去
async def stream_messages(): async def stream_messages():
# 代表服务端已经收到了请求 # 代表服务端已经收到了请求
yield make_chunk(status="init", meta=meta, msg=HumanMessage(content=query).model_dump()) yield make_chunk(status="init", meta=meta, msg=HumanMessage(content=query).model_dump())

15
server/utils/lifespan.py Normal file
View File

@ -0,0 +1,15 @@
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI
from server.services import tasker
#TODO:[已完成]使用lifespan进行统一生命周期管理
@asynccontextmanager
async def lifespan(app: FastAPI):
await tasker.start()
"""FastAPI lifespan事件管理器"""
yield
await tasker.shutdown()

15
server/utils/singleton.py Normal file
View File

@ -0,0 +1,15 @@
from threading import Lock
class SingletonMeta(type):
"""
This is a thread-safe implementation of Singleton.
"""
_instances = {}
_lock: Lock = Lock()
def __call__(cls, *args, **kwargs):
with cls._lock:
if cls not in cls._instances:
instance = super().__call__(*args, **kwargs)
cls._instances[cls] = instance
return cls._instances[cls]

View File

@ -1,7 +1,10 @@
import asyncio import asyncio
import importlib
import inspect
from pathlib import Path
from .chatbot.graph import ChatbotAgent from src.agents.common.base import BaseAgent
from .react.graph import ReActAgent from src.utils import logger
class AgentManager: class AgentManager:
@ -35,10 +38,51 @@ class AgentManager:
agents = self.get_agents() agents = self.get_agents()
return await asyncio.gather(*[a.get_info() for a in agents]) return await asyncio.gather(*[a.get_info() for a in agents])
def auto_discover_agents(self):
"""自动发现并注册 src/agents/ 下的所有智能体。
遍历 src/agents/ 目录下的所有子文件夹如果子文件夹包含 __init__.py
则尝试从中导入 BaseAgent 的子类并注册(使用自动导入的方式支持私有agent)
"""
# 获取 agents 目录的路径
agents_dir = Path(__file__).parent
# 遍历所有子目录
for item in agents_dir.iterdir():
logger.info(f"尝试导入模块:{item}")
# 跳过非目录、common 目录、__pycache__ 等
if not item.is_dir() or item.name.startswith("_") or item.name == "common":
continue
# 检查是否有 __init__.py 文件
init_file = item / "__init__.py"
if not init_file.exists():
logger.warning(f"{item} 不是一个有效的模块")
continue
# 尝试导入模块
try:
module_name = f"src.agents.{item.name}"
module = importlib.import_module(module_name)
# 查找模块中所有 BaseAgent 的子类
for name, obj in inspect.getmembers(module):
if (
inspect.isclass(obj)
and issubclass(obj, BaseAgent)
and obj is not BaseAgent
and obj.__module__.startswith(module_name)
):
logger.info(f"自动发现智能体: {obj.__name__} 来自 {item.name}")
self.register_agent(obj)
except Exception as e:
logger.warning(f"无法从 {item.name} 加载智能体: {e}")
agent_manager = AgentManager() agent_manager = AgentManager()
agent_manager.register_agent(ChatbotAgent) # 自动发现并注册所有智能体
agent_manager.register_agent(ReActAgent) agent_manager.auto_discover_agents()
agent_manager.init_all_agents() agent_manager.init_all_agents()
__all__ = ["agent_manager"] __all__ = ["agent_manager"]

View File

@ -9,10 +9,20 @@ from src.agents.common.tools import get_buildin_tools
from src.storage.minio import upload_image_to_minio from src.storage.minio import upload_image_to_minio
from src.utils import logger from src.utils import logger
#TODO:[已完成]修改了tool定义的示例使用更符合langgraph调用的方式
@tool @tool(name_or_callable="全能计算器",description="可以对给定的2个数字选择进行加减乘除四种计算")
def calculator(a: float, b: float, operation: str) -> float: def calculator(a: float, b: float, operation: str) -> float:
"""Calculate two numbers. operation: add, subtract, multiply, divide""" """
可以对给定的2个数字选择进行加减乘除四种计算
Args:
a: 第一个数字
b: 第二个数字
operation: 计算操作符号可以是addsubtractmultiplydivide
Returns:
float: 最终的计算结果
"""
try: try:
if operation == "add": if operation == "add":
return a + b return a + b

View File

@ -0,0 +1,3 @@
from .graph import ReActAgent
__all__ = ["ReActAgent"]

View File

@ -142,6 +142,12 @@ DEFAULT_EMBED_MODELS: dict[str, EmbedModelInfo] = {
base_url="https://api.siliconflow.cn/v1/embeddings", base_url="https://api.siliconflow.cn/v1/embeddings",
api_key="SILICONFLOW_API_KEY", api_key="SILICONFLOW_API_KEY",
), ),
"siliconflow/Pro/BAAI/bge-m3": EmbedModelInfo(
name="Pro/BAAI/bge-m3",
dimension=1024,
base_url="https://api.siliconflow.cn/v1/embeddings",
api_key="SILICONFLOW_API_KEY",
),
"siliconflow/Qwen/Qwen3-Embedding-0.6B": EmbedModelInfo( "siliconflow/Qwen/Qwen3-Embedding-0.6B": EmbedModelInfo(
name="Qwen/Qwen3-Embedding-0.6B", name="Qwen/Qwen3-Embedding-0.6B",
dimension=1024, dimension=1024,
@ -179,6 +185,11 @@ DEFAULT_RERANKERS: dict[str, RerankerInfo] = {
base_url="https://api.siliconflow.cn/v1/rerank", base_url="https://api.siliconflow.cn/v1/rerank",
api_key="SILICONFLOW_API_KEY", api_key="SILICONFLOW_API_KEY",
), ),
"siliconflow/Pro/BAAI/bge-reranker-v2-m3": RerankerInfo(
name="Pro/BAAI/bge-reranker-v2-m3",
base_url="https://api.siliconflow.cn/v1/rerank",
api_key="SILICONFLOW_API_KEY",
),
"vllm/BAAI/bge-reranker-v2-m3": RerankerInfo( "vllm/BAAI/bge-reranker-v2-m3": RerankerInfo(
name="BAAI/bge-reranker-v2-m3", name="BAAI/bge-reranker-v2-m3",
base_url="http://localhost:8000/v1/rerank", base_url="http://localhost:8000/v1/rerank",

View File

@ -1,139 +1,150 @@
#################################################### ##################################################################################
# #
# 默认模型配置文件。可以直接修改该文件更新模型。 # 注意:本文件已弃用,现在模型的默认配置在 src/config/static/models.py 中定义
# 如需自定义路径,可通过环境变量 # 修改后的配置信息,保存在 saves/config/base.toml 中
# OVERRIDE_DEFAULT_MODELS_CONFIG_WITH 指向新的 YAML。
# #
##################################################### ###################################################################################
MODEL_NAMES: # MODEL_NAMES:
openai: # openai:
name: OpenAI # name: OpenAI
url: https://platform.openai.com/docs/models # url: https://platform.openai.com/docs/models
base_url: https://api.openai.com/v1 # base_url: https://api.openai.com/v1
default: gpt-4o-mini # default: gpt-4o-mini
env: OPENAI_API_KEY # env: OPENAI_API_KEY
models: # models:
- gpt-4 # - gpt-4
- gpt-4o # - gpt-4o
- gpt-4o-mini # - gpt-4o-mini
deepseek: # deepseek:
name: DeepSeek # name: DeepSeek
url: https://platform.deepseek.com/api-docs/zh-cn/pricing # url: https://platform.deepseek.com/api-docs/zh-cn/pricing
base_url: https://api.deepseek.com/v1 # base_url: https://api.deepseek.com/v1
default: deepseek-chat # default: deepseek-chat
env: DEEPSEEK_API_KEY # env: DEEPSEEK_API_KEY
models: # models:
- deepseek-chat # - deepseek-chat
- deepseek-reasoner # - deepseek-reasoner
zhipu: # zhipu:
name: 智谱AI (Zhipu) # name: 智谱AI (Zhipu)
url: https://open.bigmodel.cn/dev/api # url: https://open.bigmodel.cn/dev/api
base_url: https://open.bigmodel.cn/api/paas/v4/ # base_url: https://open.bigmodel.cn/api/paas/v4/
default: glm-4.5-flash # default: glm-4.5-flash
env: ZHIPUAI_API_KEY # env: ZHIPUAI_API_KEY
models: # models:
- glm-4.6 # - glm-4.6
- glm-4.5-air # - glm-4.5-air
- glm-4.5-flash # - glm-4.5-flash
siliconflow: # siliconflow:
name: SiliconFlow # name: SiliconFlow
url: https://cloud.siliconflow.cn/models # url: https://cloud.siliconflow.cn/models
base_url: https://api.siliconflow.cn/v1 # base_url: https://api.siliconflow.cn/v1
default: deepseek-ai/DeepSeek-V3.2-Exp # default: Qwen/Qwen3-32B
env: SILICONFLOW_API_KEY # env: SILICONFLOW_API_KEY
models: # models:
- deepseek-ai/DeepSeek-V3.2-Exp # - Qwen/Qwen3-Next-80B-A3B-Thinking
- Qwen/Qwen3-235B-A22B-Thinking-2507 # - Qwen/Qwen3-32B
- Qwen/Qwen3-235B-A22B-Instruct-2507 # - deepseek-ai/DeepSeek-V3.2-Exp
- moonshotai/Kimi-K2-Instruct-0905 # - Qwen/Qwen3-235B-A22B-Thinking-2507
- zai-org/GLM-4.6 # - Qwen/Qwen3-235B-A22B-Instruct-2507
# - moonshotai/Kimi-K2-Instruct-0905
# - zai-org/GLM-4.6
together.ai: # together.ai:
name: Together.ai # name: Together.ai
url: https://api.together.ai/models # url: https://api.together.ai/models
base_url: https://api.together.xyz/v1/ # base_url: https://api.together.xyz/v1/
default: meta-llama/Llama-3.3-70B-Instruct-Turbo-Free # default: meta-llama/Llama-3.3-70B-Instruct-Turbo-Free
env: TOGETHER_API_KEY # env: TOGETHER_API_KEY
models: # models:
- meta-llama/Llama-3.3-70B-Instruct-Turbo-Free # - meta-llama/Llama-3.3-70B-Instruct-Turbo-Free
dashscope: # dashscope:
name: 阿里百炼 (DashScope) # name: 阿里百炼 (DashScope)
url: https://bailian.console.aliyun.com/?switchAgent=10226727&productCode=p_efm#/model-market # url: https://bailian.console.aliyun.com/?switchAgent=10226727&productCode=p_efm#/model-market
base_url: https://dashscope.aliyuncs.com/compatible-mode/v1 # base_url: https://dashscope.aliyuncs.com/compatible-mode/v1
default: qwen-max-latest # default: qwen-max-latest
env: DASHSCOPE_API_KEY # env: DASHSCOPE_API_KEY
models: # models:
- qwen-max-latest # - qwen-max-latest
- qwen-plus-latest # - qwen-plus-latest
- qwen-turbo-latest # - qwen-turbo-latest
- qwen3-235b-a22b-thinking-2507 # - qwen3-235b-a22b-thinking-2507
- qwen3-235b-a22b-instruct-2507 # - qwen3-235b-a22b-instruct-2507
ark: # ark:
name: 豆包Ark # name: 豆包Ark
url: https://console.volcengine.com/ark/region:ark+cn-beijing/model # url: https://console.volcengine.com/ark/region:ark+cn-beijing/model
base_url: https://ark.cn-beijing.volces.com/api/v3 # base_url: https://ark.cn-beijing.volces.com/api/v3
default: doubao-seed-1-6-250615 # default: doubao-seed-1-6-250615
env: ARK_API_KEY # env: ARK_API_KEY
models: # models:
- doubao-seed-1-6-250615 # - doubao-seed-1-6-250615
- doubao-seed-1-6-thinking-250715 # - doubao-seed-1-6-thinking-250715
- doubao-seed-1-6-flash-250715 # - doubao-seed-1-6-flash-250715
openrouter: # openrouter:
name: OpenRouter # name: OpenRouter
url: https://openrouter.ai/models # url: https://openrouter.ai/models
base_url: https://openrouter.ai/api/v1 # base_url: https://openrouter.ai/api/v1
default: openai/gpt-4o # default: openai/gpt-4o
env: OPENROUTER_API_KEY # env: OPENROUTER_API_KEY
models: # models:
- openai/gpt-4o # - openai/gpt-4o
- x-ai/grok-4 # - x-ai/grok-4
- google/gemini-2.5-pro # - google/gemini-2.5-pro
- anthropic/claude-sonnet-4 # - anthropic/claude-sonnet-4
EMBED_MODEL_INFO: # EMBED_MODEL_INFO:
siliconflow/BAAI/bge-m3: # siliconflow/Pro/BAAI/bge-m3:
name: BAAI/bge-m3 # name: Pro/BAAI/bge-m3
dimension: 1024 # dimension: 1024
base_url: https://api.siliconflow.cn/v1/embeddings # base_url: https://api.siliconflow.cn/v1/embeddings
api_key: SILICONFLOW_API_KEY # api_key: SILICONFLOW_API_KEY
siliconflow/Qwen/Qwen3-Embedding-0.6B: # siliconflow/BAAI/bge-m3:
name: Qwen/Qwen3-Embedding-0.6B # name: BAAI/bge-m3
dimension: 1024 # dimension: 1024
base_url: https://api.siliconflow.cn/v1/embeddings # base_url: https://api.siliconflow.cn/v1/embeddings
api_key: SILICONFLOW_API_KEY # api_key: SILICONFLOW_API_KEY
vllm/Qwen/Qwen3-Embedding-0.6B: # siliconflow/Qwen/Qwen3-Embedding-0.6B:
name: Qwen3-Embedding-0.6B # name: Qwen/Qwen3-Embedding-0.6B
dimension: 1024 # dimension: 1024
base_url: http://localhost:8000/v1/embeddings # base_url: https://api.siliconflow.cn/v1/embeddings
api_key: no_api_key # api_key: SILICONFLOW_API_KEY
ollama/nomic-embed-text: # vllm/Qwen/Qwen3-Embedding-0.6B:
name: nomic-embed-text # name: Qwen3-Embedding-0.6B
base_url: http://localhost:11434/api/embed # dimension: 1024
dimension: 768 # base_url: http://localhost:8000/v1/embeddings
# api_key: no_api_key
ollama/bge-m3: # ollama/nomic-embed-text:
name: bge-m3 # name: nomic-embed-text
base_url: http://localhost:11434/api/embed # base_url: http://localhost:11434/api/embed
dimension: 1024 # dimension: 768
RERANKER_LIST: # ollama/bge-m3:
# name: bge-m3
# base_url: http://localhost:11434/api/embed
# dimension: 1024
siliconflow/BAAI/bge-reranker-v2-m3: # RERANKER_LIST:
name: BAAI/bge-reranker-v2-m3 # siliconflow/Pro/BAAI/bge-reranker-v2-m3:
base_url: https://api.siliconflow.cn/v1/rerank # name: Pro/BAAI/bge-reranker-v2-m3
api_key: SILICONFLOW_API_KEY # base_url: https://api.siliconflow.cn/v1/rerank
# api_key: SILICONFLOW_API_KEY
vllm/BAAI/bge-reranker-v2-m3: # siliconflow/BAAI/bge-reranker-v2-m3:
name: BAAI/bge-reranker-v2-m3 # name: BAAI/bge-reranker-v2-m3
base_url: http://localhost:8000/v1/rerank # base_url: https://api.siliconflow.cn/v1/rerank
api_key: no_api_key # api_key: SILICONFLOW_API_KEY
# vllm/BAAI/bge-reranker-v2-m3:
# name: BAAI/bge-reranker-v2-m3
# base_url: http://localhost:8000/v1/rerank
# api_key: no_api_key

View File

@ -139,12 +139,13 @@ class KnowledgeBase(ABC):
db_id = f"kb_{hashstr(database_name, with_salt=True)}" db_id = f"kb_{hashstr(database_name, with_salt=True)}"
# 创建数据库记录 # 创建数据库记录
# 确保 Pydantic 模型被转换为字典,以便 JSON 序列化
self.databases_meta[db_id] = { self.databases_meta[db_id] = {
"name": database_name, "name": database_name,
"description": description, "description": description,
"kb_type": self.kb_type, "kb_type": self.kb_type,
"embed_info": embed_info, "embed_info": embed_info.model_dump() if hasattr(embed_info, 'model_dump') else embed_info,
"llm_info": llm_info, "llm_info": llm_info.model_dump() if hasattr(llm_info, 'model_dump') else llm_info,
"metadata": kwargs, "metadata": kwargs,
"created_at": utc_isoformat(), "created_at": utc_isoformat(),
} }

View File

@ -12,6 +12,7 @@ from src.storage.db.models import Conversation, ConversationStats, Message, Tool
from src.utils import logger from src.utils import logger
from src.utils.datetime_utils import utc_now from src.utils.datetime_utils import utc_now
#TODO:[未完成]待修改为异步版本
class ConversationManager: class ConversationManager:
"""Manager for conversation storage operations""" """Manager for conversation storage operations"""

View File

@ -5,6 +5,7 @@ from contextlib import contextmanager
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from server.utils.singleton import SingletonMeta
from src import config from src import config
from src.storage.db.models import Base, User from src.storage.db.models import Base, User
from src.utils import logger from src.utils import logger
@ -18,8 +19,10 @@ except ImportError:
def validate_database_schema(db_path): def validate_database_schema(db_path):
return True, [] return True, []
# TODO:[优化建议]需要将数据库修改为异步的aiosqlite或者异步mysql缓存使用Redis存储
# TODO:[已完成]为DBManager添加单例模式
class DBManager: class DBManager(metaclass=SingletonMeta):
"""数据库管理器 - 只提供基础的数据库连接和会话管理""" """数据库管理器 - 只提供基础的数据库连接和会话管理"""
def __init__(self): def __init__(self):

View File

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<a-alert message="自定义模型在 0.3.x 的稳定版中移除,只能通过修改 models.yaml 来添加模型和供应商。" type="warning" /> <a-alert message="自定义模型在 0.3.x 的稳定版中移除,只能通过修改 src/config/static/models.py 来添加模型和供应商。" type="warning" />
<br> <br>
<div class="model-provider-card configured-provider" v-for="(item, key) in modelKeys" :key="key"> <div class="model-provider-card configured-provider" v-for="(item, key) in modelKeys" :key="key">
<div class="card-header" @click="toggleExpand(item)"> <div class="card-header" @click="toggleExpand(item)">
@ -118,7 +118,7 @@
<div v-if="providerConfig.allModels.length === 0" class="modal-no-models"> <div v-if="providerConfig.allModels.length === 0" class="modal-no-models">
<a-alert v-if="!modelStatus[providerConfig.provider]" type="warning" message="请在 .env 中配置对应的 APIKEY并重新启动服务" /> <a-alert v-if="!modelStatus[providerConfig.provider]" type="warning" message="请在 .env 中配置对应的 APIKEY并重新启动服务" />
<div v-else> <div v-else>
<a-alert type="warning" message="该提供商暂未适配获取模型列表的方法,如需添加模型,请编辑 src/config/static/models.yaml 或通过环境变量 OVERRIDE_DEFAULT_MODELS_CONFIG_WITH 指向的文件。" /> <a-alert type="warning" message="该提供商暂未适配获取模型列表的方法,如需添加模型,请编辑 src/config/static/models.py 。" />
<img src="@/assets/pics/guides/how-to-add-models.png" alt="添加模型指引" style="width: 100%; height: 100%; margin-top: 16px;"> <img src="@/assets/pics/guides/how-to-add-models.png" alt="添加模型指引" style="width: 100%; height: 100%; margin-top: 16px;">
</div> </div>
</div> </div>

View File

@ -210,11 +210,11 @@ const state = reactive({
}) })
const embedModelChoices = computed(() => { const embedModelChoices = computed(() => {
return Object.keys(configStore.config.embed_model_names) || [] return Object.keys(configStore?.config?.embed_model_names || {}) || []
}) })
const rerankerChoices = computed(() => { const rerankerChoices = computed(() => {
return Object.keys(configStore.config.reranker_names) || [] return Object.keys(configStore?.config?.reranker_names || {}) || []
}) })
const preHandleChange = (key, e) => { const preHandleChange = (key, e) => {
@ -557,4 +557,4 @@ const openLink = (url) => {
overflow-y: auto; overflow-y: auto;
} }
} }
</style> </style>