From 20ddc76a4d164d39c768a5b1e4139f81de03dc04 Mon Sep 17 00:00:00 2001 From: wwq Date: Mon, 20 Apr 2026 21:48:45 +0800 Subject: [PATCH 1/3] feat(model_parsing): add model reference resolution for LLM and related node types - Add model reference resolution for LLM, Question Classifier, and Parameter Extractor nodes. - Support parsing various model reference formats, including dictionaries, UUID strings, and name strings, when `model_id` is present. - Add warning logs for cases where model resolution fails. --- api/app/services/app_dsl_service.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/api/app/services/app_dsl_service.py b/api/app/services/app_dsl_service.py index 26e4098c..0b3321a7 100644 --- a/api/app/services/app_dsl_service.py +++ b/api/app/services/app_dsl_service.py @@ -601,6 +601,35 @@ class AppDslService: else: warnings.append(f"[{node_label}] 知识库 '{kb_id}' 未匹配,已移除,请导入后手动配置") config["knowledge_bases"] = resolved_kbs + elif node_type in (NodeType.LLM.value, NodeType.QUESTION_CLASSIFIER.value, NodeType.PARAMETER_EXTRACTOR.value): + model_ref = config.get("model_id") + if model_ref: + ref_dict = None + if isinstance(model_ref, dict): + ref_id = model_ref.get("id") + ref_name = model_ref.get("name") + if ref_id: + ref_dict = {"id": ref_id} + elif ref_name and ref_name != "None": + ref_dict = {"name": ref_name, "provider": model_ref.get("provider"), "type": model_ref.get("type")} + elif isinstance(model_ref, str) and model_ref != "None" and len(model_ref) >= 36: + try: + uuid.UUID(model_ref) + ref_dict = {"id": model_ref} + except ValueError: + ref_dict = {"name": model_ref} + elif isinstance(model_ref, str) and model_ref != "None": + ref_dict = {"name": model_ref} + if ref_dict: + resolved_model_id = self._resolve_model(ref_dict, tenant_id, warnings) + if resolved_model_id: + config["model_id"] = resolved_model_id + else: + warnings.append(f"[{node_label}] 模型未匹配,已置空,请导入后手动配置") + config["model_id"] = None + else: + warnings.append(f"[{node_label}] 模型未匹配,已置空,请导入后手动配置") + config["model_id"] = None resolved_nodes.append({**node, "config": config}) return resolved_nodes From f1dc507b5cf85d6170ae7dc5b88e1d8e7d27587c Mon Sep 17 00:00:00 2001 From: wwq Date: Tue, 21 Apr 2026 11:55:00 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E7=9F=A5=E8=AF=86?= =?UTF-8?q?=E5=BA=93=E5=92=8C=E6=A8=A1=E5=9E=8B=E5=BC=95=E7=94=A8=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除对字符串长度的UUID验证,仅检查是否为有效UUID或非"None"字符串 --- api/app/services/app_dsl_service.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/api/app/services/app_dsl_service.py b/api/app/services/app_dsl_service.py index 0b3321a7..b1fedeca 100644 --- a/api/app/services/app_dsl_service.py +++ b/api/app/services/app_dsl_service.py @@ -587,14 +587,12 @@ class AppDslService: if not kb_id: continue kb_ref = {} - if isinstance(kb_id, str) and len(kb_id) >= 36: + if isinstance(kb_id, str) and kb_id != "None": try: uuid.UUID(kb_id) kb_ref["id"] = kb_id except ValueError: kb_ref["name"] = kb_id - else: - kb_ref["name"] = kb_id resolved_id = self._resolve_kb(kb_ref, workspace_id, []) if resolved_id: resolved_kbs.append({**kb, "kb_id": resolved_id}) @@ -612,14 +610,12 @@ class AppDslService: ref_dict = {"id": ref_id} elif ref_name and ref_name != "None": ref_dict = {"name": ref_name, "provider": model_ref.get("provider"), "type": model_ref.get("type")} - elif isinstance(model_ref, str) and model_ref != "None" and len(model_ref) >= 36: + elif isinstance(model_ref, str) and model_ref != "None": try: uuid.UUID(model_ref) ref_dict = {"id": model_ref} except ValueError: ref_dict = {"name": model_ref} - elif isinstance(model_ref, str) and model_ref != "None": - ref_dict = {"name": model_ref} if ref_dict: resolved_model_id = self._resolve_model(ref_dict, tenant_id, warnings) if resolved_model_id: From 6de5d413c4b69194aed41589856fddc0c20f549b Mon Sep 17 00:00:00 2001 From: wwq Date: Tue, 21 Apr 2026 15:03:18 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(app=5Fdsl=5Fservice):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=A8=A1=E5=9E=8B=E5=92=8C=E7=9F=A5=E8=AF=86=E5=BA=93?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E8=A7=A3=E6=9E=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改进模型引用解析,优先使用ID匹配并处理异常情况 优化知识库引用解析,移除不必要的"None"字符串检查 统一返回字符串类型的ID,保持类型一致性 --- api/app/services/app_dsl_service.py | 52 ++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/api/app/services/app_dsl_service.py b/api/app/services/app_dsl_service.py index b1fedeca..63279d2c 100644 --- a/api/app/services/app_dsl_service.py +++ b/api/app/services/app_dsl_service.py @@ -434,19 +434,37 @@ class AppDslService: def _resolve_model(self, ref: Optional[dict], tenant_id: uuid.UUID, warnings: list) -> Optional[uuid.UUID]: if not ref: return None - q = self.db.query(ModelConfig).filter( - ModelConfig.tenant_id == tenant_id, - ModelConfig.name == ref.get("name"), - ModelConfig.is_active.is_(True) - ) - if ref.get("provider"): - q = q.filter(ModelConfig.provider == ref["provider"]) - if ref.get("type"): - q = q.filter(ModelConfig.type == ref["type"]) - m = q.first() - if not m: - warnings.append(f"模型 '{ref.get('name')}' 未匹配,已置空,请导入后手动配置") - return m.id if m else None + model_id = ref.get("id") + if model_id: + try: + model_uuid = uuid.UUID(str(model_id)) + m = self.db.query(ModelConfig).filter( + ModelConfig.id == model_uuid, + ModelConfig.tenant_id == tenant_id, + ModelConfig.is_active.is_(True) + ).first() + if m: + return str(m.id) + except (ValueError, AttributeError): + pass + model_name = ref.get("name") + if model_name: + q = self.db.query(ModelConfig).filter( + ModelConfig.tenant_id == tenant_id, + ModelConfig.name == model_name, + ModelConfig.is_active.is_(True) + ) + if ref.get("provider"): + q = q.filter(ModelConfig.provider == ref["provider"]) + if ref.get("type"): + q = q.filter(ModelConfig.type == ref["type"]) + m = q.first() + if m: + return str(m.id) + warnings.append(f"模型 '{model_name}' 未匹配,已置空,请导入后手动配置") + else: + warnings.append(f"模型 ID '{model_id}' 未匹配,已置空,请导入后手动配置") + return None def _resolve_kb(self, ref: Optional[dict], workspace_id: uuid.UUID, warnings: list) -> Optional[str]: if not ref: @@ -587,12 +605,14 @@ class AppDslService: if not kb_id: continue kb_ref = {} - if isinstance(kb_id, str) and kb_id != "None": + if isinstance(kb_id, str): try: uuid.UUID(kb_id) kb_ref["id"] = kb_id except ValueError: kb_ref["name"] = kb_id + else: + kb_ref["name"] = kb_id resolved_id = self._resolve_kb(kb_ref, workspace_id, []) if resolved_id: resolved_kbs.append({**kb, "kb_id": resolved_id}) @@ -608,9 +628,9 @@ class AppDslService: ref_name = model_ref.get("name") if ref_id: ref_dict = {"id": ref_id} - elif ref_name and ref_name != "None": + elif ref_name is not None: ref_dict = {"name": ref_name, "provider": model_ref.get("provider"), "type": model_ref.get("type")} - elif isinstance(model_ref, str) and model_ref != "None": + elif isinstance(model_ref, str): try: uuid.UUID(model_ref) ref_dict = {"id": model_ref}