2025-01-21 20:52:28 +08:00
|
|
|
#
|
|
|
|
|
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
|
|
|
|
|
#
|
2024-07-22 19:17:30 +08:00
|
|
|
# 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.
|
|
|
|
|
#
|
2025-12-30 11:09:18 +08:00
|
|
|
import logging
|
2025-08-19 16:41:18 +08:00
|
|
|
import os
|
2024-07-22 19:17:30 +08:00
|
|
|
import re
|
2025-08-19 16:41:18 +08:00
|
|
|
import tempfile
|
2024-07-22 19:17:30 +08:00
|
|
|
|
2025-11-05 08:01:39 +08:00
|
|
|
from common.constants import LLMType
|
2024-07-22 19:17:30 +08:00
|
|
|
from api.db.services.llm_service import LLMBundle
|
2026-03-05 17:27:17 +08:00
|
|
|
from api.db.joint_services.tenant_model_service import get_tenant_default_model_by_type
|
2025-08-19 16:41:18 +08:00
|
|
|
from rag.nlp import rag_tokenizer, tokenize
|
2024-07-22 19:17:30 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def chunk(filename, binary, tenant_id, lang, callback=None, **kwargs):
|
2025-08-19 16:41:18 +08:00
|
|
|
doc = {"docnm_kwd": filename, "title_tks": rag_tokenizer.tokenize(re.sub(r"\.[a-zA-Z]+$", "", filename))}
|
2024-07-22 19:17:30 +08:00
|
|
|
doc["title_sm_tks"] = rag_tokenizer.fine_grained_tokenize(doc["title_tks"])
|
|
|
|
|
|
|
|
|
|
# is it English
|
2025-12-30 11:09:18 +08:00
|
|
|
is_english = lang.lower() == "english" # is_english(sections)
|
2024-07-22 19:17:30 +08:00
|
|
|
try:
|
2025-08-19 16:41:18 +08:00
|
|
|
_, ext = os.path.splitext(filename)
|
|
|
|
|
if not ext:
|
|
|
|
|
raise RuntimeError("No extension detected.")
|
|
|
|
|
|
2026-07-03 12:53:39 +08:00
|
|
|
if ext not in [".da", ".wave", ".wav", ".mp3", ".aac", ".flac", ".ogg", ".aiff", ".au", ".midi", ".wma", ".realaudio", ".vqf", ".oggvorbis", ".ape"]:
|
2025-08-19 16:41:18 +08:00
|
|
|
raise RuntimeError(f"Extension {ext} is not supported yet.")
|
|
|
|
|
|
|
|
|
|
tmp_path = ""
|
|
|
|
|
with tempfile.NamedTemporaryFile(suffix=ext, delete=False) as tmpf:
|
|
|
|
|
tmpf.write(binary)
|
|
|
|
|
tmpf.flush()
|
|
|
|
|
tmp_path = os.path.abspath(tmpf.name)
|
|
|
|
|
|
2024-07-22 19:17:30 +08:00
|
|
|
callback(0.1, "USE Sequence2Txt LLM to transcription the audio")
|
2026-03-05 17:27:17 +08:00
|
|
|
seq2txt_model_config = get_tenant_default_model_by_type(tenant_id, LLMType.SPEECH2TEXT)
|
|
|
|
|
seq2txt_mdl = LLMBundle(tenant_id, seq2txt_model_config, lang=lang)
|
2025-08-19 16:41:18 +08:00
|
|
|
ans = seq2txt_mdl.transcription(tmp_path)
|
2024-07-22 19:17:30 +08:00
|
|
|
callback(0.8, "Sequence2Txt LLM respond: %s ..." % ans[:32])
|
2025-08-19 16:41:18 +08:00
|
|
|
|
feat: add native Dutch language support for BM25 tokenization (#14140)
## Summary
- Add language-aware Snowball stemmer to `RagTokenizer` supporting 16
languages (Dutch, German, French, Spanish, etc.)
- Thread the KB `language` parameter through the full tokenization
pipeline (14 parser modules + task executor)
- Add Dutch to the frontend language lists and cross-language form
## Problem
RAGFlow uses the English Porter stemmer + WordNet lemmatizer for **all**
BM25 tokenization, regardless of the knowledge base language setting.
This produces incorrect stems for non-English text. For example:
| Dutch word | Dutch stemmer | English Porter |
|---|---|---|
| documenten | document | documenten (unchanged!) |
| gebruikers | gebruiker | gebruik (over-stemmed) |
| instellingen | instell | instellingen (unchanged!) |
This degrades BM25 recall for any non-English knowledge base.
## Solution
NLTK already ships Snowball stemmers for 16 languages. This PR:
1. **`rag/nlp/rag_tokenizer.py`**: Overrides `tokenize()` with
`set_language()` and `_normalize_token()` that selects the correct NLTK
Snowball stemmer. Falls back to Porter for unmapped languages (Chinese,
Japanese, Korean, etc. — these use character-based tokenization anyway).
2. **`rag/nlp/__init__.py`** + **14 `rag/app/*.py` parsers** +
**`rag/svr/task_executor.py`**: Threads the `language` parameter through
`tokenize()`, `tokenize_chunks()`, `tokenize_table()`, and all callers.
3. **Frontend**: Adds Dutch (`Nederlands`) to `LanguageList`,
`LanguageMap`, `LanguageAbbreviationMap`, `LanguageTranslationMap`,
cross-language form field, and `en.ts` locale.
## Backward Compatibility
- Default language is `"English"`, preserving existing behavior for all
current users
- Languages without a Snowball stemmer mapping fall back to Porter (no
change)
- No new dependencies — NLTK Snowball is already bundled
2026-07-06 17:39:56 +02:00
|
|
|
tokenize(doc, ans, is_english, language=lang)
|
2024-07-22 19:17:30 +08:00
|
|
|
return [doc]
|
|
|
|
|
except Exception as e:
|
|
|
|
|
callback(prog=-1, msg=str(e))
|
2025-08-19 16:41:18 +08:00
|
|
|
finally:
|
|
|
|
|
if tmp_path and os.path.exists(tmp_path):
|
|
|
|
|
try:
|
|
|
|
|
os.unlink(tmp_path)
|
2025-12-30 11:09:18 +08:00
|
|
|
except Exception as e:
|
|
|
|
|
logging.exception(f"Failed to remove temporary file: {tmp_path}, exception: {e}")
|
2025-08-19 16:41:18 +08:00
|
|
|
pass
|
2024-07-22 19:17:30 +08:00
|
|
|
return []
|