mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-05-20 00:07:00 +08:00
### What problem does this PR solve? Closes #13907 The template catalog had duplicate files (e.g. `*_r.json`) only to place the same template into multiple sidebar groups. This increases maintenance cost and makes template updates error-prone. This PR adds first-class support for multiple template categories in a single file via `canvas_types`, then removes duplicate template files. What changed: - Added `canvas_types` to `CanvasTemplate` model and DB migration. - Added normalization logic when loading templates: - accepts legacy `canvas_type` - accepts new `canvas_types` - merges/deduplicates values - preserves backward compatibility by keeping `canvas_type` as first normalized value. - Updated template import flow to load only `.json` files and in stable sorted order. - Updated frontend template filtering to match on `canvas_types` first, with fallback to legacy `canvas_type`. - Consolidated duplicated template pairs into single files and removed: - `deep_search_r.json` - `reflective_academic_paper_generator_r.json` - `seo_article_writer_r.json` - Added regression/edge-case tests for category normalization and route serialization expectations. ### Type of change - [ ] Bug Fix (non-breaking change which fixes an issue) - [x] New Feature (non-breaking change which adds functionality) - [ ] Documentation Update - [ ] Refactoring - [ ] Performance Improvement - [ ] Other (please describe):
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
#
|
|
# Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
import logging
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def _collect_canvas_types(canvas_type: Any, canvas_types: Any) -> list[str]:
|
|
categories: list[str] = []
|
|
|
|
if isinstance(canvas_type, str):
|
|
category = canvas_type.strip()
|
|
if category:
|
|
categories.append(category)
|
|
|
|
iterable_types: list[Any]
|
|
if isinstance(canvas_types, list):
|
|
iterable_types = canvas_types
|
|
elif canvas_types is None:
|
|
iterable_types = []
|
|
else:
|
|
iterable_types = [canvas_types]
|
|
|
|
for item in iterable_types:
|
|
if not isinstance(item, str):
|
|
continue
|
|
category = item.strip()
|
|
if not category:
|
|
continue
|
|
categories.append(category)
|
|
|
|
deduplicated: list[str] = []
|
|
seen: set[str] = set()
|
|
for category in categories:
|
|
if category in seen:
|
|
continue
|
|
seen.add(category)
|
|
deduplicated.append(category)
|
|
|
|
return deduplicated
|
|
|
|
|
|
def normalize_canvas_template_categories(template: dict[str, Any]) -> dict[str, Any]:
|
|
normalized = dict(template)
|
|
raw_canvas_type = normalized.get("canvas_type")
|
|
raw_canvas_types = normalized.get("canvas_types")
|
|
canvas_types = _collect_canvas_types(
|
|
raw_canvas_type,
|
|
raw_canvas_types,
|
|
)
|
|
normalized["canvas_types"] = canvas_types
|
|
normalized["canvas_type"] = canvas_types[0] if canvas_types else None
|
|
if raw_canvas_type != normalized["canvas_type"] or raw_canvas_types != normalized["canvas_types"]:
|
|
logger.debug(
|
|
"Normalized canvas categories for template_id=%s: canvas_type=%r -> %r, canvas_types=%r -> %r",
|
|
normalized.get("id"),
|
|
raw_canvas_type,
|
|
normalized["canvas_type"],
|
|
raw_canvas_types,
|
|
normalized["canvas_types"],
|
|
)
|
|
return normalized
|