From 2fae96d60c04712e49e55d14349f7b99869adbff Mon Sep 17 00:00:00 2001 From: LauraGPT Date: Mon, 29 Jun 2026 20:58:51 +0000 Subject: [PATCH 1/3] fix: allow configuring FunASR recognition language --- main/xiaozhi-server/config.yaml | 3 + .../core/providers/asr/fun_local.py | 3 +- .../tests/test_fun_local_language.py | 87 +++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 main/xiaozhi-server/tests/test_fun_local_language.py diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 802dcb50..323b562e 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -360,6 +360,9 @@ ASR: type: fun_local model_dir: models/SenseVoiceSmall output_dir: tmp/ + # 识别语种:auto 自动检测;如需限制只识别中文可设为 zh,避免中文短句误判为韩文等。 + # SenseVoice 支持 zh、en、ja、ko、yue 等语种标记。 + language: auto FunASRServer: # 独立部署FunASR,使用FunASR的API服务,只需要五句话 # 第一句:mkdir -p ./funasr-runtime-resources/models diff --git a/main/xiaozhi-server/core/providers/asr/fun_local.py b/main/xiaozhi-server/core/providers/asr/fun_local.py index 62ae99e7..fa428710 100644 --- a/main/xiaozhi-server/core/providers/asr/fun_local.py +++ b/main/xiaozhi-server/core/providers/asr/fun_local.py @@ -50,6 +50,7 @@ class ASRProvider(ASRProviderBase): self.interface_type = InterfaceType.LOCAL self.model_dir = config.get("model_dir") self.output_dir = config.get("output_dir") # 修正配置键名 + self.language = config.get("language", "auto") self.delete_audio_file = delete_audio_file # 确保输出目录存在 @@ -80,7 +81,7 @@ class ASRProvider(ASRProviderBase): self.model.generate, input=artifacts.pcm_bytes, cache={}, - language="auto", + language=self.language, use_itn=True, batch_size_s=60, ) diff --git a/main/xiaozhi-server/tests/test_fun_local_language.py b/main/xiaozhi-server/tests/test_fun_local_language.py new file mode 100644 index 00000000..9c1dd35b --- /dev/null +++ b/main/xiaozhi-server/tests/test_fun_local_language.py @@ -0,0 +1,87 @@ +import asyncio +import importlib +import sys +import types +from pathlib import Path + + +def install_fakes(monkeypatch): + class Logger: + def bind(self, **_kwargs): + return self + + def debug(self, *_args, **_kwargs): + pass + + def error(self, *_args, **_kwargs): + pass + + def info(self, *_args, **_kwargs): + pass + + def warning(self, *_args, **_kwargs): + pass + + logger_module = types.ModuleType("config.logger") + logger_module.setup_logging = lambda: Logger() + config_module = types.ModuleType("config") + config_module.__path__ = [] + monkeypatch.setitem(sys.modules, "config", config_module) + monkeypatch.setitem(sys.modules, "config.logger", logger_module) + + psutil_module = types.ModuleType("psutil") + psutil_module.virtual_memory = lambda: types.SimpleNamespace(total=4 * 1024 * 1024 * 1024) + monkeypatch.setitem(sys.modules, "psutil", psutil_module) + + class Base: + pass + + base_module = types.ModuleType("core.providers.asr.base") + base_module.ASRProviderBase = Base + monkeypatch.setitem(sys.modules, "core.providers.asr.base", base_module) + + dto_module = types.ModuleType("core.providers.asr.dto.dto") + dto_module.InterfaceType = types.SimpleNamespace(LOCAL="local") + monkeypatch.setitem(sys.modules, "core.providers.asr.dto.dto", dto_module) + + utils_module = types.ModuleType("core.providers.asr.utils") + utils_module.lang_tag_filter = lambda text: {"content": text} + monkeypatch.setitem(sys.modules, "core.providers.asr.utils", utils_module) + + class FakeAutoModel: + last_instance = None + + def __init__(self, **_kwargs): + self.generate_kwargs = None + FakeAutoModel.last_instance = self + + def generate(self, **kwargs): + self.generate_kwargs = kwargs + return [{"text": "你好"}] + + funasr_module = types.ModuleType("funasr") + funasr_module.AutoModel = FakeAutoModel + monkeypatch.setitem(sys.modules, "funasr", funasr_module) + return FakeAutoModel + + +def test_fun_local_passes_configured_language_to_funasr_generate(tmp_path, monkeypatch): + server_root = Path(__file__).resolve().parents[1] + monkeypatch.syspath_prepend(str(server_root)) + fake_model = install_fakes(monkeypatch) + monkeypatch.delitem(sys.modules, "core.providers.asr.fun_local", raising=False) + module = importlib.import_module("core.providers.asr.fun_local") + + provider = module.ASRProvider( + {"model_dir": "models/SenseVoiceSmall", "output_dir": str(tmp_path), "language": "zh"}, + delete_audio_file=False, + ) + + artifacts = types.SimpleNamespace(pcm_bytes=b"\0\0", file_path=str(tmp_path / "audio.wav")) + text, audio_path = asyncio.run( + provider.speech_to_text([], "session-1", audio_format="pcm", artifacts=artifacts) + ) + + assert text == {"content": "你好"} + assert audio_path == artifacts.file_path + assert fake_model.last_instance.generate_kwargs["language"] == "zh" From 5a9b5af44182be33dffa318aa2f81bcad692fa9e Mon Sep 17 00:00:00 2001 From: zhifu gao Date: Tue, 30 Jun 2026 14:07:16 +0800 Subject: [PATCH 2/3] fix: add FunASR language config migration --- .../src/main/resources/db/changelog/202606301405.sql | 9 +++++++++ .../main/resources/db/changelog/db.changelog-master.yaml | 7 +++++++ 2 files changed, 16 insertions(+) create mode 100644 main/manager-api/src/main/resources/db/changelog/202606301405.sql mode change 100755 => 100644 main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml diff --git a/main/manager-api/src/main/resources/db/changelog/202606301405.sql b/main/manager-api/src/main/resources/db/changelog/202606301405.sql new file mode 100644 index 00000000..4939a68c --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202606301405.sql @@ -0,0 +1,9 @@ +-- Add language configuration for local FunASR ASR. +UPDATE `ai_model_provider` +SET `fields` = '[{"key":"model_dir","label":"模型目录","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"language","label":"识别语言","type":"string","default":"auto"}]' +WHERE `id` = 'SYSTEM_ASR_FunASR'; + +UPDATE `ai_model_config` +SET `config_json` = JSON_SET(`config_json`, '$.language', 'auto') +WHERE `id` = 'ASR_FunASR' +AND JSON_EXTRACT(`config_json`, '$.language') IS NULL; diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml old mode 100755 new mode 100644 index 9be7149e..2dcc1921 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -690,3 +690,10 @@ databaseChangeLog: - sqlFile: encoding: utf8 path: classpath:db/changelog/202606261131.sql + - changeSet: + id: 202606301405 + author: LauraGPT + changes: + - sqlFile: + encoding: utf8 + path: classpath:db/changelog/202606301405.sql From bc0ffd1bd3527f303ba397007bfa1abae3c6b485 Mon Sep 17 00:00:00 2001 From: 3030332422 <3030332422@qq.com> Date: Wed, 1 Jul 2026 15:02:26 +0800 Subject: [PATCH 3/3] =?UTF-8?q?update=EF=BC=9A=E4=BF=AE=E6=94=B9sql?= =?UTF-8?q?=E6=96=87=E4=BB=B6=EF=BC=8C=E5=88=A0=E5=8E=BB=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../resources/db/changelog/202606301405.sql | 9 -- .../resources/db/changelog/202607011405.sql | 19 ++++ .../db/changelog/db.changelog-master.yaml | 4 +- .../tests/test_fun_local_language.py | 87 ------------------- 4 files changed, 21 insertions(+), 98 deletions(-) delete mode 100644 main/manager-api/src/main/resources/db/changelog/202606301405.sql create mode 100644 main/manager-api/src/main/resources/db/changelog/202607011405.sql delete mode 100644 main/xiaozhi-server/tests/test_fun_local_language.py diff --git a/main/manager-api/src/main/resources/db/changelog/202606301405.sql b/main/manager-api/src/main/resources/db/changelog/202606301405.sql deleted file mode 100644 index 4939a68c..00000000 --- a/main/manager-api/src/main/resources/db/changelog/202606301405.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Add language configuration for local FunASR ASR. -UPDATE `ai_model_provider` -SET `fields` = '[{"key":"model_dir","label":"模型目录","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"language","label":"识别语言","type":"string","default":"auto"}]' -WHERE `id` = 'SYSTEM_ASR_FunASR'; - -UPDATE `ai_model_config` -SET `config_json` = JSON_SET(`config_json`, '$.language', 'auto') -WHERE `id` = 'ASR_FunASR' -AND JSON_EXTRACT(`config_json`, '$.language') IS NULL; diff --git a/main/manager-api/src/main/resources/db/changelog/202607011405.sql b/main/manager-api/src/main/resources/db/changelog/202607011405.sql new file mode 100644 index 00000000..260ea666 --- /dev/null +++ b/main/manager-api/src/main/resources/db/changelog/202607011405.sql @@ -0,0 +1,19 @@ +-- Add language configuration for local FunASR ASR. +UPDATE `ai_model_provider` +SET `fields` = '[{"key":"model_dir","label":"模型目录","type":"string"},{"key":"output_dir","label":"输出目录","type":"string"},{"key":"language","label":"识别语言","type":"string","default":"auto"}]' +WHERE `id` = 'SYSTEM_ASR_FunASR'; + +UPDATE `ai_model_config` +SET `config_json` = JSON_SET(`config_json`, '$.language', 'auto') +WHERE `id` = 'ASR_FunASR' +AND JSON_EXTRACT(`config_json`, '$.language') IS NULL; + +-- Update the FunASR local model configuration description to mention the language option. +UPDATE `ai_model_config` +SET `remark` = 'FunASR本地模型配置说明: +1. 需要下载模型文件到xiaozhi-server/models/SenseVoiceSmall目录 +2. 支持中日韩粤语音识别 +3. 本地推理,无需网络连接 +4. 待识别文件保存在tmp/目录 +5. “识别语言”字段控制识别语种:auto = 自动检测;如需限定只识别中文可设为 zh(en=英语、ja=日语、ko=韩语、yue=粤语)。' +WHERE `id` = 'ASR_FunASR'; diff --git a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml index 2dcc1921..a1902bf4 100644 --- a/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/main/manager-api/src/main/resources/db/changelog/db.changelog-master.yaml @@ -691,9 +691,9 @@ databaseChangeLog: encoding: utf8 path: classpath:db/changelog/202606261131.sql - changeSet: - id: 202606301405 + id: 202607011405 author: LauraGPT changes: - sqlFile: encoding: utf8 - path: classpath:db/changelog/202606301405.sql + path: classpath:db/changelog/202607011405.sql diff --git a/main/xiaozhi-server/tests/test_fun_local_language.py b/main/xiaozhi-server/tests/test_fun_local_language.py deleted file mode 100644 index 9c1dd35b..00000000 --- a/main/xiaozhi-server/tests/test_fun_local_language.py +++ /dev/null @@ -1,87 +0,0 @@ -import asyncio -import importlib -import sys -import types -from pathlib import Path - - -def install_fakes(monkeypatch): - class Logger: - def bind(self, **_kwargs): - return self - - def debug(self, *_args, **_kwargs): - pass - - def error(self, *_args, **_kwargs): - pass - - def info(self, *_args, **_kwargs): - pass - - def warning(self, *_args, **_kwargs): - pass - - logger_module = types.ModuleType("config.logger") - logger_module.setup_logging = lambda: Logger() - config_module = types.ModuleType("config") - config_module.__path__ = [] - monkeypatch.setitem(sys.modules, "config", config_module) - monkeypatch.setitem(sys.modules, "config.logger", logger_module) - - psutil_module = types.ModuleType("psutil") - psutil_module.virtual_memory = lambda: types.SimpleNamespace(total=4 * 1024 * 1024 * 1024) - monkeypatch.setitem(sys.modules, "psutil", psutil_module) - - class Base: - pass - - base_module = types.ModuleType("core.providers.asr.base") - base_module.ASRProviderBase = Base - monkeypatch.setitem(sys.modules, "core.providers.asr.base", base_module) - - dto_module = types.ModuleType("core.providers.asr.dto.dto") - dto_module.InterfaceType = types.SimpleNamespace(LOCAL="local") - monkeypatch.setitem(sys.modules, "core.providers.asr.dto.dto", dto_module) - - utils_module = types.ModuleType("core.providers.asr.utils") - utils_module.lang_tag_filter = lambda text: {"content": text} - monkeypatch.setitem(sys.modules, "core.providers.asr.utils", utils_module) - - class FakeAutoModel: - last_instance = None - - def __init__(self, **_kwargs): - self.generate_kwargs = None - FakeAutoModel.last_instance = self - - def generate(self, **kwargs): - self.generate_kwargs = kwargs - return [{"text": "你好"}] - - funasr_module = types.ModuleType("funasr") - funasr_module.AutoModel = FakeAutoModel - monkeypatch.setitem(sys.modules, "funasr", funasr_module) - return FakeAutoModel - - -def test_fun_local_passes_configured_language_to_funasr_generate(tmp_path, monkeypatch): - server_root = Path(__file__).resolve().parents[1] - monkeypatch.syspath_prepend(str(server_root)) - fake_model = install_fakes(monkeypatch) - monkeypatch.delitem(sys.modules, "core.providers.asr.fun_local", raising=False) - module = importlib.import_module("core.providers.asr.fun_local") - - provider = module.ASRProvider( - {"model_dir": "models/SenseVoiceSmall", "output_dir": str(tmp_path), "language": "zh"}, - delete_audio_file=False, - ) - - artifacts = types.SimpleNamespace(pcm_bytes=b"\0\0", file_path=str(tmp_path / "audio.wav")) - text, audio_path = asyncio.run( - provider.speech_to_text([], "session-1", audio_format="pcm", artifacts=artifacts) - ) - - assert text == {"content": "你好"} - assert audio_path == artifacts.file_path - assert fake_model.last_instance.generate_kwargs["language"] == "zh"