From 1430d0e431ac3a045ec1a6f534678f7714186dab Mon Sep 17 00:00:00 2001 From: Lynn Date: Thu, 9 Jul 2026 10:19:10 +0800 Subject: [PATCH] Fix: provider name (#16733) --- api/apps/services/models_api_service.py | 37 ++-- api/apps/services/provider_api_service.py | 103 ++++++----- api/db/services/document_service.py | 7 +- api/db/services/memory_service.py | 3 +- conf/models/aliyun.json | 4 +- conf/models/avian.json | 2 +- conf/models/baichuan.json | 4 +- conf/models/baidu.json | 6 +- conf/models/cohere.json | 2 +- conf/models/fishaudio.json | 4 +- conf/models/gitee.json | 2 +- conf/models/google.json | 4 +- conf/models/hunyuan.json | 2 +- conf/models/jiekouai.json | 4 +- conf/models/lmstudio.json | 4 +- conf/models/localai.json | 2 +- conf/models/modelscope.json | 2 +- conf/models/novita.json | 2 +- conf/models/nvidia.json | 4 +- conf/models/ollama.json | 4 +- conf/models/siliconflow.json | 2 +- conf/models/vllm.json | 4 +- conf/models/voyage.json | 2 +- conf/models/xinference.json | 2 +- conf/models/xunfei.json | 4 +- docs/release_notes.md | 18 +- internal/development.md | 2 +- internal/entity/models/aliyun.go | 2 +- internal/entity/models/avian.go | 2 +- internal/entity/models/avian_test.go | 4 +- internal/entity/models/baichuan.go | 2 +- internal/entity/models/baidu.go | 2 +- internal/entity/models/cohere.go | 2 +- internal/entity/models/factory.go | 22 +-- internal/entity/models/fishaudio.go | 2 +- internal/entity/models/gitee.go | 2 +- internal/entity/models/google.go | 2 +- internal/entity/models/hunyuan.go | 2 +- internal/entity/models/hunyuan_test.go | 6 +- internal/entity/models/lmstudio.go | 2 +- internal/entity/models/localai.go | 2 +- internal/entity/models/localai_test.go | 4 +- internal/entity/models/model_test.go | 18 +- internal/entity/models/modelscope.go | 2 +- internal/entity/models/modelscope_test.go | 10 +- internal/entity/models/novita.go | 2 +- internal/entity/models/novita_test.go | 2 +- internal/entity/models/ollama.go | 2 +- internal/entity/models/siliconflow.go | 2 +- .../entity/models/sse_scanner_buffer_test.go | 10 +- internal/entity/models/vllm.go | 2 +- internal/entity/models/voyage.go | 2 +- internal/entity/models/voyage_test.go | 4 +- internal/entity/models/xinference.go | 2 +- internal/entity/models/xinference_test.go | 10 +- internal/entity/models/xunfei.go | 2 +- internal/service/deep_researcher.go | 8 +- internal/service/model_service.go | 12 +- .../task_executor_refactor/task_handler.py | 12 +- ragflow_deps/download_go_deps.py | 13 +- .../api/db/services/test_get_queue_length.py | 1 + .../common/test_llm_request_context.py | 1 + tools/scripts/mysql_migration.py | 172 ++++++------------ tools/scripts/rename_models_providers.py | 55 ++++++ web/src/components/memories-form-field.tsx | 33 ++-- 65 files changed, 333 insertions(+), 338 deletions(-) create mode 100644 tools/scripts/rename_models_providers.py diff --git a/api/apps/services/models_api_service.py b/api/apps/services/models_api_service.py index 5ebcfdb485..5b16e7dd99 100644 --- a/api/apps/services/models_api_service.py +++ b/api/apps/services/models_api_service.py @@ -130,13 +130,7 @@ def _get_model_info(tenant_id: str, default_model: str, model_type: str): logging.warning(f"Model '{model_name}' is disabled") return None - return { - "model_provider": provider_name, - "model_instance": instance_name, - "model_name": model_name, - "model_type": model_type, - "enable": True - } + return {"model_provider": provider_name, "model_instance": instance_name, "model_name": model_name, "model_type": model_type, "enable": True} def _check_model_available(tenant_id: str, provider_name: str, instance_name: str, model_name: str, model_type: str): @@ -292,14 +286,23 @@ def list_tenant_added_models(tenant_id: str, model_type_filter: str = None): target_type_records = [record for record in model_records if record.model_type & model_type_filter_bin] if model_type_filter_bin else model_records factory_rank_mapping = {factory["name"]: -_to_int(factory.get("rank", "500")) for factory in FACTORY_LLM_INFOS} - added_models = [{ - "model_type": get_model_type_human(model_record.model_type), - "name": model_record.model_name, - "provider_id": model_record.provider_id, - "provider_name": provider_info_map[model_record.provider_id].provider_name, - "instance_id": model_record.instance_id, - "instance_name": instance_info_map[model_record.instance_id].instance_name - } for model_record in target_type_records] + model_rank_map: dict = {} + for factory in FACTORY_LLM_INFOS: + for llm in factory.get("llm", []): + model_rank_map[(factory["name"], llm["llm_name"])] = _to_int(llm.get("rank", 500)) + + added_models = [ + { + "model_type": get_model_type_human(model_record.model_type), + "name": model_record.model_name, + "provider_id": model_record.provider_id, + "provider_name": provider_info_map[model_record.provider_id].provider_name, + "instance_id": model_record.instance_id, + "instance_name": instance_info_map[model_record.instance_id].instance_name, + "rank": model_rank_map.get((provider_info_map[model_record.provider_id].provider_name, model_record.model_name), 500), + } + for model_record in target_type_records + ] # Add TEI Builtin embedding model if configured compose_profiles = os.getenv("COMPOSE_PROFILES", "") @@ -316,10 +319,10 @@ def list_tenant_added_models(tenant_id: str, model_type_filter: str = None): "provider_name": "Builtin", "instance_id": "", "instance_name": "default", + "rank": model_rank_map.get(("Builtin", tei_model), 500), } ) - added_models.sort( - key=lambda x: (factory_rank_mapping.get(x["provider_name"]), x["provider_name"], x["instance_name"])) + added_models.sort(key=lambda x: (factory_rank_mapping.get(x["provider_name"]), x["provider_name"], x["instance_name"], -x["rank"], x["name"])) return True, added_models diff --git a/api/apps/services/provider_api_service.py b/api/apps/services/provider_api_service.py index e508ca2d0c..9f3f075895 100644 --- a/api/apps/services/provider_api_service.py +++ b/api/apps/services/provider_api_service.py @@ -20,7 +20,7 @@ import asyncio from common.constants import LLMType, ActiveStatusEnum, ModelVerifyStatusEnum from common.settings import FACTORY_LLM_INFOS -from api.db.joint_services.tenant_model_service import get_model_config_from_provider_instance, delete_models_by_instance_ids, delete_instances_by_provider_ids, _decode_api_key_config +from api.db.joint_services.tenant_model_service import get_model_config_from_provider_instance, delete_models_by_instance_ids, delete_instances_by_provider_ids from api.db.services.tenant_model_provider_service import TenantModelProviderService from api.db.services.tenant_model_instance_service import TenantModelInstanceService from api.db.services.tenant_model_service import TenantModelService @@ -214,7 +214,7 @@ async def list_provider_models(provider_id_or_name: str, api_key: str = None, ba remote_models = await ModelMeta[provider_name](api_key, model_base_url).get_model_list() if not static_llms and not remote_models: - return False, f"No models found for provider '{provider_id_or_name}'" + return True, [] # Merge static and remote models, preferring remote_models on name conflicts merged = {m["name"]: m for m in static_llms} @@ -257,7 +257,9 @@ def show_provider_model(provider_id_or_name: str, model_name: str): } -async def update_provider_instance(tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, instance_name: str, api_key: str|dict, base_url: str, region: str, model_info: list[dict]=None, verify: bool=True): +async def update_provider_instance( + tenant_id: str, provider_id_or_name: str, instance_id_or_name: str, instance_name: str, api_key: str | dict, base_url: str, region: str, model_info: list[dict] = None, verify: bool = True +): """ Update a provider instance. @@ -420,12 +422,9 @@ async def update_provider_instance(tenant_id: str, provider_id_or_name: str, ins if verify: verify_status = model_verify_result.get(llm_name, ModelVerifyStatusEnum.UNKNOWN.value) extra_fields["verify"] = verify_status - success, _msg = add_model_to_instance(tenant_id, provider_name, effective_instance_name, **{ - "model_type": _factory_model_types(llm), - "model_name": llm_name, - "max_tokens": llm["max_tokens"], - "extra": extra_fields - }) + success, _msg = add_model_to_instance( + tenant_id, provider_name, effective_instance_name, **{"model_type": _factory_model_types(llm), "model_name": llm_name, "max_tokens": llm["max_tokens"], "extra": extra_fields} + ) if not success: msg += _msg @@ -511,16 +510,21 @@ async def create_provider_instance(tenant_id: str, provider_id_or_name: str, ins factory_llms = factory_info[0]["llm"] for llm in factory_llms: llm_name = _factory_llm_name(llm) - success, _msg = add_model_to_instance(tenant_id, provider_name, instance_name, **{ - "model_type": _factory_model_types(llm), - "model_name": llm_name, - "max_tokens": llm["max_tokens"], - "extra": { - "is_tools": llm.get("is_tools", False), - "thinking": "thinking" in llm.get("features", []), - "verify": model_verify_result.get(llm_name, ModelVerifyStatusEnum.UNKNOWN.value) - } - }) + success, _msg = add_model_to_instance( + tenant_id, + provider_name, + instance_name, + **{ + "model_type": _factory_model_types(llm), + "model_name": llm_name, + "max_tokens": llm["max_tokens"], + "extra": { + "is_tools": llm.get("is_tools", False), + "thinking": "thinking" in llm.get("features", []), + "verify": model_verify_result.get(llm_name, ModelVerifyStatusEnum.UNKNOWN.value), + }, + }, + ) if not success: msg += _msg if msg: @@ -552,12 +556,7 @@ async def create_name_only_provider_instance(tenant_id: str, provider_name: str, if not provider_obj: return False, f"Provider '{provider_name}' does not exist" - TenantModelInstanceService.create_instance( - provider_id=provider_obj.id, - instance_name=instance_name, - api_key="", - extra=json.dumps({}) - ) + TenantModelInstanceService.create_instance(provider_id=provider_obj.id, instance_name=instance_name, api_key="", extra=json.dumps({})) return True, "success" @@ -634,10 +633,15 @@ async def verify_api_key(provider_id_or_name: str, api_key: str | dict, base_url return False, f"Provider '{provider_id_or_name}' not found", {} if model_info: - factory_llms = [{ - "model_type": _type, - "llm_name": model.get("model_name", ""), - } for model in model_info if model for _type in model.get("model_type", [])] + factory_llms = [ + { + "model_type": _type, + "llm_name": model.get("model_name", ""), + } + for model in model_info + if model + for _type in model.get("model_type", []) + ] if not factory_llms: return False, f"No valid models found for provider '{provider_id_or_name}'", {} else: @@ -820,7 +824,7 @@ def show_provider_instance(tenant_id: str, provider_id_or_name: str, instance_id "region": extra_fields.get("region", ""), "base_url": extra_fields.get("base_url", ""), "api_key": instance_obj.api_key, - "status": instance_obj.status + "status": instance_obj.status, } @@ -888,10 +892,17 @@ def list_instance_models(tenant_id: str, provider_id_or_name: str, instance_id_o if not factory_info: return False, f"Provider '{provider_id_or_name}' not found" llms = factory_info[0].get("llm", []) - models = [{"name": llm["llm_name"]} for llm in llms] - models.sort(key=lambda x: x["name"]) + models = [{"name": llm["llm_name"], "rank": _to_int(llm.get("rank", 500))} for llm in llms] + models.sort(key=lambda x: (-x["rank"], x["name"])) return True, models + # Build rank mapping from LLM dictionary for the provider + factory_info = [f for f in FACTORY_LLM_INFOS if f["name"] == provider_obj.provider_name] + model_rank_map = {} + if factory_info: + for llm in factory_info[0].get("llm", []): + model_rank_map[llm["llm_name"]] = _to_int(llm.get("rank", 500)) + # Get instance instance_obj = None if instance_id_or_name: @@ -907,14 +918,18 @@ def list_instance_models(tenant_id: str, provider_id_or_name: str, instance_id_o model_list = [] for model in model_objs: model_extra = json.loads(model.extra) - model_list.append({ - "name": model.model_name, - "model_type": get_model_type_human(model.model_type), - "max_tokens": model_extra.get("max_tokens", 8192) if model.extra else 8192, - "status": model.status, - "verify": model_extra.get("verify", ModelVerifyStatusEnum.UNKNOWN.value), - "features": (["is_tools"] if model_extra.get("is_tools") else []) + (["thinking"] if model_extra.get("thinking") else []) - }) + model_list.append( + { + "name": model.model_name, + "model_type": get_model_type_human(model.model_type), + "max_tokens": model_extra.get("max_tokens", 8192) if model.extra else 8192, + "status": model.status, + "verify": model_extra.get("verify", ModelVerifyStatusEnum.UNKNOWN.value), + "features": (["is_tools"] if model_extra.get("is_tools") else []) + (["thinking"] if model_extra.get("thinking") else []), + "rank": model_rank_map.get(model.model_name, 500), + } + ) + model_list.sort(key=lambda x: (-x["rank"], x["name"])) return True, model_list @@ -984,13 +999,7 @@ def add_model_to_instance(tenant_id: str, provider_id_or_name: str, instance_id_ extra_fields.update({"thinking": "thinking" in target_model[0].get("features", [])}) if extra: extra_fields.update(extra) - TenantModelService.insert( - model_name=model_name, - provider_id=provider_obj.id, - instance_id=instance_obj.id, - model_type=model_type_bin, - extra=json.dumps(extra_fields) - ) + TenantModelService.insert(model_name=model_name, provider_id=provider_obj.id, instance_id=instance_obj.id, model_type=model_type_bin, extra=json.dumps(extra_fields)) return True, "success" diff --git a/api/db/services/document_service.py b/api/db/services/document_service.py index b9642dffa0..06f32c5b11 100644 --- a/api/db/services/document_service.py +++ b/api/db/services/document_service.py @@ -1210,12 +1210,7 @@ def get_pending_task_count(priority=None): query = ( Task.select(fn.COUNT(Task.id)) .join(Document, on=(Task.doc_id == Document.id)) - .where( - (Task.progress == 0) - & ((Document.run.is_null(True)) | (Document.run != TaskStatus.CANCEL.value)) - & (Document.progress >= 0) - & (Document.status == StatusEnum.VALID.value) - ) + .where((Task.progress == 0) & ((Document.run.is_null(True)) | (Document.run != TaskStatus.CANCEL.value)) & (Document.progress >= 0) & (Document.status == StatusEnum.VALID.value)) ) if priority is not None: query = query.where(Task.priority == priority) diff --git a/api/db/services/memory_service.py b/api/db/services/memory_service.py index 5dab6361df..933ea613c2 100644 --- a/api/db/services/memory_service.py +++ b/api/db/services/memory_service.py @@ -109,8 +109,7 @@ class MemoryService(CommonService): @classmethod @DB.connection_context() - def create_memory(cls, tenant_id: str, name: str, memory_type: List[str], embd_id: str, llm_id: str, - tenant_embd_id: str | None = None, tenant_llm_id: str | None = None): + def create_memory(cls, tenant_id: str, name: str, memory_type: List[str], embd_id: str, llm_id: str, tenant_embd_id: str | None = None, tenant_llm_id: str | None = None): # Deduplicate name within tenant memory_name = duplicate_name(cls.query, name=name, tenant_id=tenant_id) if len(memory_name) > MEMORY_NAME_LIMIT: diff --git a/conf/models/aliyun.json b/conf/models/aliyun.json index 66a569bfa7..aac841b531 100644 --- a/conf/models/aliyun.json +++ b/conf/models/aliyun.json @@ -1,5 +1,5 @@ { - "name": "Aliyun", + "name": "Tongyi-Qianwen", "rank": 983, "url": { "default": "https://dashscope.aliyuncs.com", @@ -50,4 +50,4 @@ ] } } -} \ No newline at end of file +} diff --git a/conf/models/avian.json b/conf/models/avian.json index 40c0d4f524..b926350b1e 100644 --- a/conf/models/avian.json +++ b/conf/models/avian.json @@ -1,5 +1,5 @@ { - "name": "avian", + "name": "Avian", "url": { "default": "https://api.avian.io" }, diff --git a/conf/models/baichuan.json b/conf/models/baichuan.json index c7bc5f1c0d..9a41310afa 100644 --- a/conf/models/baichuan.json +++ b/conf/models/baichuan.json @@ -1,5 +1,5 @@ { - "name": "Baichuan", + "name": "BaiChuan", "url": { "default": "https://api.baichuan-ai.com/v1" }, @@ -87,4 +87,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/conf/models/baidu.json b/conf/models/baidu.json index b165465269..21985aa3cd 100644 --- a/conf/models/baidu.json +++ b/conf/models/baidu.json @@ -1,5 +1,5 @@ { - "name": "Baidu", + "name": "BaiduYiyan", "url": { "default": "https://qianfan.baidubce.com/v2" }, @@ -44,7 +44,7 @@ { "name": "qwen3-32b", "max_tokens": 30720, - "model_types":[ + "model_types": [ "chat" ] }, @@ -84,4 +84,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/conf/models/cohere.json b/conf/models/cohere.json index 98a2c1d576..a985534485 100644 --- a/conf/models/cohere.json +++ b/conf/models/cohere.json @@ -1,5 +1,5 @@ { - "name": "CoHere", + "name": "Cohere", "rank": 990, "url": { "default": "https://api.cohere.com" diff --git a/conf/models/fishaudio.json b/conf/models/fishaudio.json index aa6beda964..836cd215d2 100644 --- a/conf/models/fishaudio.json +++ b/conf/models/fishaudio.json @@ -1,5 +1,5 @@ { - "name": "FishAudio", + "name": "Fish Audio", "url": { "default": "https://api.fish.audio" }, @@ -33,4 +33,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/conf/models/gitee.json b/conf/models/gitee.json index 629a7e2892..6ab71759f1 100644 --- a/conf/models/gitee.json +++ b/conf/models/gitee.json @@ -1,5 +1,5 @@ { - "name": "Gitee", + "name": "GiteeAI", "url": { "default": "https://api.moark.ai/v1", "china": "https://api.moark.com/v1", diff --git a/conf/models/google.json b/conf/models/google.json index 96bbb22c4f..d821be59f1 100644 --- a/conf/models/google.json +++ b/conf/models/google.json @@ -1,5 +1,5 @@ { - "name": "Google", + "name": "Gemini", "rank": 997, "url": { "default": "https://generativelanguage.googleapis.com" @@ -42,4 +42,4 @@ ] } } -} \ No newline at end of file +} diff --git a/conf/models/hunyuan.json b/conf/models/hunyuan.json index 5ec80244c1..04fd0602d1 100644 --- a/conf/models/hunyuan.json +++ b/conf/models/hunyuan.json @@ -1,5 +1,5 @@ { - "name": "HunYuan", + "name": "Tencent Hunyuan", "url": { "default": "https://api.hunyuan.cloud.tencent.com/v1" }, diff --git a/conf/models/jiekouai.json b/conf/models/jiekouai.json index 4591c4e5a9..c5f46cae10 100644 --- a/conf/models/jiekouai.json +++ b/conf/models/jiekouai.json @@ -1,5 +1,5 @@ { - "name": "JieKouAI", + "name": "Jiekou.AI", "url": { "default": "https://api.jiekou.ai" }, @@ -103,4 +103,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/conf/models/lmstudio.json b/conf/models/lmstudio.json index a5293ffb9d..4c0f4b989e 100644 --- a/conf/models/lmstudio.json +++ b/conf/models/lmstudio.json @@ -1,9 +1,9 @@ { - "name": "lmstudio", + "name": "LM-Studio", "url_suffix": { "chat": "chat/completions", "models": "models", "embedding": "embeddings" }, "class": "local" -} \ No newline at end of file +} diff --git a/conf/models/localai.json b/conf/models/localai.json index 9222a95218..301e3b5f30 100644 --- a/conf/models/localai.json +++ b/conf/models/localai.json @@ -1,5 +1,5 @@ { - "name": "localai", + "name": "LocalAI", "url_suffix": { "chat": "chat/completions", "models": "models", diff --git a/conf/models/modelscope.json b/conf/models/modelscope.json index 02a9ebe0f8..691789ddd0 100644 --- a/conf/models/modelscope.json +++ b/conf/models/modelscope.json @@ -1,5 +1,5 @@ { - "name": "modelscope", + "name": "ModelScope", "url_suffix": { "chat": "v1/chat/completions", "models": "v1/models" diff --git a/conf/models/novita.json b/conf/models/novita.json index dfc11e0382..2f2612d05d 100644 --- a/conf/models/novita.json +++ b/conf/models/novita.json @@ -1,5 +1,5 @@ { - "name": "Novita", + "name": "NovitaAI", "url": { "default": "https://api.novita.ai" }, diff --git a/conf/models/nvidia.json b/conf/models/nvidia.json index b711b76145..608db40a35 100644 --- a/conf/models/nvidia.json +++ b/conf/models/nvidia.json @@ -1,5 +1,5 @@ { - "name": "Nvidia", + "name": "NVIDIA", "url": { "default": "https://integrate.api.nvidia.com/v1" }, @@ -370,4 +370,4 @@ } } ] -} \ No newline at end of file +} diff --git a/conf/models/ollama.json b/conf/models/ollama.json index 3a470d2a45..cab76fe2c4 100644 --- a/conf/models/ollama.json +++ b/conf/models/ollama.json @@ -1,5 +1,5 @@ { - "name": "ollama", + "name": "Ollama", "rank": 978, "url_suffix": { "chat": "api/chat", @@ -7,4 +7,4 @@ "embedding": "api/embed" }, "class": "local" -} \ No newline at end of file +} diff --git a/conf/models/siliconflow.json b/conf/models/siliconflow.json index d7b8e18f3d..f1d6b523b0 100644 --- a/conf/models/siliconflow.json +++ b/conf/models/siliconflow.json @@ -1,5 +1,5 @@ { - "name": "SiliconFlow", + "name": "SILICONFLOW", "rank": 980, "url": { "default": "https://api.siliconflow.cn/v1" diff --git a/conf/models/vllm.json b/conf/models/vllm.json index 2a8d8157f0..5ab96a4cd1 100644 --- a/conf/models/vllm.json +++ b/conf/models/vllm.json @@ -1,5 +1,5 @@ { - "name": "vllm", + "name": "VLLM", "rank": 975, "url_suffix": { "chat": "chat/completions", @@ -8,4 +8,4 @@ "rerank": "rerank" }, "class": "local" -} \ No newline at end of file +} diff --git a/conf/models/voyage.json b/conf/models/voyage.json index 8ad059cf0f..2873ff4539 100644 --- a/conf/models/voyage.json +++ b/conf/models/voyage.json @@ -1,5 +1,5 @@ { - "name": "Voyage", + "name": "Voyage AI", "url": { "default": "https://api.voyageai.com" }, diff --git a/conf/models/xinference.json b/conf/models/xinference.json index cf8fb61fa9..b77e9de00b 100644 --- a/conf/models/xinference.json +++ b/conf/models/xinference.json @@ -1,5 +1,5 @@ { - "name": "xinference", + "name": "Xinference", "url_suffix": { "chat": "v1/chat/completions", "embedding": "v1/embeddings", diff --git a/conf/models/xunfei.json b/conf/models/xunfei.json index 6ab0385b55..4473b74e3b 100644 --- a/conf/models/xunfei.json +++ b/conf/models/xunfei.json @@ -1,5 +1,5 @@ { - "name": "XunFei", + "name": "XunFei Spark", "url": { "default": "https://spark-api-open.xf-yun.com" }, @@ -20,4 +20,4 @@ } } ] -} \ No newline at end of file +} diff --git a/docs/release_notes.md b/docs/release_notes.md index 1f8b3cc3cf..574c123da3 100644 --- a/docs/release_notes.md +++ b/docs/release_notes.md @@ -425,7 +425,7 @@ Released on December 31, 2025. ### Fixed issues -- Memory: +- Memory: - The RAGFlow server failed to start if an empty memory object existed. - Unable to delete a newly created empty Memory. - RAG: MDX file parsing was not supported. @@ -661,7 +661,7 @@ Ecommerce Customer Service Workflow: A template designed to handle enquiries abo ### Fixed issues -- Dataset: +- Dataset: - Unable to share resources with the team. - Inappropriate restrictions on the number and size of uploaded files. - Chat: @@ -677,13 +677,13 @@ Released on August 20, 2025. ### Improvements -- Revamps the user interface for the **Datasets**, **Chat**, and **Search** pages. +- Revamps the user interface for the **Datasets**, **Chat**, and **Search** pages. - Search and Chat: Introduces document-level metadata filtering, allowing automatic or manual filtering during chats or searches. - Search: Supports creating search apps tailored to various business scenarios - Chat: Supports comparing answer performance of up to three chat model settings on a single **Chat** page. -- Agent: - - Implements a toggle in the **Agent** component to enable or disable citation. - - Introduces a drag-and-drop method for creating components. +- Agent: + - Implements a toggle in the **Agent** component to enable or disable citation. + - Introduces a drag-and-drop method for creating components. - Documentation: Corrects inaccuracies in the API reference. ### New Agent templates @@ -693,8 +693,8 @@ Released on August 20, 2025. ### Fixed issues - The timeout mechanism introduced in v0.20.0 caused tasks like GraphRAG to halt. -- Predefined opening greeting in the **Agent** component was missing during conversations. -- An automatic line break issue in the prompt editor. +- Predefined opening greeting in the **Agent** component was missing during conversations. +- An automatic line break issue in the prompt editor. - A memory leak issue caused by PyPDF. [#9469](https://github.com/infiniflow/ragflow/pull/9469) ### API changes @@ -778,7 +778,7 @@ Released on June 23, 2025. ### Newly supported models -- Qwen 3 Embedding. [#8184](https://github.com/infiniflow/ragflow/pull/8184) +- Qwen 3 Embedding. [#8184](https://github.com/infiniflow/ragflow/pull/8184) - Voyage Multimodal 3. [#7987](https://github.com/infiniflow/ragflow/pull/7987) ## v0.19.0 diff --git a/internal/development.md b/internal/development.md index 2a3d24edaa..84f52ecea5 100644 --- a/internal/development.md +++ b/internal/development.md @@ -13,7 +13,7 @@ sudo apt update test -f /usr/share/doc/kitware-archive-keyring/copyright || sudo rm /usr/share/keyrings/kitware-archive-keyring.gpg sudo apt install -y kitware-archive-keyring sudo apt update -sudo apt install -y cmake +sudo apt install -y cmake ``` ### 1.2 Install clang-20 diff --git a/internal/entity/models/aliyun.go b/internal/entity/models/aliyun.go index a9c22e5e3f..9aeec0f37d 100644 --- a/internal/entity/models/aliyun.go +++ b/internal/entity/models/aliyun.go @@ -48,7 +48,7 @@ func (a *AliyunModel) NewInstance(baseURL map[string]string) ModelDriver { } func (a *AliyunModel) Name() string { - return "aliyun" + return "Tongyi-Qianwen" } func (a *AliyunModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/avian.go b/internal/entity/models/avian.go index 9281b2abbd..1d757ed4c9 100644 --- a/internal/entity/models/avian.go +++ b/internal/entity/models/avian.go @@ -49,7 +49,7 @@ func (a *AvianModel) NewInstance(baseURL map[string]string) ModelDriver { } func (a *AvianModel) Name() string { - return "avian" + return "Avian" } func (a *AvianModel) chatPayload(modelName string, messages []Message, stream bool, chatModelConfig *ChatConfig) map[string]interface{} { diff --git a/internal/entity/models/avian_test.go b/internal/entity/models/avian_test.go index 464c357c51..934a49de25 100644 --- a/internal/entity/models/avian_test.go +++ b/internal/entity/models/avian_test.go @@ -63,8 +63,8 @@ func newAvianForTest(baseURL string) *AvianModel { } func TestAvianName(t *testing.T) { - if got := newAvianForTest("http://unused").Name(); got != "avian" { - t.Errorf("Name()=%q, want avian", got) + if got := newAvianForTest("http://unused").Name(); got != "Avian" { + t.Errorf("Name()=%q, want Avian", got) } } diff --git a/internal/entity/models/baichuan.go b/internal/entity/models/baichuan.go index d659eb82be..8c503143ef 100644 --- a/internal/entity/models/baichuan.go +++ b/internal/entity/models/baichuan.go @@ -46,7 +46,7 @@ func (b *BaichuanModel) NewInstance(baseURL map[string]string) ModelDriver { } func (b *BaichuanModel) Name() string { - return "baichuan" + return "BaiChuan" } func (b *BaichuanModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/baidu.go b/internal/entity/models/baidu.go index 49ecf22636..1b46cbb1fa 100644 --- a/internal/entity/models/baidu.go +++ b/internal/entity/models/baidu.go @@ -47,7 +47,7 @@ func NewBaiduModel(baseURL map[string]string, urlSuffix URLSuffix) *BaiduModel { } func (b *BaiduModel) Name() string { - return "baidu" + return "BaiduYiyan" } func (b *BaiduModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/cohere.go b/internal/entity/models/cohere.go index f73613d82e..e4d7a5fe7c 100644 --- a/internal/entity/models/cohere.go +++ b/internal/entity/models/cohere.go @@ -49,7 +49,7 @@ func NewCoHereModel(baseURL map[string]string, urlSuffix URLSuffix) *CoHereModel } func (c *CoHereModel) Name() string { - return "cohere" + return "Cohere" } func (c *CoHereModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/factory.go b/internal/entity/models/factory.go index 714c772acc..bd08df771d 100644 --- a/internal/entity/models/factory.go +++ b/internal/entity/models/factory.go @@ -43,13 +43,13 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string return NewMoonshotModel(baseURL, urlSuffix), nil case "minimax": return NewMinimaxModel(baseURL, urlSuffix), nil - case "gitee": + case "giteeai": return NewGiteeModel(baseURL, urlSuffix), nil case "siliconflow": return NewSiliconflowModel(baseURL, urlSuffix), nil - case "google": + case "gemini": return NewGoogleModel(baseURL, urlSuffix), nil - case "aliyun": + case "tongyi-qianwen": return NewAliyunModel(baseURL, urlSuffix), nil case "volcengine": return NewVolcEngine(baseURL, urlSuffix), nil @@ -57,7 +57,7 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string return NewVllmModel(baseURL, urlSuffix), nil case "xai": return NewXAIModel(baseURL, urlSuffix), nil - case "lmstudio": + case "lm-studio": return NewLmStudioModel(baseURL, urlSuffix), nil case "ollama": return NewOllamaModel(baseURL, urlSuffix), nil @@ -73,13 +73,13 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string return NewOpenRouterModel(baseURL, urlSuffix), nil case "huggingface": return NewHuggingFaceModel(baseURL, urlSuffix), nil - case "baidu": + case "baiduyiyan": return NewBaiduModel(baseURL, urlSuffix), nil case "cohere": return NewCoHereModel(baseURL, urlSuffix), nil case "cometapi": return NewCometAPIModel(baseURL, urlSuffix), nil - case "fishaudio": + case "fish audio": return NewFishAudioModel(baseURL, urlSuffix), nil case "mistral": return NewMistralModel(baseURL, urlSuffix), nil @@ -101,13 +101,13 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string return NewModelScopeModel(baseURL, urlSuffix), nil case "longcat": return NewLongCatModel(baseURL, urlSuffix), nil - case "hunyuan": + case "tencent hunyuan": return NewHunyuanModel(baseURL, urlSuffix), nil case "tokenpony": return NewTokenPonyModel(baseURL, urlSuffix), nil case "tokenhub": return NewTokenHubModel(baseURL, urlSuffix), nil - case "novita": + case "novitaai": return NewNovitaModel(baseURL, urlSuffix), nil case "avian": return NewAvianModel(baseURL, urlSuffix), nil @@ -117,17 +117,17 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string return NewTogetherAIModel(baseURL, urlSuffix), nil case "ppio": return NewPPIOModel(baseURL, urlSuffix), nil - case "voyage": + case "voyage ai": return NewVoyageModel(baseURL, urlSuffix), nil case "paddleocr.net": return NewPaddleOCRModel(baseURL, urlSuffix), nil - case "xunfei": + case "xunfei spark": return NewXunFeiModel(baseURL, urlSuffix), nil case "deepinfra": return NewDeepInfraModel(baseURL, urlSuffix), nil case "mineru.net": return NewMinerUModel(baseURL, urlSuffix), nil - case "jiekouai": + case "jiekou.ai": return NewJieKouAIModel(baseURL, urlSuffix), nil case "302.ai": return NewAI302Model(baseURL, urlSuffix), nil diff --git a/internal/entity/models/fishaudio.go b/internal/entity/models/fishaudio.go index 9411df1e34..56a81232af 100644 --- a/internal/entity/models/fishaudio.go +++ b/internal/entity/models/fishaudio.go @@ -50,7 +50,7 @@ func (f *FishAudioModel) NewInstance(baseURL map[string]string) ModelDriver { } func (f *FishAudioModel) Name() string { - return "fishaudio" + return "Fish Audio" } func (f *FishAudioModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/gitee.go b/internal/entity/models/gitee.go index 130a625614..2be4b9e716 100644 --- a/internal/entity/models/gitee.go +++ b/internal/entity/models/gitee.go @@ -50,7 +50,7 @@ func (g *GiteeModel) NewInstance(baseURL map[string]string) ModelDriver { } func (g *GiteeModel) Name() string { - return "gitee" + return "GiteeAI" } // ChatWithMessages sends multiple messages with roles and returns response diff --git a/internal/entity/models/google.go b/internal/entity/models/google.go index fc37ed03a1..11b0fe48b6 100644 --- a/internal/entity/models/google.go +++ b/internal/entity/models/google.go @@ -97,7 +97,7 @@ func (g *GoogleModel) NewInstance(baseURL map[string]string) ModelDriver { } func (g *GoogleModel) Name() string { - return "google" + return "Gemini" } func (g *GoogleModel) clientConfig(apiKey string, apiConfig *APIConfig) *genai.ClientConfig { diff --git a/internal/entity/models/hunyuan.go b/internal/entity/models/hunyuan.go index 096892fc86..d6b6c333ff 100644 --- a/internal/entity/models/hunyuan.go +++ b/internal/entity/models/hunyuan.go @@ -47,7 +47,7 @@ func (h *HunyuanModel) NewInstance(baseURL map[string]string) ModelDriver { } func (h *HunyuanModel) Name() string { - return "hunyuan" + return "Tencent Hunyuan" } func (h *HunyuanModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/hunyuan_test.go b/internal/entity/models/hunyuan_test.go index 5bc42795d9..754c31eca8 100644 --- a/internal/entity/models/hunyuan_test.go +++ b/internal/entity/models/hunyuan_test.go @@ -80,13 +80,13 @@ func newHunyuanForTest(baseURL string) *HunyuanModel { } func TestHunyuanName(t *testing.T) { - if got := newHunyuanForTest("http://unused").Name(); got != "hunyuan" { - t.Errorf("Name()=%q, want %q", got, "hunyuan") + if got := newHunyuanForTest("http://unused").Name(); got != "Tencent Hunyuan" { + t.Errorf("Name()=%q, want %q", got, "Tencent Hunyuan") } } func TestHunyuanFactory(t *testing.T) { - driver, err := NewModelFactory().CreateModelDriver("Hunyuan", map[string]string{"default": "http://unused"}, URLSuffix{}) + driver, err := NewModelFactory().CreateModelDriver("Tencent Hunyuan", map[string]string{"default": "http://unused"}, URLSuffix{}) if err != nil { t.Fatalf("CreateModelDriver: %v", err) } diff --git a/internal/entity/models/lmstudio.go b/internal/entity/models/lmstudio.go index f3e3ac7818..5d446624d3 100644 --- a/internal/entity/models/lmstudio.go +++ b/internal/entity/models/lmstudio.go @@ -49,7 +49,7 @@ func (l *LmStudioModel) NewInstance(baseURL map[string]string) ModelDriver { } func (l *LmStudioModel) Name() string { - return "lmstudio" + return "LM-Studio" } // ChatWithMessages sends multiple messages with roles and returns response diff --git a/internal/entity/models/localai.go b/internal/entity/models/localai.go index 8112c2d1d1..6a79db2450 100644 --- a/internal/entity/models/localai.go +++ b/internal/entity/models/localai.go @@ -53,7 +53,7 @@ func (l *LocalAIModel) NewInstance(baseURL map[string]string) ModelDriver { } func (l *LocalAIModel) Name() string { - return "localai" + return "LocalAI" } var localAIReasoningFields = []string{"reasoning_content", "reasoning", "thinking"} diff --git a/internal/entity/models/localai_test.go b/internal/entity/models/localai_test.go index f8b89da4ae..24f787521d 100644 --- a/internal/entity/models/localai_test.go +++ b/internal/entity/models/localai_test.go @@ -37,8 +37,8 @@ func withLocalAIIdleTimeout(t *testing.T, d time.Duration) { func TestLocalAIName(t *testing.T) { l := newLocalAIForTest("http://unused") - if got := l.Name(); got != "localai" { - t.Errorf("Name()=%q, want %q", got, "localai") + if got := l.Name(); got != "LocalAI" { + t.Errorf("Name()=%q, want %q", got, "LocalAI") } } diff --git a/internal/entity/models/model_test.go b/internal/entity/models/model_test.go index 76686cf92b..fa5029d540 100644 --- a/internal/entity/models/model_test.go +++ b/internal/entity/models/model_test.go @@ -182,12 +182,12 @@ func TestProviderConfigsLoadURLSuffixKeys(t *testing.T) { } pm := GetProviderManager() - cohere := pm.FindProvider("CoHere") + cohere := pm.FindProvider("Cohere") if cohere == nil { - t.Fatal("CoHere provider not found") + t.Fatal("Cohere provider not found") } if cohere.URLSuffix.Embedding != "v2/embed" { - t.Errorf("CoHere embedding suffix=%q", cohere.URLSuffix.Embedding) + t.Errorf("Cohere embedding suffix=%q", cohere.URLSuffix.Embedding) } xAI := pm.FindProvider("xAI") @@ -330,9 +330,9 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { } pm := GetProviderManager() - provider := pm.FindProvider("SiliconFlow") + provider := pm.FindProvider("SILICONFLOW") if provider == nil { - t.Fatal("SiliconFlow provider not found") + t.Fatal("SILICONFLOW provider not found") } if provider.URL["default"] != "https://api.siliconflow.cn/v1" { t.Errorf("default URL=%q", provider.URL["default"]) @@ -343,14 +343,14 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { if _, ok := provider.ModelDriver.(*SiliconflowModel); !ok { t.Fatalf("ModelDriver=%T, want *models.SiliconflowModel", provider.ModelDriver) } - if provider.ModelDriver.Name() != "siliconflow" { + if provider.ModelDriver.Name() != "SILICONFLOW" { t.Errorf("ModelDriver.Name()=%q", provider.ModelDriver.Name()) } if len(provider.Models) != 13 { - t.Fatalf("SiliconFlow model count=%d, want 13", len(provider.Models)) + t.Fatalf("SILICONFLOW model count=%d, want 13", len(provider.Models)) } - deepSeekV4Pro, err := pm.GetModelByName("SiliconFlow", "Pro/deepseek-ai/DeepSeek-V4-Pro") + deepSeekV4Pro, err := pm.GetModelByName("SILICONFLOW", "Pro/deepseek-ai/DeepSeek-V4-Pro") if err != nil { t.Fatalf("GetModelByName DeepSeek-V4-Pro: %v", err) } @@ -361,7 +361,7 @@ func TestSiliconFlowProviderConfigLoadsLatestProModels(t *testing.T) { t.Errorf("DeepSeek-V4-Pro model types=%v, want chat", deepSeekV4Pro.ModelTypes) } - kimiK26, err := pm.GetModelByName("SiliconFlow", "Pro/moonshotai/Kimi-K2.6") + kimiK26, err := pm.GetModelByName("SILICONFLOW", "Pro/moonshotai/Kimi-K2.6") if err != nil { t.Fatalf("GetModelByName Kimi-K2.6: %v", err) } diff --git a/internal/entity/models/modelscope.go b/internal/entity/models/modelscope.go index 97d1d55515..491d1b88ed 100644 --- a/internal/entity/models/modelscope.go +++ b/internal/entity/models/modelscope.go @@ -70,7 +70,7 @@ func (m *ModelScopeModel) NewInstance(baseURL map[string]string) ModelDriver { } func (m *ModelScopeModel) Name() string { - return "modelscope" + return "ModelScope" } func normalizeModelScopeBaseURL(base string) string { diff --git a/internal/entity/models/modelscope_test.go b/internal/entity/models/modelscope_test.go index 535e9eaabf..d7d597cef7 100644 --- a/internal/entity/models/modelscope_test.go +++ b/internal/entity/models/modelscope_test.go @@ -47,8 +47,8 @@ func withModelScopeIdleTimeout(t *testing.T, d time.Duration) { func TestModelScopeName(t *testing.T) { m := newModelScopeForTest("http://unused") - if got := m.Name(); got != "modelscope" { - t.Errorf("Name()=%q, want %q", got, "modelscope") + if got := m.Name(); got != "ModelScope" { + t.Errorf("Name()=%q, want %q", got, "ModelScope") } } @@ -70,12 +70,12 @@ func TestNormalizeModelScopeBaseURL(t *testing.T) { } func TestModelScopeFactoryRoute(t *testing.T) { - driver, err := NewModelFactory().CreateModelDriver("modelscope", map[string]string{"default": "http://unused"}, URLSuffix{}) + driver, err := NewModelFactory().CreateModelDriver("ModelScope", map[string]string{"default": "http://unused"}, URLSuffix{}) if err != nil { t.Fatalf("CreateModelDriver: %v", err) } - if driver.Name() != "modelscope" { - t.Errorf("driver.Name()=%q, want modelscope", driver.Name()) + if driver.Name() != "ModelScope" { + t.Errorf("driver.Name()=%q, want ModelScope", driver.Name()) } } diff --git a/internal/entity/models/novita.go b/internal/entity/models/novita.go index 15581ab40e..be19bd1ca9 100644 --- a/internal/entity/models/novita.go +++ b/internal/entity/models/novita.go @@ -48,7 +48,7 @@ func (n *NovitaModel) NewInstance(baseURL map[string]string) ModelDriver { } func (n *NovitaModel) Name() string { - return "novita" + return "NovitaAI" } const ( diff --git a/internal/entity/models/novita_test.go b/internal/entity/models/novita_test.go index 82067ed47b..81ad829f7e 100644 --- a/internal/entity/models/novita_test.go +++ b/internal/entity/models/novita_test.go @@ -249,7 +249,7 @@ func TestNovitaThinkExtractorLessThanIsNotTagStart(t *testing.T) { // ---- driver methods ---- func TestNovitaName(t *testing.T) { - if got := newNovitaForTest("http://unused").Name(); got != "novita" { + if got := newNovitaForTest("http://unused").Name(); got != "NovitaAI" { t.Errorf("Name()=%q", got) } } diff --git a/internal/entity/models/ollama.go b/internal/entity/models/ollama.go index 1facb4e5b2..734562752d 100644 --- a/internal/entity/models/ollama.go +++ b/internal/entity/models/ollama.go @@ -69,7 +69,7 @@ func (o *OllamaModel) NewInstance(baseURL map[string]string) ModelDriver { } func (o *OllamaModel) Name() string { - return "ollama" + return "Ollama" } func (o *OllamaModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/entity/models/siliconflow.go b/internal/entity/models/siliconflow.go index b2e586202f..0f09768aad 100644 --- a/internal/entity/models/siliconflow.go +++ b/internal/entity/models/siliconflow.go @@ -52,7 +52,7 @@ func (s *SiliconflowModel) NewInstance(baseURL map[string]string) ModelDriver { } func (s *SiliconflowModel) Name() string { - return "siliconflow" + return "SILICONFLOW" } // SiliconflowRerankRequest represents SILICONFLOW rerank request diff --git a/internal/entity/models/sse_scanner_buffer_test.go b/internal/entity/models/sse_scanner_buffer_test.go index c458921739..0e76d90a36 100644 --- a/internal/entity/models/sse_scanner_buffer_test.go +++ b/internal/entity/models/sse_scanner_buffer_test.go @@ -50,16 +50,16 @@ func TestChatStreamLargeChunkNotTruncated(t *testing.T) { build func(string) chatStreamer }{ {"deepinfra", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewDeepInfraModel(b, s) })}, - {"vllm", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewVllmModel(b, s) })}, + {"VLLM", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewVllmModel(b, s) })}, {"openrouter", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewOpenRouterModel(b, s) })}, - {"siliconflow", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewSiliconflowModel(b, s) })}, + {"SILICONFLOW", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewSiliconflowModel(b, s) })}, {"moonshot", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewMoonshotModel(b, s) })}, {"deepseek", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewDeepSeekModel(b, s) })}, {"nvidia", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewNvidiaModel(b, s) })}, - {"lmstudio", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewLmStudioModel(b, s) })}, - {"gitee", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewGiteeModel(b, s) })}, + {"LM-Studio", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewLmStudioModel(b, s) })}, + {"GiteeAI", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewGiteeModel(b, s) })}, {"tokenhub", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewTokenHubModel(b, s) })}, - {"jiekouai", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewJieKouAIModel(b, s) })}, + {"Jiekou.AI", build(func(b map[string]string, s URLSuffix) chatStreamer { return NewJieKouAIModel(b, s) })}, } for _, tc := range cases { diff --git a/internal/entity/models/vllm.go b/internal/entity/models/vllm.go index 3b78be34e4..af608fcd94 100644 --- a/internal/entity/models/vllm.go +++ b/internal/entity/models/vllm.go @@ -49,7 +49,7 @@ func (v *VllmModel) NewInstance(baseURL map[string]string) ModelDriver { } func (v *VllmModel) Name() string { - return "vllm" + return "VLLM" } // ChatWithMessages sends multiple messages with roles and returns response diff --git a/internal/entity/models/voyage.go b/internal/entity/models/voyage.go index 3a14428cf6..95b009baaa 100644 --- a/internal/entity/models/voyage.go +++ b/internal/entity/models/voyage.go @@ -47,7 +47,7 @@ func (v *VoyageModel) NewInstance(baseURL map[string]string) ModelDriver { } func (v *VoyageModel) Name() string { - return "voyage" + return "Voyage AI" } type voyageEmbeddingData struct { diff --git a/internal/entity/models/voyage_test.go b/internal/entity/models/voyage_test.go index accd258959..7fad9cf2e6 100644 --- a/internal/entity/models/voyage_test.go +++ b/internal/entity/models/voyage_test.go @@ -46,8 +46,8 @@ func newVoyageForTest(baseURL string) *VoyageModel { } func TestVoyageName(t *testing.T) { - if got := newVoyageForTest("http://unused").Name(); got != "voyage" { - t.Errorf("Name()=%q, want %q", got, "voyage") + if got := newVoyageForTest("http://unused").Name(); got != "Voyage AI" { + t.Errorf("Name()=%q, want %q", got, "Voyage AI") } } diff --git a/internal/entity/models/xinference.go b/internal/entity/models/xinference.go index ebc402c8ee..7908f92f1e 100644 --- a/internal/entity/models/xinference.go +++ b/internal/entity/models/xinference.go @@ -72,7 +72,7 @@ func (x *XinferenceModel) NewInstance(baseURL map[string]string) ModelDriver { } func (x *XinferenceModel) Name() string { - return "xinference" + return "Xinference" } func normalizeXinferenceBaseURL(base string) string { diff --git a/internal/entity/models/xinference_test.go b/internal/entity/models/xinference_test.go index 5b2ae5f7d9..81a03f222d 100644 --- a/internal/entity/models/xinference_test.go +++ b/internal/entity/models/xinference_test.go @@ -33,8 +33,8 @@ func withXinferenceIdleTimeout(t *testing.T, d time.Duration) { func TestXinferenceName(t *testing.T) { x := newXinferenceForTest("http://unused") - if got := x.Name(); got != "xinference" { - t.Errorf("Name()=%q, want %q", got, "xinference") + if got := x.Name(); got != "Xinference" { + t.Errorf("Name()=%q, want %q", got, "Xinference") } } @@ -56,12 +56,12 @@ func TestNormalizeXinferenceBaseURL(t *testing.T) { } func TestXinferenceFactoryRoute(t *testing.T) { - driver, err := NewModelFactory().CreateModelDriver("xinference", map[string]string{"default": "http://unused"}, URLSuffix{}) + driver, err := NewModelFactory().CreateModelDriver("Xinference", map[string]string{"default": "http://unused"}, URLSuffix{}) if err != nil { t.Fatalf("CreateModelDriver: %v", err) } - if driver.Name() != "xinference" { - t.Errorf("driver.Name()=%q, want xinference", driver.Name()) + if driver.Name() != "Xinference" { + t.Errorf("driver.Name()=%q, want Xinference", driver.Name()) } } diff --git a/internal/entity/models/xunfei.go b/internal/entity/models/xunfei.go index a4229ef859..c331e39a2c 100644 --- a/internal/entity/models/xunfei.go +++ b/internal/entity/models/xunfei.go @@ -45,7 +45,7 @@ func (x *XunFeiModel) NewInstance(baseURL map[string]string) ModelDriver { } func (x *XunFeiModel) Name() string { - return "xunfei" + return "XunFei Spark" } func (x *XunFeiModel) ChatWithMessages(modelName string, messages []Message, apiConfig *APIConfig, chatModelConfig *ChatConfig) (*ChatResponse, error) { diff --git a/internal/service/deep_researcher.go b/internal/service/deep_researcher.go index 81ae00209e..06bff70c12 100644 --- a/internal/service/deep_researcher.go +++ b/internal/service/deep_researcher.go @@ -66,8 +66,8 @@ Requirements: 4. The missing_information should only be filled when insufficient, otherwise empty array. ` -const multiQueriesGenTemplate = `You are a query optimization expert. -The user's original query failed to retrieve sufficient information; +const multiQueriesGenTemplate = `You are a query optimization expert. +The user's original query failed to retrieve sufficient information; please generate multiple complementary improved questions and corresponding queries. Original query: @@ -102,8 +102,8 @@ Requirements: 1. Questions array contains 1-3 questions and corresponding queries. 2. Each question length is between 5-200 characters. 3. Each query length is between 1-5 keywords. -4. Each query MUST be in the same language as the retrieved content in. -5. DO NOT generate question and query that is similar to the original query. +4. Each query MUST be in the same language as the retrieved content in. +5. DO NOT generate question and query that is similar to the original query. 6. Reasoning explains the generation strategy. ` diff --git a/internal/service/model_service.go b/internal/service/model_service.go index 0b4f0c90ae..be20d5e6ce 100644 --- a/internal/service/model_service.go +++ b/internal/service/model_service.go @@ -2270,9 +2270,9 @@ func (m *ModelProviderService) GetModelTypeByName(tenantID, modelName string) ([ targetFactoryName = "siliconflow_intl" } // Use the case-insensitive FindProvider / findModel helpers. The Go's - // conf/models/*.json files use mixed case ("SiliconFlow") while the - // Python's conf/llm_factories.json uses all-caps ("SILICONFLOW"); a strict - // `==` here would fail for any case mismatch. The rest of the Go codebase + // conf/models/*.json files now use the same names as the Python's + // conf/llm_factories.json (e.g. "SILICONFLOW"); but a strict + // `==` here would still fail for any case mismatch. The rest of the Go codebase // uses these helpers too. targetProvider := dao.GetModelProviderManager().FindProvider(targetFactoryName) if targetProvider == nil { @@ -2602,9 +2602,9 @@ func (m *ModelProviderService) GetModelConfigFromProviderInstance(tenantID strin targetFactoryName = "siliconflow_intl" } // Use the case-insensitive FindProvider / findModel helpers. The Go's - // conf/models/*.json files use mixed case ("SiliconFlow") while the - // Python's conf/llm_factories.json uses all-caps ("SILICONFLOW"); a strict - // `==` here would fail for any case mismatch even though the Python passes + // conf/models/*.json files now use the same names as the Python's + // conf/llm_factories.json (e.g. "SILICONFLOW"); but a strict + // `==` here would still fail for any case mismatch even though the Python passes // (its FACTORY_LLM_INFOS and the parsed provider_name happen to agree on // casing). The rest of the Go codebase uses these helpers too. targetProvider := dao.GetModelProviderManager().FindProvider(targetFactoryName) diff --git a/rag/svr/task_executor_refactor/task_handler.py b/rag/svr/task_executor_refactor/task_handler.py index f754a2fcce..cbb37e1399 100644 --- a/rag/svr/task_executor_refactor/task_handler.py +++ b/rag/svr/task_executor_refactor/task_handler.py @@ -321,17 +321,11 @@ class TaskHandler: try: if ctx.tenant_embd_id: try: - embd_model_config = get_model_config_by_id( - task_tenant_id, ctx.tenant_embd_id - ) + embd_model_config = get_model_config_by_id(task_tenant_id, ctx.tenant_embd_id) except LookupError: - embd_model_config = get_model_config_from_provider_instance( - task_tenant_id, LLMType.EMBEDDING, task_embedding_id - ) + embd_model_config = get_model_config_from_provider_instance(task_tenant_id, LLMType.EMBEDDING, task_embedding_id) elif task_embedding_id: - embd_model_config = get_model_config_from_provider_instance( - task_tenant_id, LLMType.EMBEDDING, task_embedding_id - ) + embd_model_config = get_model_config_from_provider_instance(task_tenant_id, LLMType.EMBEDDING, task_embedding_id) else: embd_model_config = get_tenant_default_model_by_type(task_tenant_id, LLMType.EMBEDDING) embedding_model = LLMBundle(task_tenant_id, embd_model_config, lang=task_language) diff --git a/ragflow_deps/download_go_deps.py b/ragflow_deps/download_go_deps.py index d3f1e5ed24..85fae8ebf4 100644 --- a/ragflow_deps/download_go_deps.py +++ b/ragflow_deps/download_go_deps.py @@ -36,6 +36,7 @@ import sys import requests from typing import Union + def get_urls(use_china_mirrors=False) -> list[Union[str, list[str]]]: if use_china_mirrors: return [ @@ -91,10 +92,10 @@ def get_urls(use_china_mirrors=False) -> list[Union[str, list[str]]]: def download_with_progress(url, filename): response = requests.get(url, stream=True) - total_size = int(response.headers.get('content-length', 0)) + total_size = int(response.headers.get("content-length", 0)) block_size = 1024 - with open(filename, 'wb') as file: + with open(filename, "wb") as file: downloaded = 0 for data in response.iter_content(block_size): file.write(data) @@ -102,7 +103,7 @@ def download_with_progress(url, filename): if total_size > 0: progress = (downloaded / total_size) * 100 - sys.stdout.write(f'\rProgress: {progress:.1f}% ({downloaded}/{total_size} bytes)') + sys.stdout.write(f"\rProgress: {progress:.1f}% ({downloaded}/{total_size} bytes)") sys.stdout.flush() print() @@ -123,9 +124,9 @@ if __name__ == "__main__": # Some mirrors (e.g. archive.ubuntu.com) reject the default urllib # User-Agent with HTTP 403, so install an opener with a browser-like UA. -# opener = urllib.request.build_opener() -# opener.addheaders = [("User-Agent", "Mozilla/5.0")] -# urllib.request.install_opener(opener) + # opener = urllib.request.build_opener() + # opener.addheaders = [("User-Agent", "Mozilla/5.0")] + # urllib.request.install_opener(opener) for url in urls: download_url = url[0] if isinstance(url, list) else url diff --git a/test/unit_test/api/db/services/test_get_queue_length.py b/test/unit_test/api/db/services/test_get_queue_length.py index a985fed519..571353efbc 100644 --- a/test/unit_test/api/db/services/test_get_queue_length.py +++ b/test/unit_test/api/db/services/test_get_queue_length.py @@ -32,6 +32,7 @@ warnings.filterwarnings( def _install_cv2_stub_if_unavailable(): try: import cv2 # noqa: F401 + return except (ImportError, OSError) as exc: # cv2 can fail to import with OSError too (e.g. missing shared libs), diff --git a/test/unit_test/common/test_llm_request_context.py b/test/unit_test/common/test_llm_request_context.py index 17af0ec80b..b6748e806c 100644 --- a/test/unit_test/common/test_llm_request_context.py +++ b/test/unit_test/common/test_llm_request_context.py @@ -19,6 +19,7 @@ Covers the behaviour flagged in review: (1) session_id is preferred over user_id, (2) an explicit caller-supplied ``user`` overrides the context value, and (3) a caller-supplied empty ``user`` suppresses forwarding entirely. """ + import types import pytest diff --git a/tools/scripts/mysql_migration.py b/tools/scripts/mysql_migration.py index 50f255332b..4d871548c6 100644 --- a/tools/scripts/mysql_migration.py +++ b/tools/scripts/mysql_migration.py @@ -158,11 +158,7 @@ class MigrationDatabase: def get_column_type(self, table_name: str, column_name: str) -> str | None: """Get the DATA_TYPE of a column from information_schema, returns None if column does not exist""" - cursor = self.execute_sql( - "SELECT DATA_TYPE FROM information_schema.columns " - "WHERE table_schema = %s AND table_name = %s AND column_name = %s", - (self.config.database, table_name, column_name) - ) + cursor = self.execute_sql("SELECT DATA_TYPE FROM information_schema.columns WHERE table_schema = %s AND table_name = %s AND column_name = %s", (self.config.database, table_name, column_name)) row = cursor.fetchone() return row[0] if row else None @@ -1304,35 +1300,22 @@ class TenantModelSeedingStage(MigrationStage): instance_extra_include = None instance_extra_exclude = None # Query provider for this factory - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_provider WHERE provider_name = %s", - (provider_name_filter,) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_provider WHERE provider_name = %s", (provider_name_filter,)) providers = cursor.fetchall() - for provider_id, in providers: + for (provider_id,) in providers: # Query instances for this provider, with optional extra include/exclude filter if instance_extra_include and instance_extra_exclude: cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s AND extra NOT LIKE %s", - (provider_id, instance_extra_include, instance_extra_exclude) + "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s AND extra NOT LIKE %s", (provider_id, instance_extra_include, instance_extra_exclude) ) elif instance_extra_include: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s", - (provider_id, instance_extra_include) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s", (provider_id, instance_extra_include)) elif instance_extra_exclude: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra NOT LIKE %s", - (provider_id, instance_extra_exclude) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra NOT LIKE %s", (provider_id, instance_extra_exclude)) else: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s", - (provider_id,) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s", (provider_id,)) instances = cursor.fetchall() - for instance_id, in instances: + for (instance_id,) in instances: for llm in llm_list: model_name = llm.get("llm_name", "") model_types = llm.get("model_type", "chat") @@ -1340,9 +1323,7 @@ class TenantModelSeedingStage(MigrationStage): model_types = [model_types] for mt in model_types: cursor = self.db.execute_sql( - "SELECT COUNT(*) FROM tenant_model " - "WHERE provider_id = %s AND instance_id = %s AND model_name = %s AND model_type = %s", - (provider_id, instance_id, model_name, mt) + "SELECT COUNT(*) FROM tenant_model WHERE provider_id = %s AND instance_id = %s AND model_name = %s AND model_type = %s", (provider_id, instance_id, model_name, mt) ) if cursor.fetchone()[0] == 0: total_missing += 1 @@ -1381,9 +1362,7 @@ class TenantModelSeedingStage(MigrationStage): # Pre-load existing extra values for model_names from tenant_model # to reuse non-default extra for same model_name across providers - cursor = self.db.execute_sql( - "SELECT model_name, extra FROM tenant_model WHERE extra != '{}' AND extra IS NOT NULL" - ) + cursor = self.db.execute_sql("SELECT model_name, extra FROM tenant_model WHERE extra != '{}' AND extra IS NOT NULL") existing_extra_map = {} for model_name, extra in cursor.fetchall(): if model_name and extra and extra != "{}": @@ -1391,9 +1370,7 @@ class TenantModelSeedingStage(MigrationStage): # Pre-load existing status values for model_names from tenant_model # Prefer non-unsupported status when seeding new records for the same model_name - cursor = self.db.execute_sql( - "SELECT model_name, status FROM tenant_model WHERE status != 'unsupported' AND status IS NOT NULL" - ) + cursor = self.db.execute_sql("SELECT model_name, status FROM tenant_model WHERE status != 'unsupported' AND status IS NOT NULL") existing_status_map = {} for model_name, status in cursor.fetchall(): if model_name and status: @@ -1423,40 +1400,27 @@ class TenantModelSeedingStage(MigrationStage): instance_extra_exclude = None # Query provider for this factory - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_provider WHERE provider_name = %s", - (provider_name_filter,) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_provider WHERE provider_name = %s", (provider_name_filter,)) providers = cursor.fetchall() - for provider_id, in providers: + for (provider_id,) in providers: # Query instances for this provider, with optional extra include/exclude filter if instance_extra_include and instance_extra_exclude: cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s AND extra NOT LIKE %s", - (provider_id, instance_extra_include, instance_extra_exclude) + "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s AND extra NOT LIKE %s", (provider_id, instance_extra_include, instance_extra_exclude) ) elif instance_extra_include: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s", - (provider_id, instance_extra_include) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra LIKE %s", (provider_id, instance_extra_include)) elif instance_extra_exclude: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra NOT LIKE %s", - (provider_id, instance_extra_exclude) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s AND extra NOT LIKE %s", (provider_id, instance_extra_exclude)) else: - cursor = self.db.execute_sql( - "SELECT id FROM tenant_model_instance WHERE provider_id = %s", - (provider_id,) - ) + cursor = self.db.execute_sql("SELECT id FROM tenant_model_instance WHERE provider_id = %s", (provider_id,)) instances = cursor.fetchall() if not instances: logger.warning(f"No instances found for provider '{provider_name_filter}' (id={provider_id}), skipping") continue - for instance_id, in instances: + for (instance_id,) in instances: for llm in llm_list: model_name = llm.get("llm_name", "") if not model_name: @@ -1484,9 +1448,7 @@ class TenantModelSeedingStage(MigrationStage): for mt in model_types: # Check if record already exists cursor = self.db.execute_sql( - "SELECT COUNT(*) FROM tenant_model " - "WHERE provider_id = %s AND instance_id = %s AND model_name = %s AND model_type = %s", - (provider_id, instance_id, model_name, mt) + "SELECT COUNT(*) FROM tenant_model WHERE provider_id = %s AND instance_id = %s AND model_name = %s AND model_type = %s", (provider_id, instance_id, model_name, mt) ) if cursor.fetchone()[0] > 0: continue @@ -1501,8 +1463,7 @@ class TenantModelSeedingStage(MigrationStage): if self.dry_run: logger.info(f"[DRY RUN] Would insert {len(all_records)} records") for model_name, provider_id, instance_id, mt, status, extra in all_records[:5]: - logger.info(f" model_name={model_name}, provider_id={provider_id}, " - f"instance_id={instance_id}, model_type={mt}, status={status}, extra={extra}") + logger.info(f" model_name={model_name}, provider_id={provider_id}, instance_id={instance_id}, model_type={mt}, status={status}, extra={extra}") if len(all_records) > 5: logger.info(f" ... and {len(all_records) - 5} more records") return len(all_records), self.target_tables @@ -1510,7 +1471,7 @@ class TenantModelSeedingStage(MigrationStage): # Insert records in batches batch_size = 100 for i in range(0, len(all_records), batch_size): - batch = all_records[i:i + batch_size] + batch = all_records[i : i + batch_size] values = [] for model_name, provider_id, instance_id, mt, status, extra in batch: record_id = self.generate_uuid() @@ -1518,17 +1479,19 @@ class TenantModelSeedingStage(MigrationStage): mt_escaped = mt.replace("'", "''") if mt else "" extra_escaped = extra.replace("'", "''") if extra else "{}" status_escaped = status.replace("'", "''") if status else "active" - values.append(f"('{record_id}', '{model_name_escaped}', '{provider_id}', " - f"'{instance_id}', '{mt_escaped}', '{status_escaped}', " - f"'{extra_escaped}', " - f"{current_ts * 1000}, FROM_UNIXTIME({current_ts}), " - f"{current_ts * 1000}, FROM_UNIXTIME({current_ts}))") + values.append( + f"('{record_id}', '{model_name_escaped}', '{provider_id}', " + f"'{instance_id}', '{mt_escaped}', '{status_escaped}', " + f"'{extra_escaped}', " + f"{current_ts * 1000}, FROM_UNIXTIME({current_ts}), " + f"{current_ts * 1000}, FROM_UNIXTIME({current_ts}))" + ) insert_sql = f""" INSERT INTO tenant_model (id, model_name, provider_id, instance_id, model_type, status, extra, create_time, create_date, update_time, update_date) - VALUES {', '.join(values)} + VALUES {", ".join(values)} """ self.db.execute_sql(insert_sql) rows_inserted += len(batch) @@ -1568,13 +1531,13 @@ class ModelTypeMergeStage(MigrationStage): # Mapping from LLMType string values to ModelTypeBinary integer values MODEL_TYPE_STR_TO_INT = { - "chat": 1, # 0b0000001 + "chat": 1, # 0b0000001 "embedding": 2, # 0b0000010 "speech2text": 4, # 0b0000100 "image2text": 8, # 0b0001000 - "rerank": 16, # 0b0010000 - "tts": 32, # 0b0100000 - "ocr": 64, # 0b1000000 + "rerank": 16, # 0b0010000 + "tts": 32, # 0b0100000 + "ocr": 64, # 0b1000000 } def current_timestamp(self) -> int: @@ -1616,19 +1579,15 @@ class ModelTypeMergeStage(MigrationStage): return 0, self.target_tables # Load all records from tenant_model - cursor = self.db.execute_sql( - "SELECT id, model_name, provider_id, instance_id, model_type, status, extra, " - "create_time, create_date, update_time, update_date " - "FROM tenant_model ORDER BY id" - ) + cursor = self.db.execute_sql("SELECT id, model_name, provider_id, instance_id, model_type, status, extra, create_time, create_date, update_time, update_date FROM tenant_model ORDER BY id") all_rows = cursor.fetchall() # Group by (provider_id, instance_id, model_name) from collections import defaultdict + groups = defaultdict(list) for row in all_rows: - id_, model_name, provider_id, instance_id, model_type_str, status, extra, \ - create_time, create_date, update_time, update_date = row + id_, model_name, provider_id, instance_id, model_type_str, status, extra, create_time, create_date, update_time, update_date = row groups[(provider_id, instance_id, model_name)].append(row) # Merge each group into a single record @@ -1656,18 +1615,13 @@ class ModelTypeMergeStage(MigrationStage): merged_status = first_non_unsupported_status or "active" # Use first row's values for all other fields, but replace id, model_type, status - id_, model_name, provider_id, instance_id, _, _, extra, \ - create_time, create_date, update_time, update_date = first_row + id_, model_name, provider_id, instance_id, _, _, extra, create_time, create_date, update_time, update_date = first_row # Only include records whose merged status is "active" if merged_status != "active": continue - merged_records.append(( - self.generate_uuid(), model_name, provider_id, instance_id, - merged_type, merged_status, extra, - create_time, create_date, update_time, update_date - )) + merged_records.append((self.generate_uuid(), model_name, provider_id, instance_id, merged_type, merged_status, extra, create_time, create_date, update_time, update_date)) if not merged_records: logger.info("No records to merge") @@ -1676,13 +1630,10 @@ class ModelTypeMergeStage(MigrationStage): logger.info(f"Merging {len(all_rows)} rows into {len(merged_records)} rows...") if self.dry_run: - logger.info(f"[DRY RUN] Would insert {len(merged_records)} merged rows, " - f"then rename tables") + logger.info(f"[DRY RUN] Would insert {len(merged_records)} merged rows, then rename tables") for rec in merged_records[:5]: _, model_name, provider_id, instance_id, merged_type, status, extra, *_ = rec - logger.info(f" model_name={model_name}, provider_id={provider_id}, " - f"instance_id={instance_id}, model_type={merged_type}, " - f"status={status}, extra={extra}") + logger.info(f" model_name={model_name}, provider_id={provider_id}, instance_id={instance_id}, model_type={merged_type}, status={status}, extra={extra}") if len(merged_records) > 5: logger.info(f" ... and {len(merged_records) - 5} more rows") return len(merged_records), self.target_tables @@ -1690,11 +1641,10 @@ class ModelTypeMergeStage(MigrationStage): # Insert merged records into temporary table batch_size = 100 for i in range(0, len(merged_records), batch_size): - batch = merged_records[i:i + batch_size] + batch = merged_records[i : i + batch_size] values = [] for rec in batch: - id_, model_name, provider_id, instance_id, merged_type, status, extra, \ - create_time, create_date, update_time, update_date = rec + id_, model_name, provider_id, instance_id, merged_type, status, extra, create_time, create_date, update_time, update_date = rec model_name_escaped = (model_name.replace("'", "''") if model_name else "") or None extra_escaped = extra.replace("'", "''") if extra else "{}" status_escaped = status.replace("'", "''") if status else "active" @@ -1703,17 +1653,19 @@ class ModelTypeMergeStage(MigrationStage): update_time_val = update_time if update_time is not None else current_ts * 1000 create_date_sql = f"FROM_UNIXTIME({int(create_time_val / 1000)})" if create_time_val else "NULL" update_date_sql = f"FROM_UNIXTIME({int(update_time_val / 1000)})" if update_time_val else "NULL" - values.append(f"('{id_}', '{model_name_escaped}', '{provider_id}', " - f"'{instance_id}', {merged_type}, '{status_escaped}', " - f"'{extra_escaped}', " - f"{create_time_val}, {create_date_sql}, " - f"{update_time_val}, {update_date_sql})") + values.append( + f"('{id_}', '{model_name_escaped}', '{provider_id}', " + f"'{instance_id}', {merged_type}, '{status_escaped}', " + f"'{extra_escaped}', " + f"{create_time_val}, {create_date_sql}, " + f"{update_time_val}, {update_date_sql})" + ) insert_sql = f""" INSERT INTO tenant_model_merge_tmp (id, model_name, provider_id, instance_id, model_type, status, extra, create_time, create_date, update_time, update_date) - VALUES {', '.join(values)} + VALUES {", ".join(values)} """ self.db.execute_sql(insert_sql) rows_inserted += len(batch) @@ -1825,9 +1777,7 @@ class TenantModelIdMigrationStage(MigrationStage): has_work = True break # Check if there are NULL values that should be populated - cursor = self.db.execute_sql( - f"SELECT COUNT(*) FROM `{table_name}` WHERE `{tenant_id_col}` IS NULL OR `{tenant_id_col}` = ''" - ) + cursor = self.db.execute_sql(f"SELECT COUNT(*) FROM `{table_name}` WHERE `{tenant_id_col}` IS NULL OR `{tenant_id_col}` = ''") null_count = cursor.fetchone()[0] if null_count > 0: has_work = True @@ -1860,18 +1810,14 @@ class TenantModelIdMigrationStage(MigrationStage): # Column does not exist yet — add it as VARCHAR(32) logger.info(f"Adding column {table_name}.{tenant_id_col} as VARCHAR(32) NULL") if not self.dry_run: - self.db.execute_sql( - f"ALTER TABLE `{table_name}` ADD COLUMN `{tenant_id_col}` VARCHAR(32) NULL" - ) + self.db.execute_sql(f"ALTER TABLE `{table_name}` ADD COLUMN `{tenant_id_col}` VARCHAR(32) NULL") tables_operated.add(table_name) continue col_type = self.db.get_column_type(table_name, tenant_id_col) if col_type and col_type.lower() in ("int", "bigint", "integer"): logger.info(f"Converting {table_name}.{tenant_id_col} from {col_type} to VARCHAR(32)") if not self.dry_run: - self.db.execute_sql( - f"ALTER TABLE `{table_name}` MODIFY COLUMN `{tenant_id_col}` VARCHAR(32) NULL" - ) + self.db.execute_sql(f"ALTER TABLE `{table_name}` MODIFY COLUMN `{tenant_id_col}` VARCHAR(32) NULL") tables_operated.add(table_name) # Step 2: Build lookup cache from tenant_model joined with provider + instance @@ -1893,9 +1839,7 @@ class TenantModelIdMigrationStage(MigrationStage): # Get rows where tenant_*_id is NULL or empty cursor = self.db.execute_sql( - f"SELECT id, `{legacy_col}` FROM `{table_name}` " - f"WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') " - f"AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" + f"SELECT id, `{legacy_col}` FROM `{table_name}` WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" ) # Also need tenant_id for model lookup @@ -1903,9 +1847,7 @@ class TenantModelIdMigrationStage(MigrationStage): # For knowledgebase, dialog, memory we also need tenant_id if table_name == "tenant": cursor = self.db.execute_sql( - f"SELECT id, `{legacy_col}` FROM `{table_name}` " - f"WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') " - f"AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" + f"SELECT id, `{legacy_col}` FROM `{table_name}` WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" ) while True: rows = cursor.fetchmany(self.scan_batch_size) @@ -1930,9 +1872,7 @@ class TenantModelIdMigrationStage(MigrationStage): tables_operated.add(table_name) else: cursor = self.db.execute_sql( - f"SELECT id, tenant_id, `{legacy_col}` FROM `{table_name}` " - f"WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') " - f"AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" + f"SELECT id, tenant_id, `{legacy_col}` FROM `{table_name}` WHERE (`{tenant_id_col}` IS NULL OR `{tenant_id_col}` = '') AND `{legacy_col}` IS NOT NULL AND `{legacy_col}` != ''" ) while True: rows = cursor.fetchmany(self.scan_batch_size) diff --git a/tools/scripts/rename_models_providers.py b/tools/scripts/rename_models_providers.py new file mode 100644 index 0000000000..6538e82097 --- /dev/null +++ b/tools/scripts/rename_models_providers.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +"""Rename provider name fields in conf/models/*.json to match llm_factories.json.""" +import json, os + +REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +MODELS_DIR = os.path.join(REPO_ROOT, "conf", "models") + +# Valid mappings: models current name → llm_factories name +CHANGES = { + "avian": "Avian", + "Baichuan": "BaiChuan", + "Baidu": "BaiduYiyan", + "CoHere": "Cohere", + "FishAudio": "Fish Audio", + "Gitee": "GiteeAI", + "Google": "Gemini", + "JieKouAI": "Jiekou.AI", + "lmstudio": "LM-Studio", + "localai": "LocalAI", + "modelscope": "ModelScope", + "Novita": "NovitaAI", + "Nvidia": "NVIDIA", + "ollama": "Ollama", + "SiliconFlow": "SILICONFLOW", + "HunYuan": "Tencent Hunyuan", + "Aliyun": "Tongyi-Qianwen", + "vllm": "VLLM", + "Voyage": "Voyage AI", + "xinference": "Xinference", + "XunFei": "XunFei Spark", +} + +def main(): + # Build current name → filename mapping + file_map = {} + for fn in os.listdir(MODELS_DIR): + if not fn.endswith(".json"): + continue + with open(os.path.join(MODELS_DIR, fn)) as f: + d = json.load(f) + file_map[d["name"]] = fn + + for old_name, new_name in CHANGES.items(): + fn = file_map[old_name] + path = os.path.join(MODELS_DIR, fn) + with open(path) as f: + d = json.load(f) + d["name"] = new_name + with open(path, "w") as f: + json.dump(d, f, indent=2, ensure_ascii=False) + f.write("\n") + print(f'{fn}: "{old_name}" -> "{new_name}"') + +if __name__ == "__main__": + main() diff --git a/web/src/components/memories-form-field.tsx b/web/src/components/memories-form-field.tsx index 61c5ab0477..7d71cc7ea3 100644 --- a/web/src/components/memories-form-field.tsx +++ b/web/src/components/memories-form-field.tsx @@ -52,24 +52,21 @@ export function useDisableDifferenceEmbeddingMemory(name: string) { }, [memoryIds, memoryList]); const options = useMemo(() => { - return memoryList - .filter(Boolean) - .map((item: IMemory) => { - return { - label: item.name, - icon: () => ( - - ), - suffix: , - value: item.id, - disabled: - item.embd_id !== selectedEmbedId && selectedEmbedId !== '', - }; - }); + return memoryList.filter(Boolean).map((item: IMemory) => { + return { + label: item.name, + icon: () => ( + + ), + suffix: , + value: item.id, + disabled: item.embd_id !== selectedEmbedId && selectedEmbedId !== '', + }; + }); }, [memoryList, selectedEmbedId]); return {