From 2dbf9390d01f8f7587da232f5289b141e8a0091f Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Sun, 18 Oct 2026 07:30:19 +1300 Subject: [PATCH 01/10] feat(tts): add optional Japanese pronunciation dictionary pack --- .env.example | 5 + .gitignore | 1 + GPT_SoVITS/text/japanese.py | 71 +- LICENSES/GPT-SoVITS-NESTED-NOTICES.md | 16 +- LICENSES/JAPANESE-DICTIONARY.md | 68 + LICENSES/provenance.json | 18 +- THIRD_PARTY_NOTICES.md | 8 +- assets/index.json | 11 + config/asset_packages.py | 14 +- .../voice/tts_japanese_dictionary.json | 23 + config/settings.py | 1 + docs/external_asset_bundles.md | 8 + docs/japanese_pronunciation_dictionary.md | 172 + .../src/renderer/components/SettingsPage.tsx | 2 + .../components/voiceConfigurationCatalog.ts | 13 +- .../src/shared/configCatalog.generated.ts | 36 + .../tests/emotionReferenceSettings.test.mjs | 3 +- .../tests/voiceConfigurationCatalog.test.mjs | 16 +- release/source_release_policy.json | 4 + server/handlers/system_handler.py | 17 + tests/test_japanese_dictionary.py | 397 + tests/test_release_tooling.py | 10 + tools/audit_japanese_dictionary.py | 180 + .../data/japanese_dictionary_corrections.csv | 172 + tools/data/japanese_dictionary_source.json | 9911 +++++++++++++++++ tools/external_assets.py | 9 +- tools/prepare_japanese_dictionary.py | 123 + tools/tts_text_processor.py | 43 +- tts/first_sentence_audio_cache.py | 6 +- tts/japanese_dictionary.py | 182 + tts/pipeline.py | 3 +- 31 files changed, 11444 insertions(+), 99 deletions(-) create mode 100644 LICENSES/JAPANESE-DICTIONARY.md create mode 100644 config/catalog/voice/tts_japanese_dictionary.json create mode 100644 docs/japanese_pronunciation_dictionary.md create mode 100644 tests/test_japanese_dictionary.py create mode 100644 tools/audit_japanese_dictionary.py create mode 100644 tools/data/japanese_dictionary_corrections.csv create mode 100644 tools/data/japanese_dictionary_source.json create mode 100644 tools/prepare_japanese_dictionary.py create mode 100644 tts/japanese_dictionary.py diff --git a/.env.example b/.env.example index 62c9334d..f111b01e 100644 --- a/.env.example +++ b/.env.example @@ -569,3 +569,8 @@ TTS_OUTPUT_LANGUAGE=日文 # 日文 | 英文 # RENDER_FRAME_SOURCE=auto # auto | static # RENDER_FRAME_BUDGET_MIB=auto # auto | 384 | 768 | 2048 # END GENERATED CONFIG: graphics_frame_source + +# BEGIN GENERATED CONFIG: tts_japanese_dictionary +# Japanese pronunciation dictionary +ENABLE_JAPANESE_USER_DICTIONARY=true +# END GENERATED CONFIG: tts_japanese_dictionary diff --git a/.gitignore b/.gitignore index a218acc0..6c8c457c 100644 --- a/.gitignore +++ b/.gitignore @@ -140,6 +140,7 @@ assets/companion/** assets/images/* !assets/images/amadeus_desktop_wallpaper.png assets/previews/** +assets/text/ja/** assets/scenarios/** assets/audio/sfx/*.wav diff --git a/GPT_SoVITS/text/japanese.py b/GPT_SoVITS/text/japanese.py index e023ce7a..b42db694 100644 --- a/GPT_SoVITS/text/japanese.py +++ b/GPT_SoVITS/text/japanese.py @@ -1,68 +1,10 @@ # modified from https://github.com/CjangCjengh/vits/blob/main/text/japanese.py import re -import os -import hashlib -try: - import pyopenjtalk - current_file_path = os.path.dirname(__file__) - - # 防止win下无法读取模型 - if os.name == 'nt': - python_dir = os.getcwd() - OPEN_JTALK_DICT_DIR = pyopenjtalk.OPEN_JTALK_DICT_DIR.decode("utf-8") - if not (re.match(r'^[A-Za-z0-9_/\\:.\-]*$', OPEN_JTALK_DICT_DIR)): - if (OPEN_JTALK_DICT_DIR[:len(python_dir)].upper() == python_dir.upper()): - OPEN_JTALK_DICT_DIR = os.path.join(os.path.relpath(OPEN_JTALK_DICT_DIR,python_dir)) - else: - import shutil - if not os.path.exists('TEMP'): - os.mkdir('TEMP') - if not os.path.exists(os.path.join("TEMP", "ja")): - os.mkdir(os.path.join("TEMP", "ja")) - if os.path.exists(os.path.join("TEMP", "ja", "open_jtalk_dic")): - shutil.rmtree(os.path.join("TEMP", "ja", "open_jtalk_dic")) - shutil.copytree(pyopenjtalk.OPEN_JTALK_DICT_DIR.decode("utf-8"), os.path.join("TEMP", "ja", "open_jtalk_dic"), ) - OPEN_JTALK_DICT_DIR = os.path.join("TEMP", "ja", "open_jtalk_dic") - pyopenjtalk.OPEN_JTALK_DICT_DIR = OPEN_JTALK_DICT_DIR.encode("utf-8") - - if not (re.match(r'^[A-Za-z0-9_/\\:.\-]*$', current_file_path)): - if (current_file_path[:len(python_dir)].upper() == python_dir.upper()): - current_file_path = os.path.join(os.path.relpath(current_file_path,python_dir)) - else: - if not os.path.exists('TEMP'): - os.mkdir('TEMP') - if not os.path.exists(os.path.join("TEMP", "ja")): - os.mkdir(os.path.join("TEMP", "ja")) - if not os.path.exists(os.path.join("TEMP", "ja", "ja_userdic")): - os.mkdir(os.path.join("TEMP", "ja", "ja_userdic")) - shutil.copyfile(os.path.join(current_file_path, "ja_userdic", "userdict.csv"),os.path.join("TEMP", "ja", "ja_userdic", "userdict.csv")) - current_file_path = os.path.join("TEMP", "ja") - - - def get_hash(fp: str) -> str: - hash_md5 = hashlib.md5() - with open(fp, "rb") as f: - for chunk in iter(lambda: f.read(4096), b""): - hash_md5.update(chunk) - return hash_md5.hexdigest() - - USERDIC_CSV_PATH = os.path.join(current_file_path, "ja_userdic", "userdict.csv") - USERDIC_BIN_PATH = os.path.join(current_file_path, "ja_userdic", "user.dict") - USERDIC_HASH_PATH = os.path.join(current_file_path, "ja_userdic", "userdict.md5") - # 如果没有用户词典,就生成一个;如果有,就检查md5,如果不一样,就重新生成 - if os.path.exists(USERDIC_CSV_PATH): - if not os.path.exists(USERDIC_BIN_PATH) or get_hash(USERDIC_CSV_PATH) != open(USERDIC_HASH_PATH, "r",encoding='utf-8').read(): - pyopenjtalk.mecab_dict_index(USERDIC_CSV_PATH, USERDIC_BIN_PATH) - with open(USERDIC_HASH_PATH, "w", encoding='utf-8') as f: - f.write(get_hash(USERDIC_CSV_PATH)) - - if os.path.exists(USERDIC_BIN_PATH): - pyopenjtalk.update_global_jtalk_with_user_dict(USERDIC_BIN_PATH) -except Exception as e: - # print(e) - import pyopenjtalk - # failed to load user dictionary, ignore. - pass +import pyopenjtalk + +from tts.japanese_dictionary import configure_japanese_dictionary + +configure_japanese_dictionary(pyopenjtalk) from text.symbols import punctuation @@ -73,6 +15,9 @@ def get_hash(fp: str) -> str: # Regular expression matching non-Japanese characters or punctuation marks: _japanese_marks = re.compile( + # Preserve punctuation inside Latin words so a native dictionary can match + # ballet's / coca-cola. Standalone punctuation retains the usual boundary. + r"(?!(?<=[A-Za-zA-Za-z])(?:['’']|[\-−-](?=[A-Za-zA-Za-z])))" r"[^A-Za-z\d\u3005\u3040-\u30ff\u4e00-\u9fff\uff11-\uff19\uff21-\uff3a\uff41-\uff5a\uff66-\uff9d]" ) diff --git a/LICENSES/GPT-SoVITS-NESTED-NOTICES.md b/LICENSES/GPT-SoVITS-NESTED-NOTICES.md index 06167a2d..87929f61 100644 --- a/LICENSES/GPT-SoVITS-NESTED-NOTICES.md +++ b/LICENSES/GPT-SoVITS-NESTED-NOTICES.md @@ -73,9 +73,19 @@ in `LICENSES/Bert-VITS2-Cantonese-Yue-AGPL-3.0.txt`. ### Generated or unverified dictionaries and caches -The public source archive excludes `GPT_SoVITS/text/ja_userdic/**` because no -source or redistribution record accompanies the 38 MiB custom dictionary. -The Japanese frontend already treats the user dictionary as optional. +The public source archive continues to exclude the local +`GPT_SoVITS/text/ja_userdic/**` directory and compiled dictionaries. On +2026-10-16 its CSV was matched, after CRLF-to-LF normalization, to the canonical +GPT-SoVITS CSV at `1b47ca07ef43a284e0b8bd7f29d3e1e3ff76082a`, blob +`2be580b461185ce18c9000d9ec6c8032623e17fd`. This supersedes the earlier +unrecorded-source classification for those verified contents; arbitrary local +copies and edits are not thereby approved for publication. + +The optional `japanese-pronunciation` pack is prepared separately from the pinned +source, with explicit corrections, provenance and MIT/CMUdict/Amadeus notices. +See `LICENSES/JAPANESE-DICTIONARY.md` for evidence and source-chain limitations. +`assets/text/ja/**` remains outside source archives. The frontend treats the +pack as optional and keeps compiled files in a disposable local cache. It also excludes `GPT_SoVITS/text/**/*.pickle`. These are generated lookup caches: English and G2PW rebuild their caches from tracked text dictionaries, diff --git a/LICENSES/JAPANESE-DICTIONARY.md b/LICENSES/JAPANESE-DICTIONARY.md new file mode 100644 index 00000000..f91aba63 --- /dev/null +++ b/LICENSES/JAPANESE-DICTIONARY.md @@ -0,0 +1,68 @@ +# Optional Japanese pronunciation dictionary + +Checked on 2026-10-16 against the canonical `RVC-Boss/GPT-SoVITS` repository +at commit `1b47ca07ef43a284e0b8bd7f29d3e1e3ff76082a`: + +- Source: `GPT_SoVITS/text/ja_userdic/userdict.csv`. +- Git blob: `2be580b461185ce18c9000d9ec6c8032623e17fd`. +- SHA-256 of the UTF-8/LF file: + `d857e443ee48d9641096816a98996669602895411e4330d7d91d1dbe1103389f`. +- Source size: 17,180,971 bytes, 134,201 entries. +- The existing local CSV matches this source after CRLF-to-LF normalization. + This comparison identifies its contents, not the original download event. +- Last upstream CSV change: `eee607b71dae07fb3b29f8f2727490039b3ac984`. + +Source permalink: +https://github.com/RVC-Boss/GPT-SoVITS/blob/1b47ca07ef43a284e0b8bd7f29d3e1e3ff76082a/GPT_SoVITS/text/ja_userdic/userdict.csv + +## Attribution and license basis + +The immediate upstream repository carries the MIT license, copyright 2024 +RVC-Boss. Preserve its complete notice as `GPT-SoVITS-MIT.txt`. + +Upstream PR https://github.com/RVC-Boss/GPT-SoVITS/pull/1660 describes a +CMUdict-to-Katakana conversion using ChatGPT and DeepSeek. PR +https://github.com/RVC-Boss/GPT-SoVITS/pull/1670 restores that dictionary +after fixing imports. The author explicitly identifies imperfect translation +accuracy. The exact original CMUdict revision and per-entry verification are +not recorded in those PRs. Preserve `CMUdict-BSD-2-Clause.txt` for this stated +source chain; its existing 0.07 notice does not establish the exact translation +input revision. This release relies on the immediate upstream MIT grant, +consistent with the project's recorded upstream reliance policy, rather than +inventing a separate license for generated translations. + +Amadeus's preparation code and corrections follow the project's root +AGPL-3.0 license. The pack includes that complete text as +`Amadeus-AGPL-3.0.txt`, in addition to the upstream notices. Do not remove +upstream attribution or relabel the upstream work as solely Amadeus-authored. + +## Local derivative and distribution boundary + +`tools/prepare_japanese_dictionary.py` accepts only the pinned source contents +(after newline normalization), then applies the separately tracked +`tools/data/japanese_dictionary_corrections.csv`. The recipe records each +correction, the source revision, the source hash, and the source-chain limits. +Preparation additionally normalizes Unicode reading fields, untranslated +initialism readings and native mora counts without moving valid accent nuclei, +then removes only identical full rows. Different lexical readings remain. +The generated `provenance.json` additionally identifies the derivative's +version, hash, entry count, normalization rules and repair counters. + +Version 2026.10.16.3 retains the previous 11 singular-reading repairs and +complete `proposer` entry, and reviews the remaining 317 flagged entries: +94 lexical/script/annotation repairs, 65 phonetic representation repairs and +158 unchanged entries awaiting stronger evidence. The recipe identifies each +reviewed before/after pronunciation, relevant CMU/native evidence and measured +word-cost adjustments. Other readings of a corrected surface remain available. +Only repaired entries receive native MeCab surface spelling. `proposer` still +uses a provisional flat accent; +native listening review is still required. This does not claim exhaustive +dictionary accuracy or contextual disambiguation of all Japanese homographs. + +The CSV and notices are distributed separately as the optional +`japanese-pronunciation` asset pack. Large generated pack files remain outside +Git/source archives. No compiled `user.dict`, local `userdict.md5`, personal +CSV, model weights, or recordings are included in this pack. Compilation uses +the installed pyopenjtalk-plus runtime and writes only disposable cache files. +Updates do not overwrite the optional personal `assets/text/ja/user.csv` or +the existing legacy directory. diff --git a/LICENSES/provenance.json b/LICENSES/provenance.json index e580ea7b..3b597dfd 100644 --- a/LICENSES/provenance.json +++ b/LICENSES/provenance.json @@ -40,7 +40,7 @@ "modified": true, "release_action": "include", "gate_status": "ready", - "notes": "The initial Amadeus import has 155 exact blobs, 11 different blobs, and 7 local-only files across 173 non-BigVGAN files when compared with the closest verified upstream baseline; 33 scoped paths changed later, and local_tts_infer.py is the included Amadeus inference overlay. The scoped v2Pro extension adds the upstream ERes2NetV2 speaker encoder and conditioning contract while model weights remain external. The release relies on the immediate RVC-Boss/GPT-SoVITS repository-wide MIT grant while preserving the SoundStorm and opencpop source-chain caveats instead of assigning them an invented standalone license. The separately verified AGPL Cantonese frontend, UVR/AP-BWE utilities, unverified Japanese dictionary, generated caches, model weights, and voices are excluded by policy." + "notes": "The initial Amadeus import has 155 exact blobs, 11 different blobs, and 7 local-only files across 173 non-BigVGAN files when compared with the closest verified upstream baseline; 33 scoped paths changed later, and local_tts_infer.py is the included Amadeus inference overlay. The scoped v2Pro extension adds the upstream ERes2NetV2 speaker encoder and conditioning contract while model weights remain external. The release relies on the immediate RVC-Boss/GPT-SoVITS repository-wide MIT grant while preserving the SoundStorm and opencpop source-chain caveats instead of assigning them an invented standalone license. The separately verified AGPL Cantonese frontend, UVR/AP-BWE utilities, local Japanese dictionary copies, generated caches, model weights, and voices are excluded by policy. The optional verified Japanese dictionary has its own provenance record." }, { "id": "bert-vits2-cantonese-frontend", @@ -78,7 +78,21 @@ "modified": null, "release_action": "exclude", "gate_status": "ready", - "notes": "The custom Japanese dictionary has no checked-in source or redistribution record. Pickle files are generated caches or inactive local data and can be rebuilt or omitted by their current readers. The public source archive excludes all of them without deleting the private checkout copies." + "notes": "The existing Japanese CSV was matched after newline normalization to canonical GPT-SoVITS blob 2be580b461185ce18c9000d9ec6c8032623e17fd on 2026-10-16; source and license evidence are recorded separately in JAPANESE-DICTIONARY.md. This legacy local directory may contain personal edits and compiled binaries and remains excluded without deleting it. Pickle files are generated caches or inactive local data and can be rebuilt or omitted." + }, + { + "id": "japanese-pronunciation", + "name": "Optional Japanese pronunciation dictionary", + "category": "external-runtime-data", + "paths": ["assets/text/ja/english/**"], + "source_url": "https://github.com/RVC-Boss/GPT-SoVITS", + "source_revision": "1b47ca07ef43a284e0b8bd7f29d3e1e3ff76082a; userdict.csv blob 2be580b461185ce18c9000d9ec6c8032623e17fd; Amadeus corrections recorded in tools/data/japanese_dictionary_source.json", + "license_expression": "AGPL-3.0 AND MIT AND BSD-2-Clause", + "license_evidence": ["LICENSES/JAPANESE-DICTIONARY.md", "LICENSES/GPT-SoVITS-MIT.txt", "LICENSES/CMUdict-BSD-2-Clause.txt", "LICENSE"], + "modified": true, + "release_action": "exclude", + "gate_status": "ready", + "notes": "Data is built as an optional external asset bundle, not included in source archives. Its separate pack carries complete notices and generated source/derivative hashes. Immediate upstream MIT reliance and the unrecorded exact CMUdict input revision are explicit; no exhaustive pronunciation or listening validation is claimed. This record does not publish any bundle." }, { "id": "gpt-sovits-uvr5-tools", diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 1cd97f1e..eb16e434 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -156,8 +156,12 @@ checkout is unchanged. ### GPT-SoVITS caches, custom Japanese dictionary, and UVR5 utility Generated `*.pickle` lookup caches and `GPT_SoVITS/text/ja_userdic/**` are -excluded. The cache readers rebuild or omit them, while the custom Japanese -dictionary has no recorded distribution source. `tools/uvr5/**` is also +excluded. The cache readers rebuild or omit them. The local Japanese CSV has +now been matched to a pinned canonical GPT-SoVITS source; the independently +prepared optional `japanese-pronunciation` pack carries the CSV's provenance, +corrections and upstream/CMUdict/Amadeus license notices. See +`LICENSES/JAPANESE-DICTIONARY.md`. Its large files, personal dictionaries and +compiled caches remain excluded from source archives. `tools/uvr5/**` is also excluded because no Amadeus Electron/server/TTS runtime caller reaches that upstream WebUI utility tree. diff --git a/assets/index.json b/assets/index.json index 915ee2e3..a0a5020e 100644 --- a/assets/index.json +++ b/assets/index.json @@ -18,6 +18,17 @@ } ], "external_packs": [ + { + "id": "japanese-pronunciation", + "display_name": "Japanese Pronunciation Extension", + "spec_version": 1, + "paths": [], + "trees": ["text/ja/english"], + "required": ["text/ja/english/userdict.csv", "text/ja/english/provenance.json", + "text/ja/english/NOTICE.md", "text/ja/english/GPT-SoVITS-MIT.txt", + "text/ja/english/CMUdict-BSD-2-Clause.txt", "text/ja/english/Amadeus-AGPL-3.0.txt"], + "validator": "japanese_dictionary" + }, { "id": "voice-kurisu-emotions", "display_name": "Kurisu V3 Emotion Reference Pack", diff --git a/config/asset_packages.py b/config/asset_packages.py index 54e4e32c..42576906 100644 --- a/config/asset_packages.py +++ b/config/asset_packages.py @@ -83,7 +83,7 @@ def asset_pack_specs(index_path: Path = ASSET_INDEX_PATH) -> dict[str, AssetPack if spec_version <= 0: raise AssetPackageError(f"external pack {pack_id!r} has an invalid spec version") validator = str(entry.get("validator") or "required_files").strip() - if validator not in {"required_files", "visual_runtime", "character_pack", "companion_pack", "tts_reference_pack"}: + if validator not in {"required_files", "visual_runtime", "character_pack", "companion_pack", "tts_reference_pack", "japanese_dictionary"}: raise AssetPackageError(f"external pack {pack_id!r} has an unknown validator") spec = AssetPackSpec( id=pack_id, @@ -100,6 +100,8 @@ def asset_pack_specs(index_path: Path = ASSET_INDEX_PATH) -> dict[str, AssetPack raise AssetPackageError("companion pack requires exactly one runtime tree") if spec.validator == "tts_reference_pack" and len(spec.trees) != 1: raise AssetPackageError("TTS reference pack requires exactly one runtime tree") + if spec.validator == "japanese_dictionary" and len(spec.trees) != 1: + raise AssetPackageError("Japanese dictionary requires exactly one runtime tree") specs[pack_id] = spec return specs @@ -135,6 +137,7 @@ def external_asset_pack_status( present = [path for path in spec.required if asset_path(asset_root, path).is_file()] missing = [path for path in spec.required if path not in present] message = "" + version = "" if not present: state = "not_installed" elif missing: @@ -150,6 +153,14 @@ def external_asset_pack_status( state = "invalid" message = str(exc) + if state == "installed" and spec.validator == "japanese_dictionary": + from tts.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack + try: + version = load_dictionary_pack(asset_path(asset_root, spec.trees[0])).version + except JapaneseDictionaryError as exc: + state = "invalid" + message = str(exc) + if state == "installed" and spec.validator == "visual_runtime": graph_path = asset_path(asset_root, PurePosixPath("scenarios/runtime/scenario_graph.json")) try: @@ -182,5 +193,6 @@ def external_asset_pack_status( "required_count": len(spec.required), "missing": [path.as_posix() for path in missing], "message": message, + **({"version": version} if spec.validator == "japanese_dictionary" else {}), "install_command": "python tools/external_assets.py install ", } diff --git a/config/catalog/voice/tts_japanese_dictionary.json b/config/catalog/voice/tts_japanese_dictionary.json new file mode 100644 index 00000000..8f3fb21a --- /dev/null +++ b/config/catalog/voice/tts_japanese_dictionary.json @@ -0,0 +1,23 @@ +{ + "id": "tts_japanese_dictionary", + "title": {"en-US": "Japanese pronunciation dictionary", "zh-CN": "日语发音词典"}, + "description": { + "en-US": "Use an optionally installed dictionary for English words and names in local Japanese speech.", + "zh-CN": "为本地日语语音使用可选安装的英文单词与人名读音词典。" + }, + "desktop": true, + "apply": "backend_restart", + "config": { + "ENABLE_JAPANESE_USER_DICTIONARY": { + "type": "boolean", + "title": {"en-US": "Use installed Japanese dictionaries", "zh-CN": "使用已安装的日语词典"}, + "description": { + "en-US": "Does not download a dictionary. Install japanese-pronunciation separately. Turn off to use the base dictionary; restart the backend after changes.", + "zh-CN": "不会下载词典,请单独安装 japanese-pronunciation。关闭后使用基础词典;更改后需重启后端。" + }, + "default": true, + "accepted_values": ["true", "false", "1", "0", "yes", "no"], + "example_active": true + } + } +} diff --git a/config/settings.py b/config/settings.py index 801e79fa..9232fe66 100644 --- a/config/settings.py +++ b/config/settings.py @@ -134,6 +134,7 @@ ASR_LISTEN_TIMEOUT_SECONDS: float = _catalog_values["ASR_LISTEN_TIMEOUT_SECONDS"] ASR_VAD_SILENCE_MS: int = _catalog_values["ASR_VAD_SILENCE_MS"] ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING: bool = _catalog_values["ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING"] +ENABLE_JAPANESE_USER_DICTIONARY: bool = _catalog_values["ENABLE_JAPANESE_USER_DICTIONARY"] TTS_REF_AUDIO_JA: str = _catalog_values["TTS_REF_AUDIO_JA"] TTS_REF_TEXT_JA: str = _catalog_values["TTS_REF_TEXT_JA"] TTS_REF_AUDIO_EN: str = _catalog_values["TTS_REF_AUDIO_EN"] diff --git a/docs/external_asset_bundles.md b/docs/external_asset_bundles.md index 75a7b855..782b1d3d 100644 --- a/docs/external_asset_bundles.md +++ b/docs/external_asset_bundles.md @@ -137,6 +137,14 @@ reviewed before publishing either voice archive. ## Archive contract +The independent optional `japanese-pronunciation` pack contains a pinned, +corrected Japanese word/name CSV and its provenance/license notices. Build, +verify and install use the existing commands; compiled and personal dictionaries +are excluded. This requires an application version with the new catalog entry. +See [Japanese pronunciation dictionary](japanese_pronunciation_dictionary.md). +It can be appended to an existing resource Release without rebuilding the other +archives; keep dictionary versions as distinct immutable filenames. + The optional `voice-kurisu-emotions` pack adds paired Japanese semantic references to an existing Windows CUDA V3 installation. It uses the same bundle contract and installer; it does not include model weights or overwrite default references. diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md new file mode 100644 index 00000000..7e353da0 --- /dev/null +++ b/docs/japanese_pronunciation_dictionary.md @@ -0,0 +1,172 @@ +# Optional Japanese pronunciation dictionary / 可选日语发音词典 + +`japanese-pronunciation` adds English loanword and selected name readings to +local GPT-SoVITS Japanese synthesis. It is independent of a character, voice +checkpoint and emotion-reference pack. No automatic download is performed; +the base Japanese frontend remains usable without it. + +`japanese-pronunciation` 为本地 GPT-SoVITS 日语语音补充英文单词及部分人名读音。 +它独立于角色、模型权重和情绪参考包;不会自动下载,未安装时仍可使用基础日语前端。 + +## Install and control / 安装与控制 + +Update the application to a version containing this pack's catalog entry and +loader before installation. Older applications reject the unknown pack ID. +The first compatible application release has not yet been published. + +```powershell +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/external_assets.py status +``` + +Restart the backend after installation, updates, or switching the Voice setting +**Use installed Japanese dictionaries**. Its environment key is +`ENABLE_JAPANESE_USER_DICTIONARY`; `false` disables all optional/personal/legacy +user dictionaries, leaving the base dictionary active. It defaults to true to +preserve existing installations, but does not install anything. + +安装前须更新到包含此词典目录及加载器的应用版本;旧版会拒绝未知包。 +安装、升级或更改“语音 → 使用已安装的日语词典”后需重启后端。 +`ENABLE_JAPANESE_USER_DICTIONARY=false` 使用基础词典。 +默认允许使用已安装词典,以保留现有本机行为;不代表自动安装资源。 + +Resource status reports the pack version and validates the CSV shape, +provenance hash/count and required notices without importing model libraries. +The Japanese frontend logs actual compilation/loading failures and keeps the +base frontend usable. The runtime ready state is separate from pack presence. +For subprocess synthesis, the parent settings page may show installation status +before its own frontend has loaded; the sidecar's startup log is the evidence +of actual dictionary activation. + +## Data and personal files / 数据与个人词条 + +- Official data installs below `assets/text/ja/english/`. +- Personal CSV entries may live at `assets/text/ja/user.csv`, outside the pack. + Use OpenJTalk's 15-column CSV format and appropriate entry costs; dictionary + registration order alone does not promise precedence for duplicate words. +- If the new pack is absent, the existing + `GPT_SoVITS/text/ja_userdic/userdict.csv` remains a supported local source. + An installed pack replaces that legacy source's role; an invalid installed + pack is reported instead of silently switching back to the legacy copy. +- Compilation writes only `.cache/japanese_dictionary/`, keyed by source bytes, + native runtime version and the installed system dictionary identity. The + application does not rewrite or distribute personal/legacy CSVs or binaries. +- First-sentence audio cache keys include the selected dictionary identity, so + installing, disabling or updating data cannot reuse audio with old readings + after restart. Existing audio files are not deleted. + +用户 CSV 与正式包分开,安装和升级不会覆盖。旧词典目录也不会被删除或改写。 +编译结果是本机缓存,不能作为可维护的词典源。个人重复词条的优先级须通过 +OpenJTalk 词条代价控制;文件排列顺序不保证覆盖。 + +## Prepare a local release candidate / 准备本地候选包 + +The checked recipe and license evidence live in +`tools/data/japanese_dictionary_source.json` and +`LICENSES/JAPANESE-DICTIONARY.md`. Obtain that exact upstream CSV separately, +or use an existing matching local copy. Preparation is offline, normalizes LF, +checks the source hash and applies the tracked correction CSV. The maintainer's +preparation environment needs the existing local-model dependency +`pyopenjtalk-plus==0.4.1.post9`, pinned in the recipe for repeatable mora counts. +Installation/status checks need no model libraries. Preparation refuses a +different customized source and differing existing output unless `--overwrite` +is explicit. + +```powershell +python tools/prepare_japanese_dictionary.py --source GPT_SoVITS/text/ja_userdic/userdict.csv +python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/audit_japanese_dictionary.py --source assets/text/ja/english/userdict.csv --output output/japanese_dictionary_audit.json +``` + +The bundle contains the prepared CSV, its version/provenance and complete +license notices. It excludes compiled dictionaries and extra working files. +Preparation and these commands do not upload, publish, alter `.env`, change +model paths or rebuild any existing model/voice archive. + +## Quality boundary / 质量范围 + +Version 2026.10.16.3 reviews the 317 entries left by version 2026.10.16.2: +94 receive reviewed lexical/script/annotation repairs, 65 receive phonetic +separator or format-control repairs, and 158 remain unchanged pending stronger +transcription or accent evidence. The local review report records every +disposition. An all-kana alternative alone is not proof of lexical correctness. +Examples include `balloonist` (`二` → `ニ`), `ninth` (`ん` → `ン`), untranslated +letter names, stray inflection letters and literal LLM explanations. + +The correction recipe targets the reviewed **before pronunciation**; it does +not replace every pronunciation sharing that word. Legitimate alternatives +such as singular/plural `corps` and initialism/plural `CDS/CDs` remain available; +no context classifier is added to force a single choice. + +Native input converts apostrophes and hyphens to `’` and `−`. Only repaired +entries receive this native surface spelling, and the frontend preserves +Latin compound punctuation until native tokenization. Standalone punctuation +and Japanese punctuation keep their normal handling. Native MeCab measurements +in three synthetic contexts exposed cheap unintended word splits; 109 reviewed +entry costs were reduced just enough to beat those measured splits. The +measured costs are stored in the CSV/recipe, with no runtime word-specific +pronunciation or cost rules. Unconfirmed entries retain their original rows. + +The pack contains 128,111 entries. Re-auditing it produces no further structural +repairs and leaves the 158 review entries intact. No accent is clamped or +invented for `guangjo`, whose source accent exceeds the native mora count. + +The earlier version's 11 singular-reading repairs are retained: +`follower`, `learner`, `abstraction`, `acronym`, `adapter`, `airline`, +`applicator`, `bank`, `component`, `developer`, and `handler`. Their plural +entries remain intact. It also adds the complete `proposer` → `プロポーザー` +entry; its flat accent is provisional pending listening review. +The `component` row also has its entry cost corrected from 608 to 0: native +full-pack validation otherwise selects `comp + on + ent`, despite the correct +whole-word reading being present. The corresponding `components` cost changes +from 659 to 0 to keep its existing plural reading selected. Both sides are +checked by the native test. These repairs stay in dictionary data. + +Build-time normalization uses general rules: NFC in reading fields, +removing phonetic-neutral separators only from otherwise pure kana when the +existing accent remains valid, spelling untranslated whole-token initialisms, +counting morae from one explicit +native pronunciation while preserving valid accent nuclei, and removing only +identical full rows. It does not strip English plural suffixes automatically +or merge different readings of one word. Passing kana through ordinary g2p +would retokenize it and can incorrectly split small kana; the audit feeds the +pronunciation directly to the native label builder instead. + +Preparation records repair counters before deduplication; the release's 65 +representation repairs are explicitly tracked alongside their measured costs. +The audit also lists CMUdict singular/inflection collisions as review candidates; +such collisions alone do not prove errors (`path` and `paths` can both be +`パス`). It does not guess replacements for these entries. + +运行时没有新增逐词判断。原词条修复记录在 correction CSV;通用数据清理在构建时 +执行,来源、修复理由、规则及计数均随包保存。不同读音保留,无法确定的重音或 +机器翻译残留只生成待核对清单。后续可沿用同一审计流程逐批修复原数据。 + +Whole-token abbreviation preparation handles `MLP` next to Japanese, fullwidth +Latin text and case variants of existing names, and avoids replacing `API` +inside `CAPITAL`. It applies to Japanese synthesis text; English output, +subtitles and conversation history retain their original text. + +The upstream translation is not exhaustively human-verified. This is an English +word/name extension, not a general context-sensitive Japanese homograph model +or a voice/timbre replacement. Native pronunciation/prosody comparisons do not +replace end-to-end synthesis and human listening acceptance. + +## Publication / 发布 + +Keep each ZIP version immutable and attach it, with a separately published +SHA-256, to a GitHub Release in the existing resource repository or resource +Release, if it still allows new attachments. A published immutable Release +cannot accept new assets; use a new dictionary-only resource Release in that +case. An organization resource page can link to that exact asset and explain +the minimum compatible application release, changes and known limitations. +Adding this independent ZIP does not require rebuilding earlier model, voice, +visual or emotion packages. The application code itself needs a compatible +application release. A later correction gets a new dictionary version/filename; +do not replace an older same-named asset. + +可编辑的原资源 Release 可以追加独立词典 ZIP;不可变 Release 需新建仅含词典的 +资源 Release。旧资源包不必重打包。组织资源页面作为 +介绍与下载入口,并标明所需应用版本。词典升级新增版本文件,保留旧文件。 diff --git a/electron/src/renderer/components/SettingsPage.tsx b/electron/src/renderer/components/SettingsPage.tsx index 8a3157d8..4553b219 100644 --- a/electron/src/renderer/components/SettingsPage.tsx +++ b/electron/src/renderer/components/SettingsPage.tsx @@ -1117,12 +1117,14 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack }) const artifactConfiguration = mergeGroups([catalogConfiguration("auip_artifact_style", catalogDesktop)], asConfigurationGroups(config.artifact_configuration)) const backendVoiceConfiguration = asConfigurationGroups(config.voice_configuration) + const dictionaryRuntimeValue = backendVoiceConfiguration.find(group => group.id === 'tts_japanese_dictionary')?.fields.find(field => field.key === 'ENABLE_JAPANESE_USER_DICTIONARY')?.value const voiceCatalog = buildVoiceConfigurationCatalog({ asrBackend: desktop?.sources?.ASR_BACKEND === 'user' ? desktop.values.ASR_BACKEND : val('asr_backend', 'qwen3_asr'), ttsBackend: desktop?.sources?.TTS_BACKEND === 'user' ? desktop.values.TTS_BACKEND : val('tts_backend', 'gpt_sovits'), wakeEnabled: desktop?.sources?.WAKE_ENABLED === 'user' ? desktop.values.WAKE_ENABLED === 'true' : bool('wake_enabled'), aecEnabled: desktop?.sources?.AEC_REALTIME_ENABLED === 'user' ? desktop.values.AEC_REALTIME_ENABLED === 'true' : config.aec_realtime_enabled === undefined ? Boolean(desktopCatalogFields.AEC_REALTIME_ENABLED.default) : bool('aec_realtime_enabled'), emotionReferencesEnabled: backendVoiceConfiguration.find(group => group.id === 'tts_emotion_references')?.fields.find(field => field.key === 'ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING')?.value === true, + japaneseDictionaryEnabled: typeof dictionaryRuntimeValue === 'boolean' ? dictionaryRuntimeValue : undefined, }, { ...catalogDesktop, extensions: desktop?.extensions }, locale) const voiceConfiguration: ConfigurationGroup[] = voiceCatalog.map(base => { const backend = backendVoiceConfiguration.find(group => group.id === base.id) diff --git a/electron/src/renderer/components/voiceConfigurationCatalog.ts b/electron/src/renderer/components/voiceConfigurationCatalog.ts index c9c8396c..f0344962 100644 --- a/electron/src/renderer/components/voiceConfigurationCatalog.ts +++ b/electron/src/renderer/components/voiceConfigurationCatalog.ts @@ -16,6 +16,7 @@ interface VoiceRuntimeSelection { wakeEnabled: boolean aecEnabled: boolean emotionReferencesEnabled?: boolean + japaneseDictionaryEnabled?: boolean } export function buildVoiceConfigurationCatalog( @@ -34,12 +35,15 @@ export function buildVoiceConfigurationCatalog( ASR_BACKEND: selection.asrBackend, WAKE_ENABLED: selection.wakeEnabled, AEC_REALTIME_ENABLED: selection.aecEnabled, ...(selection.emotionReferencesEnabled === undefined ? {} : { ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING: selection.emotionReferencesEnabled }), + ...(selection.japaneseDictionaryEnabled === undefined ? {} : { ENABLE_JAPANESE_USER_DICTIONARY: selection.japaneseDictionaryEnabled }), } const unknown = 'Backend status unavailable' const conversation = catalogConfiguration('conversation_asr', snapshot, runtimeValues) const wake = catalogConfiguration('wake_asr', snapshot, runtimeValues) const acoustic = catalogConfiguration('acoustic_pipeline', snapshot, runtimeValues) const emotion = catalogConfiguration('tts_emotion_references', snapshot, runtimeValues) + const dictionary = catalogConfiguration('tts_japanese_dictionary', snapshot, runtimeValues) + const dictionaryEnabled = dictionary.fields[0].value === true const asrBackend = conversation.fields.find(field => field.key === 'ASR_BACKEND')?.value const wakeEnabled = wake.fields.find(field => field.key === 'WAKE_ENABLED')?.value const aecEnabled = acoustic.fields.find(field => field.key === 'AEC_REALTIME_ENABLED')?.value @@ -109,6 +113,13 @@ export function buildVoiceConfigurationCatalog( status_ok: false, }, + { + ...dictionary, + active: ttsBackend === 'gpt_sovits' && dictionaryEnabled, + configured: !dictionaryEnabled, + status: dictionaryEnabled ? unknown : 'Off', + status_ok: !dictionaryEnabled, + }, { ...emotion, @@ -122,7 +133,7 @@ export function buildVoiceConfigurationCatalog( } export const voiceConfigurationSections = { - output: new Set(['speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', + output: new Set(['speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', ...voiceBackendGroups.filter(group => group.section === 'output').map(group => group.id)]), input: new Set(['conversation_asr', 'wake_asr', 'acoustic_pipeline']), remote: new Set(['asr_remote', ...voiceBackendGroups.filter(group => group.section === 'remote').map(group => group.id)]), diff --git a/electron/src/shared/configCatalog.generated.ts b/electron/src/shared/configCatalog.generated.ts index 34348099..f013d0e5 100644 --- a/electron/src/shared/configCatalog.generated.ts +++ b/electron/src/shared/configCatalog.generated.ts @@ -3076,6 +3076,42 @@ export const catalogGroups: CatalogGroup[] = [ }, "apply": "backend_restart" }, + { + "id": "tts_japanese_dictionary", + "title": { + "en-US": "Japanese pronunciation dictionary", + "zh-CN": "日语发音词典" + }, + "description": { + "en-US": "Use an optionally installed dictionary for English words and names in local Japanese speech.", + "zh-CN": "为本地日语语音使用可选安装的英文单词与人名读音词典。" + }, + "desktop": true, + "apply": "backend_restart", + "config": { + "ENABLE_JAPANESE_USER_DICTIONARY": { + "type": "boolean", + "title": { + "en-US": "Use installed Japanese dictionaries", + "zh-CN": "使用已安装的日语词典" + }, + "description": { + "en-US": "Does not download a dictionary. Install japanese-pronunciation separately. Turn off to use the base dictionary; restart the backend after changes.", + "zh-CN": "不会下载词典,请单独安装 japanese-pronunciation。关闭后使用基础词典;更改后需重启后端。" + }, + "default": true, + "accepted_values": [ + "true", + "false", + "1", + "0", + "yes", + "no" + ], + "example_active": true + } + } + }, { "id": "voice_reference_profile", "title": { diff --git a/electron/tests/emotionReferenceSettings.test.mjs b/electron/tests/emotionReferenceSettings.test.mjs index cd575b4b..038eb7cb 100644 --- a/electron/tests/emotionReferenceSettings.test.mjs +++ b/electron/tests/emotionReferenceSettings.test.mjs @@ -15,11 +15,10 @@ new Function('require', 'exports', compiled)(name => name === 'electron' ? { safeStorage: { isEncryptionAvailable: () => false } } : require(name), exports) -test('emotion reference setting persists both values and reaches the next backend', () => { +for (const key of ['ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING', 'ENABLE_JAPANESE_USER_DICTIONARY']) test(`${key} persists both values and reaches the next backend`, () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'amadeus-emotion-settings-')) const file = path.join(root, 'settings.json') const dotenv = path.join(root, '.env') - const key = 'ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING' try { const store = new exports.DesktopSettingsStore(file, dotenv) for (const value of [false, true]) { diff --git a/electron/tests/voiceConfigurationCatalog.test.mjs b/electron/tests/voiceConfigurationCatalog.test.mjs index 48c6e574..ce603620 100644 --- a/electron/tests/voiceConfigurationCatalog.test.mjs +++ b/electron/tests/voiceConfigurationCatalog.test.mjs @@ -11,7 +11,7 @@ test('voice setup remains discoverable without a running backend', () => { }, null) assert.deepEqual(new Set(groups.map(group => group.id)), new Set([ 'conversation_asr', 'asr_remote', 'wake_asr', 'acoustic_pipeline', - 'speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', + 'speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', ...voiceBackendGroups.map(group => group.id), ])) assert.equal(groups.find(group => group.id === 'asr_remote').status, 'Optional') @@ -88,6 +88,20 @@ test('emotion reference switch is default off, accepts the existing 1 value and } }) +test('Japanese dictionaries are optional and can be disabled with a restart', () => { + const selection = { asrBackend: 'qwen3_asr', ttsBackend: 'gpt_sovits', wakeEnabled: false, aecEnabled: false } + const field = value => exports.buildVoiceConfigurationCatalog(selection, { values: value }).find(group => group.id === 'tts_japanese_dictionary') + assert.equal(field({}).fields[0].value, true) + const disabled = field({ ENABLE_JAPANESE_USER_DICTIONARY: 'false' }) + assert.equal(disabled.active, false) + assert.equal(disabled.status, 'Off') + assert.equal(disabled.fields[0].restart_required, true) + assert.equal(exports.voiceConfigurationSections.output.has('tts_japanese_dictionary'), true) + const effective = exports.buildVoiceConfigurationCatalog({ ...selection, japaneseDictionaryEnabled: false }, null).find(group => group.id === 'tts_japanese_dictionary') + assert.equal(effective.fields[0].value, false) + assert.equal(effective.active, false) +}) + test('effective dotenv emotion setting is visible until a stored override changes it', () => { const selection = { asrBackend: 'qwen3_asr', ttsBackend: 'gpt_sovits', wakeEnabled: false, aecEnabled: true, emotionReferencesEnabled: true } const field = snapshot => exports.buildVoiceConfigurationCatalog(selection, snapshot).find(group => group.id === 'tts_emotion_references').fields[0] diff --git a/release/source_release_policy.json b/release/source_release_policy.json index 76274f55..46987bd3 100644 --- a/release/source_release_policy.json +++ b/release/source_release_policy.json @@ -45,6 +45,7 @@ "LICENSES/README.md", "LICENSES/GPT-SoVITS-MIT.txt", "LICENSES/GPT-SoVITS-NESTED-NOTICES.md", + "LICENSES/JAPANESE-DICTIONARY.md", "LICENSES/GPT-SoVITS-UPSTREAM-RELIANCE.md", "LICENSES/PyTorch-BSD-3-Clause.txt", "LICENSES/provenance.json", @@ -57,6 +58,7 @@ "docs/companion-panel.md", "docs/images/companion-panel-speaking.png", "docs/external_asset_bundles.md", + "docs/japanese_pronunciation_dictionary.md", "docs/install_profiles.md", "docs/slice-export-review.md", "docs/torch27_candidates.md", @@ -139,6 +141,7 @@ "wallpaper" ], "include_files": [ + "docs/japanese_pronunciation_dictionary.md", "docs/mcp_environment.md", "docs/backend_shutdown.md", "docs/configuration_convergence_plan_2026-10-10.md", @@ -277,6 +280,7 @@ "legacy/**", "GPT_SoVITS/text/cantonese.py", "GPT_SoVITS/text/ja_userdic/**", + "assets/text/ja/**", "GPT_SoVITS/text/**/*.pickle", "tools/AP_BWE_main/**", "tools/audio_sr.py", diff --git a/server/handlers/system_handler.py b/server/handlers/system_handler.py index ece1bf79..9f3dd157 100644 --- a/server/handlers/system_handler.py +++ b/server/handlers/system_handler.py @@ -129,6 +129,13 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No emotion_pack = external_asset_pack_status(PACK_ID) emotion_runtime = tts_pipeline.emotion_reference_status() emotion_requested = bool(settings.ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING) + from tts.japanese_dictionary import PACK_ID as DICTIONARY_PACK_ID, dictionary_runtime_status + dictionary_pack = external_asset_pack_status(DICTIONARY_PACK_ID) + dictionary_runtime = dictionary_runtime_status() + dictionary_enabled = bool(settings.ENABLE_JAPANESE_USER_DICTIONARY) + dictionary_state = "disabled" if not dictionary_enabled else ( + dictionary_runtime["state"] if dictionary_runtime["state"] != "not_loaded" else dictionary_pack["state"] + ) asr_selected = str(settings.ASR_BACKEND or "qwen3_asr").strip().lower() tts_selected = str(settings.TTS_BACKEND or "gpt_sovits").strip().lower() @@ -239,6 +246,16 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No }, *(_voice_backend_configuration(group, settings, tts_statuses, tts_selected) for group in voice_backend_groups()), + { + **_catalog_configuration("tts_japanese_dictionary", settings), + "active": tts_selected == "gpt_sovits" and dictionary_enabled, + "configured": not dictionary_enabled or dictionary_pack["installed"] or dictionary_runtime["ready"], + "status": dictionary_state, + "status_ok": dictionary_state in {"disabled", "ready", "installed"}, + "status_detail": (dictionary_pack.get("message") or dictionary_runtime["detail"]) + ( + " Pack version: " + dictionary_pack["version"] if dictionary_pack.get("version") else "" + ), + }, { **_catalog_configuration("tts_emotion_references", settings), diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py new file mode 100644 index 00000000..6ab49b91 --- /dev/null +++ b/tests/test_japanese_dictionary.py @@ -0,0 +1,397 @@ +from __future__ import annotations + +import hashlib +import json +import re +from pathlib import Path +from types import SimpleNamespace +import zipfile + +import pytest + +from config.asset_packages import AssetPackageError, external_asset_pack_status +from tools.audit_japanese_dictionary import audit_rows, native_mora_counter, native_surface_normalizer, normalize_rows +from tools.external_assets import build_bundle, install_bundle, verify_bundle +from tools.prepare_japanese_dictionary import prepare_dictionary +from tools.tts_text_processor import correct_pronunciation_for_tts +from tts import japanese_dictionary as dictionary + +ROOT = Path(__file__).resolve().parents[1] +INDEX = ROOT / "assets/index.json" +FOLLOWER = "follower,1348,1348,556,名詞,固有名詞,一般,*,*,*,フォロワーズ,フォロワーズ,フォロワーズ,0/5,*\n" +LEARNER = "learner,1348,1348,504,名詞,固有名詞,一般,*,*,*,ラーナーズ,ラーナーズ,ラーナーズ,1/5,*\n" + + +@pytest.fixture(autouse=True) +def clear_runtime_sources(monkeypatch): + monkeypatch.setattr(dictionary, "_runtime_status", { + "state": "not_loaded", "ready": False, "version": "", "detail": "Not loaded in this test.", + }) + dictionary._sources.cache_clear() + dictionary._digest.cache_clear() + yield + dictionary._sources.cache_clear() + dictionary._digest.cache_clear() + + +def _prepare(root: Path, *, native: bool = False) -> Path: + root.mkdir(parents=True, exist_ok=True) + source = root / "upstream.csv" + data = (FOLLOWER + LEARNER).encode() + if native: + # Preserve the real competing short words: a corrected full-word row + # must actually win native segmentation, not merely exist in the CSV. + for surface, reading, cost in [("comp", "コンプ", "344"), + ("on", "オン", "227"), ("ent", "エント", "287")]: + row = _row(surface, reading, "1/3" if surface != "on" else "1/2") + row[3] = cost + data += (",".join(row) + "\n").encode() + source.write_bytes(data) + recipe = json.loads((ROOT / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) + recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(list(dictionary.dictionary_rows(data)))) + recipe_path = root / "recipe.json" + recipe_path.write_text(json.dumps(recipe), encoding="utf-8") + return prepare_dictionary(source, project_root=root, recipe_path=recipe_path, + count_mora=None if native else lambda _: None) + + +@pytest.mark.parametrize("text,expected", [ + ("LeaderとMLPを確認する。", "Leaderとエムエルピーを確認する。"), + ("MLP2とGPT4o", "エムエルピー2とジーピーティー4オー"), + ("OpenAIのAPI", "オープンエーアイのエーピーアイ"), + ("CAPITAL", "CAPITAL"), + ("BilibiliとGitHub", "ビリビリとギットハブ"), + ("APIaryとHTTPServer", "APIaryとHTTPServer"), + ("mlpとMLPとAPI", "mlpとエムエルピーとエーピーアイ"), + ("openaiとOPENAIとBilibili", "オープンエーアイとオープンエーアイとビリビリ"), + ("APIaryとCAPITAL", "APIaryとCAPITAL"), + ("API's and APIs", "API's and APIs"), + ("今日は生ビールを飲む。", "今日は生ビールを飲む。"), +]) +def test_latin_words_and_initialisms_are_complete_tokens(text, expected): + assert correct_pronunciation_for_tts(text) == expected + + +def test_pipeline_applies_japanese_readings_only_to_japanese_speech(monkeypatch): + from tts import pipeline + original = "OpenAI API and MLP." + monkeypatch.setattr(pipeline, "TTS_OUTPUT_LANGUAGE", "英文") + assert pipeline._prepare_synthesis_text(original) == original + monkeypatch.setattr(pipeline, "TTS_OUTPUT_LANGUAGE", "日文") + assert pipeline._prepare_synthesis_text("MLPを確認する。") == "エムエルピーを確認する。" + + +def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): + pack_root = _prepare(tmp_path) + assert (tmp_path / "upstream.csv").read_text(encoding="utf-8") == FOLLOWER + LEARNER + pack = dictionary.load_dictionary_pack(pack_root) + corrections = list(dictionary.dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) + assert pack.entry_count == len(corrections) + readings = {row[0]: row[11] for row in dictionary.dictionary_rows((pack_root / "userdict.csv").read_bytes())} + assert readings == {row[0]: row[11] for row in corrections} + assert readings["follower"] == "フォロワー" + assert readings["learner"] == "ラーナー" + provenance = json.loads((pack_root / "provenance.json").read_text(encoding="utf-8")) + assert provenance["normalization"]["rules"] + assert provenance["normalization"]["results"]["output_entries"] == pack.entry_count + assert all(path.is_file() for path in pack.files()) + # A maintainer cannot accidentally publish a customized installation as the + # pinned upstream base, even if it has the expected CSV shape. + with pytest.raises(dictionary.JapaneseDictionaryError, match="differs from the pinned"): + prepare_dictionary(tmp_path / "upstream.csv", project_root=tmp_path / "rejected") + assert not (tmp_path / "rejected/assets").exists() + + +def test_optional_bundle_round_trip_preserves_personal_and_legacy_sources(tmp_path): + publisher = tmp_path / "publisher" + pack_root = _prepare(publisher) + (pack_root / "user.dict").write_bytes(b"derived cache must not ship") + recipient = tmp_path / "recipient" + personal = recipient / "assets/text/ja/user.csv" + personal.parent.mkdir(parents=True) + personal.write_text(FOLLOWER, encoding="utf-8") + legacy = recipient / "GPT_SoVITS/text/ja_userdic/userdict.csv" + legacy.parent.mkdir(parents=True) + legacy.write_text(LEARNER, encoding="utf-8") + assert external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "not_installed" + archive = tmp_path / "dictionary.zip" + manifest = build_bundle(project_root=publisher, output=archive, pack_ids=[dictionary.PACK_ID], index_path=INDEX) + assert manifest["file_count"] == len(dictionary.PACK_FILES) + with zipfile.ZipFile(archive) as bundle: + assert not any(name.endswith("user.dict") or name.endswith("user.csv") for name in bundle.namelist()) + assert verify_bundle(archive, index_path=INDEX) == manifest + result = install_bundle(archive, project_root=recipient, index_path=INDEX) + assert result["installed_files"] == len(dictionary.PACK_FILES) + assert install_bundle(archive, project_root=recipient, index_path=INDEX)["installed_files"] == 0 + status = external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX) + assert status["installed"] and status["version"] == dictionary.load_dictionary_pack(pack_root).version + assert personal.read_text(encoding="utf-8") == FOLLOWER + assert legacy.read_text(encoding="utf-8") == LEARNER + # Existing different data is refused before any recipient file changes. + installed_csv = recipient / "assets" / dictionary.PACK_TREE / "userdict.csv" + installed_csv.write_bytes(b"private edit") + with pytest.raises(AssetPackageError, match="different|exists|conflict"): + install_bundle(archive, project_root=recipient, index_path=INDEX) + assert installed_csv.read_bytes() == b"private edit" + assert external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "invalid" + + +@pytest.mark.parametrize("bad", [b"", b"not,an,entry\n", b"\xff"]) +def test_dictionary_contract_rejects_invalid_csv(bad): + with pytest.raises(dictionary.JapaneseDictionaryError): + list(dictionary.dictionary_rows(bad)) + + +def _row(surface, pronunciation, accent="0/4"): + row = next(dictionary.dictionary_rows(FOLLOWER.encode())) + row[0], row[13] = surface, accent + row[10:13] = [pronunciation] * 3 + return row + + +def test_normalization_repairs_representation_and_preserves_distinct_readings(): + decomposed = _row("abzug", "アブツク\u3099", "2/5") + rows = [decomposed, list(decomposed), _row("read", "リード", "1/3"), + _row("read", "レッド", "1/3"), _row("path", "パス", "1/2"), + _row("paths", "パス", "1/2"), _row("guangjo", "グァンジョ", "4/4"), + _row("bad", "アサルトed", "0/5")] + morae = {"アブツグ": 4, "リード": 3, "レッド": 3, "パス": 2, "グァンジョ": 3} + cleaned, report = normalize_rows(rows, morae.__getitem__) + assert cleaned[0][10:14] == ["アブツグ"] * 3 + ["2/4"] + assert {row[12] for row in cleaned if row[0] == "read"} == {"リード", "レッド"} + assert [row[0] for row in cleaned if row[12] == "パス"] == ["path", "paths"] + assert next(row for row in cleaned if row[0] == "guangjo")[13] == "4/4" + assert next(row for row in cleaned if row[0] == "bad")[12] == "アサルトed" + assert report["exact_duplicates_removed"] == 1 + assert report["unicode_normalized_rows"] == 2 + assert {entry["reason"] for entry in report["unresolved"]} == { + "accent_needs_review", "non_kana_reading_needs_review", + } + # Normalizing an already repaired source is stable. + assert normalize_rows(cleaned, morae.__getitem__)[0] == cleaned + + +def test_normalization_repairs_only_literal_whole_token_initialism_readings(): + rows = [_row("ceo", "CEO", "0/3"), _row("else", "CEO", "0/3"), + _row("apiary", "アピアリー", "0/5"), _row("UNRESOLVED", "UNRESOLVED", "0/10")] + cleaned, report = normalize_rows(rows, lambda _: 6) + assert cleaned[0][10:14] == ["シーイーオー"] * 3 + ["0/6"] + assert cleaned[1][12] == "CEO" and cleaned[2][12] == "アピアリー" + assert cleaned[3][12] == "UNRESOLVED" + assert report["literal_initialism_readings_repaired"] == 1 + + +def test_reading_separators_are_removed_without_discarding_lexical_residue_or_guessing_accents(): + rows = [_row("coca-cola", "コカ・コーラ", "4/6"), + _row("speaking", "スピー\u200b\u200bキング", "0/8"), + _row("jean-luc", "ジャン=リュック", "4/6"), + _row("unknown", "アイ・ウ", "4/4"), + _row("named", "スイス(スィス)", "5/7"), + _row("untranslated", "ショックした", "0/5"), + _row("possessive", "モリス'sズ", "4/6")] + counts = {"コカコーラ": 5, "スピーキング": 6, "ジャンリュック": 5, "アイウ": 3} + cleaned, report = normalize_rows(rows, counts.__getitem__) + assert [(row[12], row[13]) for row in cleaned[:3]] == [ + ("コカコーラ", "4/5"), ("スピーキング", "0/6"), ("ジャンリュック", "4/5"), + ] + assert cleaned[3:] == rows[3:], "keep uncertain accents, translations and spelling residues unchanged" + assert report["reading_separators_normalized"] == 3 + assert normalize_rows(cleaned, counts.__getitem__)[0] == cleaned + + +def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surface(tmp_path): + recipe = json.loads((ROOT / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) + repair = next(entry for entry in recipe["corrections"] if entry["word"] == "corps") + bad = _row("corps", repair["before"], "18/75") + legitimate_plural = _row("corps", "コアズ", "1/3") + source = tmp_path / "upstream.csv" + # csv.writer handles the commas and quotes inside the original LLM annotation. + import csv + import io + buffer = io.StringIO(newline="") + csv.writer(buffer, lineterminator="\n").writerows([bad, legitimate_plural]) + data = buffer.getvalue().encode() + source.write_bytes(data) + recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=2) + recipe_path = tmp_path / "recipe.json" + recipe_path.write_text(json.dumps(recipe), encoding="utf-8") + pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, count_mora=lambda _: None) + readings = [row[12] for row in dictionary.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0] == "corps"] + assert set(readings) == {"コア", "コアズ"} + assert source.read_bytes() == data + + +def test_inflection_audit_is_evidence_for_review_and_does_not_guess_lexical_repairs(tmp_path): + cmu = tmp_path / "cmu.rep" + cmu.write_text("PATH P AE1 TH\nPATHS P AE1 TH S\nBANK B AE1 NG K\nBANKS B AE1 NG K S\n", encoding="utf-8") + rows = [_row("path", "パス", "1/2"), _row("paths", "パス", "1/2"), + _row("bank", "バンクス", "1/4"), _row("banks", "バンクス", "1/4")] + report = audit_rows(rows, cmu, lambda _: None) + assert {entry["surface"] for entry in report["inflection_candidates"]} == {"path", "bank"} + assert normalize_rows(rows, lambda _: None)[0] == rows + + +def test_native_mora_counter_uses_pronunciation_without_retokenizing_kana(): + pytest.importorskip("pyopenjtalk") + count, _ = native_mora_counter() + for pronunciation, expected in {"アボリッシュ": 5, "アファブル": 4, "アドゥレイト": 5, + "アッバティエロ": 6, "アグリフュエル": 6, "エィ": 2, + "フォロワー": 4, "グァンジョ": 3, "カンナ": 3}.items(): + assert count(pronunciation) == expected + + +def test_native_surface_spelling_is_applied_only_to_repaired_readings(): + pytest.importorskip("pyopenjtalk") + count, _ = native_mora_counter() + surface = native_surface_normalizer() + rows = [_row("coca-cola", "コカ・コーラ", "4/6"), + _row("unknown's", "モリス'sズ", "4/6"), + _row("already-valid", "テスト", "0/3")] + cleaned, report = normalize_rows(rows, count, surface) + assert cleaned[0][0] == "coca−cola" and cleaned[0][12] == "コカコーラ" + assert cleaned[1:] == rows[1:] + assert report["repaired_surfaces_normalized"] == 1 + + +def _fake_jtalk(root: Path): + base = root / "system" + base.mkdir(parents=True) + (base / "sys.dic").write_bytes(b"system dictionary") + compiled, registered = [], [] + def compile_csv(source, target): + compiled.append(source) + Path(target).write_bytes(b"compiled " + Path(source).read_bytes()) + return SimpleNamespace( + OPEN_JTALK_DICT_DIR=str(base).encode(), __version__="test-1", + mecab_dict_index=compile_csv, unset_user_dict=lambda: registered.append([]), + update_global_jtalk_with_user_dict=lambda paths: registered.append(paths), + compiled=compiled, registered=registered, + ) + + +def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_path): + root = tmp_path / "日語" + pack_root = _prepare(root) + legacy = root / "GPT_SoVITS/text/ja_userdic/userdict.csv" + legacy.parent.mkdir(parents=True) + legacy.write_text(FOLLOWER, encoding="utf-8") + personal = root / "assets/text/ja/user.csv" + personal.write_text(LEARNER, encoding="utf-8") + jtalk = _fake_jtalk(root) + status = dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) + assert status["ready"] + assert jtalk.compiled == [str(pack_root / "userdict.csv"), str(personal)] + assert all(Path(path).is_relative_to(root / ".cache") for path in jtalk.registered[-1]) + assert legacy.read_text(encoding="utf-8") == FOLLOWER + assert not (pack_root / "user.dict").exists() + dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) + assert len(jtalk.compiled) == 2, "unchanged sources and runtime must reuse compiled files" + jtalk.__version__ = "test-2" + dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) + assert len(jtalk.compiled) == 4, "a changed native runtime must rebuild its cache" + dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=False) + assert dictionary.dictionary_runtime_status()["state"] == "disabled" and jtalk.registered[-1] == [] + + +def test_runtime_missing_invalid_and_compile_failure_keep_base_frontend(tmp_path): + jtalk = _fake_jtalk(tmp_path) + assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "not_installed" + dictionary._sources.cache_clear() + pack_root = _prepare(tmp_path) + def fail_compile(*args): + raise RuntimeError("compiler failed") + jtalk.mecab_dict_index = fail_compile + assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "invalid" + assert jtalk.registered[-1] == [] + assert not list((tmp_path / ".cache/japanese_dictionary").glob("*.dict")) + (pack_root / "userdict.csv").write_bytes(b"corrupt") + dictionary._sources.cache_clear() + assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "invalid" + + +def test_audio_cache_distinguishes_dictionary_selection_and_english_is_independent(tmp_path, monkeypatch): + from tts import first_sentence_audio_cache as audio_cache, pipeline + monkeypatch.setattr(dictionary, "PROJECT_ROOT", tmp_path) + enabled = [True] + monkeypatch.setattr(dictionary, "_enabled", lambda: enabled[0]) + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") + cache = audio_cache.FirstSentenceAudioCache(tmp_path / "audio") + without = cache.path_for("Follower。", {}) + _prepare(tmp_path) + dictionary._sources.cache_clear() + installed = cache.path_for("Follower。", {}) + assert installed != without + enabled[0] = False + assert cache.path_for("Follower。", {}) not in {without, installed} + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "en") + assert "japanese_dictionary" not in cache.key_payload("Follower.", {}) + + +@pytest.mark.parametrize("runtime_state,installed,enabled,expected", [ + ("invalid", True, True, "invalid"), + ("not_loaded", False, True, "invalid"), + ("invalid", True, False, "disabled"), +]) +def test_settings_expose_dictionary_failures_and_the_disable_switch(monkeypatch, runtime_state, installed, enabled, expected): + from config import asset_packages, settings + from server.handlers.system_handler import _voice_configuration + original = asset_packages.external_asset_pack_status + monkeypatch.setattr(asset_packages, "external_asset_pack_status", lambda pack_id, **kwargs: { + "installed": installed, "state": "installed" if installed else "invalid", + "version": "test", "message": "" if installed else "CSV checksum failed", + } if pack_id == dictionary.PACK_ID else original(pack_id, **kwargs)) + monkeypatch.setattr(settings, "ENABLE_JAPANESE_USER_DICTIONARY", enabled) + monkeypatch.setattr(dictionary, "_runtime_status", { + "state": runtime_state, "ready": False, "version": "test", "detail": "Dictionary compilation failed", + }) + group = next(item for item in _voice_configuration(settings) if item["id"] == "tts_japanese_dictionary") + assert group["status"] == expected + assert group["status_ok"] is (not enabled) + if enabled: + assert "failed" in group["status_detail"] + + +def test_native_runtime_reads_corrected_words_and_keeps_japanese_intact(tmp_path): + jtalk = pytest.importorskip("pyopenjtalk") + root = tmp_path / "日本語" + _prepare(root, native=True) + original_source = (root / "assets" / dictionary.PACK_TREE / "userdict.csv").read_bytes() + try: + jtalk.unset_user_dict() + japanese = jtalk.g2p("今日は生ビールを飲みます。") + assert dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True)["ready"] + for word, expected in {"Follower": "フォロワー", "Learner": "ラーナー", "Proposer": "プロポーザー", + "Bank": "バンク", "Component": "コンポーネント", "Components": "コンポーネンツ", + "Handler": "ハンドラー", "Dentist": "デンティスト", "Balloonist": "バルーニスト", + "Ninth": "ナインス", "Intruder": "イントルーダー", "Lawrence": "ローレンス"}.items(): + features = jtalk.run_frontend(word.lower()) + assert len(features) == 1 and features[0]["read"] == expected + prepared = correct_pronunciation_for_tts("LeaderとMLPを確認する。") + assert "エムエルピー" in "".join(feature["read"] for feature in jtalk.run_frontend(prepared.lower())) + assert jtalk.g2p("今日は生ビールを飲みます。") == japanese + assert (root / "assets" / dictionary.PACK_TREE / "userdict.csv").read_bytes() == original_source + finally: + jtalk.unset_user_dict() + + +def test_japanese_frontend_preserves_latin_compounds_and_ordinary_punctuation(tmp_path, monkeypatch): + jtalk = pytest.importorskip("pyopenjtalk") + monkeypatch.syspath_prepend(str(ROOT / "GPT_SoVITS")) + from text import japanese + root = tmp_path / "日本語" + _prepare(root, native=True) + try: + assert dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True)["ready"] + count, _ = native_mora_counter() + template = jtalk.run_frontend("テスト")[0] + for word, kana in {"ballet's": "バレエズ", "coca-cola": "コカコーラ", + "t-shirt": "ティーシャツ", "economists'": "エコノミスツ"}.items(): + labels = jtalk.make_label([{**template, "read": kana, "pron": kana, "acc": 0, "mora_size": count(kana)}]) + expected = [re.search(r"-(.*?)\+", label).group(1) for label in labels[1:-1]] + actual = japanese.preprocess_jap(word) + assert [phone.lower() if phone in "AIUEO" else phone for phone in actual] == expected + assert japanese._japanese_marks.findall("cat - dog") == [" ", "-", " "] + assert japanese._japanese_marks.findall("猫-犬。") == ["-", "。"] + assert japanese._japanese_marks.findall("'hello'")[0] == "'" + finally: + jtalk.unset_user_dict() diff --git a/tests/test_release_tooling.py b/tests/test_release_tooling.py index 1875ba41..60954f83 100644 --- a/tests/test_release_tooling.py +++ b/tests/test_release_tooling.py @@ -348,6 +348,16 @@ def test_gpt_sovits_release_boundary_excludes_optional_or_unverified_material() ] +def test_optional_japanese_dictionary_sources_and_notices_have_separate_release_boundaries() -> None: + policy = json.loads((ROOT / "release/source_release_policy.json").read_text(encoding="utf-8")) + sources = ["assets/text/ja/english/userdict.csv", "assets/text/ja/user.csv"] + code_and_notices = ["tools/data/japanese_dictionary_corrections.csv", "tools/audit_japanese_dictionary.py", + "LICENSES/JAPANESE-DICTIONARY.md", "docs/japanese_pronunciation_dictionary.md"] + selected, excluded = select_paths([*sources, *code_and_notices], policy) + assert excluded == sources + assert selected == sorted(code_and_notices) + + def test_gpt_sovits_provenance_records_immediate_upstream_reliance() -> None: manifest = json.loads((ROOT / "LICENSES" / "provenance.json").read_text(encoding="utf-8")) components = {component["id"]: component for component in manifest["components"]} diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py new file mode 100644 index 00000000..76361855 --- /dev/null +++ b/tools/audit_japanese_dictionary.py @@ -0,0 +1,180 @@ +"""Audit dictionary data without guessing or rewriting ambiguous word readings.""" +from __future__ import annotations + +import argparse +from collections import Counter, defaultdict +from functools import lru_cache +import hashlib +from importlib.metadata import version +import json +from pathlib import Path +import re +import sys +from typing import Callable +import unicodedata + +PROJECT_ROOT = Path(__file__).resolve().parents[1] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from tools.tts_text_processor import convert_english_abbreviations_to_katakana # noqa: E402 +from tts.japanese_dictionary import dictionary_rows # noqa: E402 + +_KANA = re.compile(r"[\u30a1-\u30fa\u30fc]+") +_MORA_PHONES = frozenset({"a", "i", "u", "e", "o", "A", "I", "U", "E", "O", "N", "cl"}) + + +def _without_reading_separators(value: str) -> str: + # These delimit written name components; they are not OpenJTalk morae. + # Apply only when every remaining character is already Katakana. Never + # discard letters, kanji, annotations or untranslated inflection endings. + joined = "".join(char for char in value + if not char.isspace() and unicodedata.category(char) != "Cf" and char not in "・=-'") + return joined if _KANA.fullmatch(joined) else value + + +def native_mora_counter() -> tuple[Callable[[str], int], str]: + import pyopenjtalk + + template = pyopenjtalk.run_frontend("テスト", use_sudachi_kanji_yomi=False, predict_nani=False)[0] + + @lru_cache(maxsize=None) + def count(pronunciation: str) -> int: + # Feed one explicit pronunciation to the native label builder. Running + # g2p(kana) would tokenize it again and can split シュ into シ + ュ, + # falsely counting two morae. Lowercase n is a consonant; only N is a mora. + feature = {**template, "read": pronunciation, "pron": pronunciation, "acc": 0, "mora_size": 1} + labels = pyopenjtalk.make_label([feature]) + phones = (re.search(r"-(.*?)\+", label).group(1) for label in labels) + return sum(phone in _MORA_PHONES for phone in phones) + + return count, version("pyopenjtalk-plus") + + +def native_surface_normalizer() -> Callable[[str], str]: + import pyopenjtalk + + jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) + return jtalk.normalize_for_mecab + + +def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None], + normalize_surface: Callable[[str], str] | None = None) -> tuple[list[list[str]], dict]: + """Apply representation repairs; preserve lexical alternatives and accent nuclei.""" + cleaned, seen = [], set() + stats = {"unicode_normalized_rows": 0, "literal_initialism_readings_repaired": 0, + "reading_separators_normalized": 0, "repaired_surfaces_normalized": 0, + "mora_counts_repaired": 0, "exact_duplicates_removed": 0} + unresolved = [] + for number, original in enumerate(rows, 1): + row = list(original) + separator_repaired = False + row[10:13] = [unicodedata.normalize("NFC", value) for value in row[10:13]] + stats["unicode_normalized_rows"] += row != original + joined = [_without_reading_separators(value) for value in row[10:13]] + if joined != row[10:13] and _KANA.fullmatch(joined[2]): + actual = count_mora(joined[2]) + accent = int(row[13].split("/")[0]) + if actual is None or 0 < actual and accent <= actual: + row[10:13] = joined + separator_repaired = True + stats["reading_separators_normalized"] += 1 + literal = unicodedata.normalize("NFKC", row[12]) + if (re.fullmatch(r"[A-Z]{2,}", literal) + and literal.casefold() == unicodedata.normalize("NFKC", row[0]).casefold()): + # An untranslated initialism is data, not a new word-specific rule. + translated = convert_english_abbreviations_to_katakana(literal) + if _KANA.fullmatch(translated): + row[10:13] = [translated] * 3 + stats["literal_initialism_readings_repaired"] += 1 + if _KANA.fullmatch(row[12]): + actual = count_mora(row[12]) + accent, declared = map(int, row[13].split("/")) + valid_reading = actual is None or 0 < actual and accent <= actual + if actual is not None and actual != declared: + if 0 < actual and accent <= actual: + row[13] = f"{accent}/{actual}" + stats["mora_counts_repaired"] += 1 + else: + unresolved.append({"line": number, "surface": row[0], "pronunciation": row[12], + "reason": "accent_needs_review", "declared": row[13], "native_mora": actual}) + else: + valid_reading = False + unresolved.append({"line": number, "surface": row[0], "pronunciation": row[12], + "reason": "non_kana_reading_needs_review"}) + # Activate a repaired reading using the native input spelling (e.g. ’ + # and −). Leave unreviewed, unchanged entries exactly as they were. + if normalize_surface is not None and valid_reading and separator_repaired: + surface = normalize_surface(row[0]) + stats["repaired_surfaces_normalized"] += surface != row[0] + row[0] = surface + entry = tuple(row) + if entry in seen: + stats["exact_duplicates_removed"] += 1 + else: + seen.add(entry) + cleaned.append(row) + return cleaned, {**stats, "input_entries": len(rows), "output_entries": len(cleaned), + "unresolved_entries": len(unresolved), "unresolved": unresolved} + + +def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], int | None], + normalize_surface: Callable[[str], str] | None = None) -> dict: + words = defaultdict(list) + for number, row in enumerate(rows, 1): + key = unicodedata.normalize("NFKC", row[0]).translate(str.maketrans("’−", "'-")).casefold() + words[key].append((number, row)) + phones = defaultdict(set) + cmu_data = Path(cmudict).read_bytes() + for line in cmu_data.decode("utf-8").splitlines(): + fields = line.split() + if fields: + phones[fields[0].split("(", 1)[0].casefold()].add(tuple(fields[1:])) + multiple = [{"surface": word, "readings": sorted({row[12] for _, row in entries}), + "lines": [line for line, _ in entries]} + for word, entries in words.items() if len({row[12] for _, row in entries}) > 1] + candidates = [] + for word, entries in words.items(): + for suffix in ("s", "'s"): + plural = word + suffix + pairs = [(base, form) for base in phones.get(word, ()) for form in phones.get(plural, ()) + if form in {base + ("S",), base + ("Z",)}] + if not pairs: + continue + for line, row in entries: + matches = [n for n, other in words.get(plural, ()) if row[12] == other[12]] + if matches: + candidates.append({"surface": word, "line": line, "reading": row[12], + "inflected_surface": plural, "inflected_lines": matches, + "cmu_phones": [list(pair) for pair in pairs[0]], + "disposition": "review_only; Japanese loanword forms may legitimately coincide"}) + _, normalization = normalize_rows(rows, count_mora, normalize_surface) + duplicates = Counter(tuple(row) for row in rows) + return {"entries": len(rows), "distinct_surfaces": len(words), + "cmudict_sha256": hashlib.sha256(cmu_data).hexdigest(), + "exact_duplicate_rows": sum(count - 1 for count in duplicates.values()), + "multiple_reading_surfaces": len(multiple), "multiple_readings": multiple, + "inflection_candidate_count": len(candidates), "inflection_candidates": candidates, + "normalization": normalization, + "limitations": "Candidates do not prove lexical errors. Legitimate heteronyms and loanword inflections are retained. No LLM, network, waveform synthesis or semantic classifier is used."} + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True) + parser.add_argument("--cmudict", type=Path, default=PROJECT_ROOT / "GPT_SoVITS/text/cmudict-fast.rep") + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + count, version = native_mora_counter() + source_data = args.source.read_bytes() + report = audit_rows(list(dictionary_rows(source_data)), args.cmudict, count, native_surface_normalizer()) + report["source_sha256"] = hashlib.sha256(source_data).hexdigest() + report["native_runtime"] = version + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps({key: report[key] for key in ("entries", "exact_duplicate_rows", "multiple_reading_surfaces", "inflection_candidate_count", "native_runtime")})) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/data/japanese_dictionary_corrections.csv b/tools/data/japanese_dictionary_corrections.csv new file mode 100644 index 00000000..9f59c18b --- /dev/null +++ b/tools/data/japanese_dictionary_corrections.csv @@ -0,0 +1,172 @@ +follower,1348,1348,556,名詞,固有名詞,一般,*,*,*,フォロワー,フォロワー,フォロワー,0/4,* +learner,1348,1348,504,名詞,固有名詞,一般,*,*,*,ラーナー,ラーナー,ラーナー,1/4,* +proposer,1348,1348,504,名詞,固有名詞,一般,*,*,*,プロポーザー,プロポーザー,プロポーザー,0/6,* +abstraction,1348,1348,711,名詞,固有名詞,一般,*,*,*,アブストラクション,アブストラクション,アブストラクション,5/8,* +acronym,1348,1348,504,名詞,固有名詞,一般,*,*,*,アクロニム,アクロニム,アクロニム,4/5,* +adapter,1348,1348,504,名詞,固有名詞,一般,*,*,*,アダプター,アダプター,アダプター,0/5,* +airline,1348,1348,504,名詞,固有名詞,一般,*,*,*,エアライン,エアライン,エアライン,3/5,* +applicator,1348,1348,659,名詞,固有名詞,一般,*,*,*,アプリケーター,アプリケーター,アプリケーター,4/7,* +bank,1348,1348,344,名詞,固有名詞,一般,*,*,*,バンク,バンク,バンク,1/3,* +component,1348,1348,0,名詞,固有名詞,一般,*,*,*,コンポーネント,コンポーネント,コンポーネント,3/7,* +components,1348,1348,0,名詞,固有名詞,一般,*,*,*,コンポーネンツ,コンポーネンツ,コンポーネンツ,0/7,* +developer,1348,1348,608,名詞,固有名詞,一般,*,*,*,デベロッパー,デベロッパー,デベロッパー,3/6,* +handler,1348,1348,504,名詞,固有名詞,一般,*,*,*,ハンドラー,ハンドラー,ハンドラー,1/5,* +adhd,1348,1348,344,名詞,固有名詞,一般,*,*,*,エーディーエイチディー,エーディーエイチディー,エーディーエイチディー,8/9,* +allergist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,アレルジストス,アレルジストス,アレルジストス,7/7,* +amd,1348,1348,287,名詞,固有名詞,一般,*,*,*,エーエムディー,エーエムディー,エーエムディー,5/6,* +bahrainian,1348,1348,659,名詞,固有名詞,一般,*,*,*,バーレイニアン,バーレイニアン,バーレイニアン,3/7,* +ballet's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,バレエズ,バレエズ,バレエズ,1/4,* +balloonist,1348,1348,659,名詞,固有名詞,一般,*,*,*,バルーニスト,バルーニスト,バルーニスト,4/6,* +barbecued,1348,1348,608,名詞,固有名詞,一般,*,*,*,バーベキュード,バーベキュード,バーベキュード,4/6,* +business's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ビジネシズ,ビジネシズ,ビジネシズ,5/5,* +busloads,1348,1348,556,名詞,固有名詞,一般,*,*,*,バスローズ,バスローズ,バスローズ,0/5,* +busts,1348,1348,398,名詞,固有名詞,一般,*,*,*,バストス,バストス,バストス,0/4,* +cajoling,1348,1348,556,名詞,固有名詞,一般,*,*,*,カジョーリング,カジョーリング,カジョーリング,2/6,* +cds,1348,1348,287,名詞,固有名詞,一般,*,*,*,シーディーズ,シーディーズ,シーディーズ,1/5,* +chamonix,1348,1348,556,名詞,固有名詞,一般,*,*,*,シャモニー,シャモニー,シャモニー,0/4,* +christ's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,クライストス,クライストス,クライストス,6/6,* +circuit's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,サーキッツ,サーキッツ,サーキッツ,3/5,* +condo,1348,1348,278,名詞,固有名詞,一般,*,*,*,コンド,コンド,コンド,1/3,* +corps,1348,1348,398,名詞,固有名詞,一般,*,*,*,コア,コア,コア,1/2,* +credit's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,クレジッツ,クレジッツ,クレジッツ,0/5,* +cricket's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,クリケッツ,クリケッツ,クリケッツ,3/5,* +criminalist's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,クリミナリスツ,クリミナリスツ,クリミナリスツ,5/7,* +debarment,1348,1348,608,名詞,固有名詞,一般,*,*,*,ディバーメント,ディバーメント,ディバーメント,5/6,* +debate's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ディベーツ,ディベーツ,ディベーツ,2/4,* +dentist,1348,1348,504,名詞,固有名詞,一般,*,*,*,デンティスト,デンティスト,デンティスト,3/5,* +documented,1348,1348,659,名詞,固有名詞,一般,*,*,*,ドキュメンテッド,ドキュメンテッド,ドキュメンテッド,5/7,* +dolomite's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ドロマイツ,ドロマイツ,ドロマイツ,3/5,* +economist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* +economists',1348,1348,-2221,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* +embassy's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,エンバシーズ,エンバシーズ,エンバシーズ,6/6,* +endocrinologist's,1348,1348,-845,名詞,固有名詞,一般,*,*,*,エンドクリノロジスツ,エンドクリノロジスツ,エンドクリノロジスツ,8/10,* +ernest's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,アーネスツ,アーネスツ,アーネスツ,0/5,* +everest's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,エヴェレストス,エヴェレストス,エヴェレストス,6/6,* +experienced,1348,1348,711,名詞,固有名詞,一般,*,*,*,エクスペリエンスト,エクスペリエンスト,エクスペリエンスト,6/9,* +faraday's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ファラデーズ,ファラデーズ,ファラデーズ,5/5,* +fluoresce,1348,1348,608,名詞,固有名詞,一般,*,*,*,フローレス,フローレス,フローレス,2/5,* +gadhafi's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,カダフィズ,カダフィズ,カダフィズ,0/4,* +grant's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,グランツ,グランツ,グランツ,2/4,* +hermetically,1348,1348,762,名詞,固有名詞,一般,*,*,*,ハーメチックリー,ハーメチックリー,ハーメチックリー,4/8,* +hgh,1348,1348,287,名詞,固有名詞,一般,*,*,*,エイチジーエイチ,エイチジーエイチ,エイチジーエイチ,0/8,* +hirohito's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ヒロヒトズ,ヒロヒトズ,ヒロヒトズ,3/5,* +hoechst's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ヘキスツ,ヘキスツ,ヘキスツ,0/4,* +hospice's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ホスピシズ,ホスピシズ,ホスピシズ,5/5,* +illusionist's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,イリュージョニスツ,イリュージョニスツ,イリュージョニスツ,5/7,* +intruder,1348,1348,556,名詞,固有名詞,一般,*,*,*,イントルーダー,イントルーダー,イントルーダー,4/7,* +intrusive,1348,1348,608,名詞,固有名詞,一般,*,*,*,イントルーシヴ,イントルーシヴ,イントルーシヴ,1/7,* +intrusiveness,1348,1348,813,名詞,固有名詞,一般,*,*,*,イントルーシヴネス,イントルーシヴネス,イントルーシヴネス,0/9,* +iodide's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,アイオダイズ,アイオダイズ,アイオダイズ,4/6,* +jfet,1348,1348,344,名詞,固有名詞,一般,*,*,*,ジェイフェット,ジェイフェット,ジェイフェット,0/5,* +journalist's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,ジャーナリスツ,ジャーナリスツ,ジャーナリスツ,4/6,* +lawrence,1348,1348,556,名詞,固有名詞,一般,*,*,*,ローレンス,ローレンス,ローレンス,1/5,* +lieutenant's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,リューテナンツ,リューテナンツ,リューテナンツ,4/6,* +luxottica's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ルクソティカズ,ルクソティカズ,ルクソティカズ,0/6,* +marlboro's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,マールボロズ,マールボロズ,マールボロズ,4/6,* +marriott's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,マリオットス,マリオットス,マリオットス,6/6,* +matsushita's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,マツシタズ,マツシタズ,マツシタズ,5/5,* +medalist's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,メダリスツ,メダリスツ,メダリスツ,3/5,* +meteorologist,1348,1348,813,名詞,固有名詞,一般,*,*,*,メテオロジスト,メテオロジスト,メテオロジスト,0/7,* +meteorologist's,1348,1348,-946,名詞,固有名詞,一般,*,*,*,メテオロジスツ,メテオロジスツ,メテオロジスツ,5/7,* +mississippi's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,ミシシッピーズ,ミシシッピーズ,ミシシッピーズ,0/7,* +mormonism,1348,1348,608,名詞,固有名詞,一般,*,*,*,モルモニズム,モルモニズム,モルモニズム,4/6,* +narrates,1348,1348,556,名詞,固有名詞,一般,*,*,*,ナレイツ,ナレイツ,ナレイツ,2/4,* +nike's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,ナイキズ,ナイキズ,ナイキズ,1/4,* +ninth,1348,1348,398,名詞,固有名詞,一般,*,*,*,ナインス,ナインス,ナインス,1/4,* +norway's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ノルウェーズ,ノルウェーズ,ノルウェーズ,5/5,* +office's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,オフィシズ,オフィシズ,オフィシズ,4/4,* +personalizes,1348,1348,762,名詞,固有名詞,一般,*,*,*,パーソナライズィズ,パーソナライズィズ,パーソナライズィズ,0/8,* +pinnochio,1348,1348,608,名詞,固有名詞,一般,*,*,*,ピノキオ,ピノキオ,ピノキオ,0/4,* +pinnochio's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ピノキオズ,ピノキオズ,ピノキオズ,5/5,* +politz,1348,1348,451,名詞,固有名詞,一般,*,*,*,ポリツ,ポリツ,ポリツ,0/3,* +poppy's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,ポッピーズ,ポッピーズ,ポッピーズ,5/5,* +post's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,ポスツ,ポスツ,ポスツ,1/3,* +purchaser,1348,1348,608,名詞,固有名詞,一般,*,*,*,パーチェイサー,パーチェイサー,パーチェイサー,3/6,* +sake's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,セイクス,セイクス,セイクス,1/4,* +sarajevo's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,サラエボズ,サラエボズ,サラエボズ,5/5,* +schubert's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,シューベルトス,シューベルトス,シューベルトス,6/6,* +scientist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,サイエンティスツ,サイエンティスツ,サイエンティスツ,5/7,* +scout's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,スカウツ,スカウツ,スカウツ,2/4,* +shevardnadze's,1348,1348,-997,名詞,固有名詞,一般,*,*,*,シェヴァルドナーゼズ,シェヴァルドナーゼズ,シェヴァルドナーゼズ,0/8,* +sizes,1348,1348,398,名詞,固有名詞,一般,*,*,*,サイズィズ,サイズィズ,サイズィズ,0/4,* +smith's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,スミス,スミス,スミス,1/3,* +soviet's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ソビエツ,ソビエツ,ソビエツ,0/4,* +stowaway,1348,1348,556,名詞,固有名詞,一般,*,*,*,ストウアウェイ,ストウアウェイ,ストウアウェイ,3/6,* +superintendent's,1348,1348,-896,名詞,固有名詞,一般,*,*,*,スーパーリンテンデンツ,スーパーリンテンデンツ,スーパーリンテンデンツ,7/11,* +suvs,1348,1348,344,名詞,固有名詞,一般,*,*,*,エスユーブイズ,エスユーブイズ,エスユーブイズ,2/7,* +swords,1348,1348,451,名詞,固有名詞,一般,*,*,*,ソーズ,ソーズ,ソーズ,1/3,* +synergy's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,シナジーズ,シナジーズ,シナジーズ,5/5,* +t-shirt,1348,1348,91,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* +t-shirts,1348,1348,144,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* +thrusts,1348,1348,504,名詞,固有名詞,一般,*,*,*,スラストス,スラストス,スラストス,5/5,* +tlc,1348,1348,287,名詞,固有名詞,一般,*,*,*,ティーエルシー,ティーエルシー,ティーエルシー,0/6,* +unfocused,1348,1348,608,名詞,固有名詞,一般,*,*,*,アンフォーカスト,アンフォーカスト,アンフォーカスト,0/7,* +untruthful,1348,1348,659,名詞,固有名詞,一般,*,*,*,アントゥルースフル,アントゥルースフル,アントゥルースフル,0/8,* +voldemort's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ヴォルデモートス,ヴォルデモートス,ヴォルデモートス,7/7,* +wurtzel,1348,1348,504,名詞,固有名詞,一般,*,*,*,ワーツェル,ワーツェル,ワーツェル,0/4,* +yomiuri,1348,1348,504,名詞,固有名詞,一般,*,*,*,ヨミウリ,ヨミウリ,ヨミウリ,0/4,* +adl-tabataba,1348,1348,762,名詞,固有名詞,一般,*,*,*,アドルタバタバ,アドルタバタバ,アドルタバタバ,0/7,* +adl-tabatabai,1348,1348,813,名詞,固有名詞,一般,*,*,*,アドルタバタバイ,アドルタバタバイ,アドルタバタバイ,0/8,* +aix-la-chapelle,1348,1348,141,名詞,固有名詞,一般,*,*,*,エクスラシャペル,エクスラシャペル,エクスラシャペル,7/7,* +anglo-catholicism,1348,1348,644,名詞,固有名詞,一般,*,*,*,アングロカトリシズム,アングロカトリシズム,アングロカトリシズム,9/10,* +bar-mitzvah,1348,1348,326,名詞,固有名詞,一般,*,*,*,バーミツバー,バーミツバー,バーミツバー,0/6,* +bas-reliefs,1348,1348,631,名詞,固有名詞,一般,*,*,*,バスリリーフス,バスリリーフス,バスリリーフス,5/7,* +broken-tooth,1348,1348,384,名詞,固有名詞,一般,*,*,*,ブローケントゥース,ブローケントゥース,ブローケントゥース,7/8,* +brothers-in-law,1348,1348,141,名詞,固有名詞,一般,*,*,*,ブラザーズインロウ,ブラザーズインロウ,ブラザーズインロウ,0/9,* +carpe-diem,1348,1348,588,名詞,固有名詞,一般,*,*,*,カーペディエム,カーペディエム,カーペディエム,5/6,* +champaign-urbana,1348,1348,594,名詞,固有名詞,一般,*,*,*,シャンペーンアーバナ,シャンペーンアーバナ,シャンペーンアーバナ,7/9,* +churchhouse,1348,1348,711,名詞,固有名詞,一般,*,*,*,チャーチハウス,チャーチハウス,チャーチハウス,5/6,* +coca-cola,1348,1348,223,名詞,固有名詞,一般,*,*,*,コカコーラ,コカコーラ,コカコーラ,4/5,* +computer-generated,1348,1348,699,名詞,固有名詞,一般,*,*,*,コンピュータージェネレーテッド,コンピュータージェネレーテッド,コンピュータージェネレーテッド,10/13,* +daughters-in-law,1348,1348,193,名詞,固有名詞,一般,*,*,*,ドーターズインロー,ドーターズインロー,ドーターズインロー,0/9,* +diaz-calderon,1348,1348,-1024,名詞,固有名詞,一般,*,*,*,ディアスカルデロン,ディアスカルデロン,ディアスカルデロン,0/8,* +diego-garcia,1348,1348,384,名詞,固有名詞,一般,*,*,*,ディエゴガルシア,ディエゴガルシア,ディエゴガルシア,5/7,* +dumb-ass,1348,1348,166,名詞,固有名詞,一般,*,*,*,ダムアス,ダムアス,ダムアス,3/4,* +el-greco,1348,1348,160,名詞,固有名詞,一般,*,*,*,エルグレコ,エルグレコ,エルグレコ,4/5,* +ex-boyfriends,1348,1348,421,名詞,固有名詞,一般,*,*,*,エクスボーイフレンズ,エクスボーイフレンズ,エクスボーイフレンズ,9/10,* +ex-girlfriends,1348,1348,473,名詞,固有名詞,一般,*,*,*,エックスガールフレンズ,エックスガールフレンズ,エックスガールフレンズ,0/11,* +face-to-face,1348,1348,-14,名詞,固有名詞,一般,*,*,*,フェイストゥフェイス,フェイストゥフェイス,フェイストゥフェイス,7/7,* +foot-and-mouth,1348,1348,100,名詞,固有名詞,一般,*,*,*,フートアンドマウス,フートアンドマウス,フートアンドマウス,8/9,* +french-polynesia,1348,1348,594,名詞,固有名詞,一般,*,*,*,フランスポリネシア,フランスポリネシア,フランスポリネシア,8/9,* +goings-ons,1348,1348,273,名詞,固有名詞,一般,*,*,*,ゴーイングスオンズ,ゴーイングスオンズ,ゴーイングスオンズ,7/9,* +great-grandfather,1348,1348,644,名詞,固有名詞,一般,*,*,*,グレイトグランドファーザー,グレイトグランドファーザー,グレイトグランドファーザー,10/12,* +hit-and-run,1348,1348,-68,名詞,固有名詞,一般,*,*,*,ヒットアンドラン,ヒットアンドラン,ヒットアンドラン,8/8,* +homo-erectus,1348,1348,711,名詞,固有名詞,一般,*,*,*,ホモエレクトゥス,ホモエレクトゥス,ホモエレクトゥス,4/7,* +ieee,1348,1348,344,名詞,固有名詞,一般,*,*,*,アイトリプルイー,アイトリプルイー,アイトリプルイー,1/8,* +jean-luc,1348,1348,166,名詞,固有名詞,一般,*,*,*,ジャンリュック,ジャンリュック,ジャンリュック,4/5,* +jean-michel,1348,1348,330,名詞,固有名詞,一般,*,*,*,ジャンミシェル,ジャンミシェル,ジャンミシェル,4/5,* +jean-michele,1348,1348,383,名詞,固有名詞,一般,*,*,*,ジャンミッシェル,ジャンミッシェル,ジャンミッシェル,4/6,* +juarez-espin,1348,1348,695,名詞,固有名詞,一般,*,*,*,フアレスエスピン,フアレスエスピン,フアレスエスピン,6/8,* +kebab-n-curry,1348,1348,25,名詞,固有名詞,一般,*,*,*,ケバブンカレー,ケバブンカレー,ケバブンカレー,0/7,* +life-affirming,1348,1348,487,名詞,固有名詞,一般,*,*,*,ライフアファーミング,ライフアファーミング,ライフアファーミング,0/9,* +might-have-beens,1348,1348,211,名詞,固有名詞,一般,*,*,*,マイトハブビーンズ,マイトハブビーンズ,マイトハブビーンズ,8/9,* +modus-operandi,1348,1348,489,名詞,固有名詞,一般,*,*,*,モーダスオペランディ,モーダスオペランディ,モーダスオペランディ,8/9,* +moholy-nagy,1348,1348,657,名詞,固有名詞,一般,*,*,*,モホリナギ,モホリナギ,モホリナギ,4/5,* +moseley-braun,1348,1348,437,名詞,固有名詞,一般,*,*,*,モーズリーブラウン,モーズリーブラウン,モーズリーブラウン,8/9,* +non-communists,1348,1348,-226,名詞,固有名詞,一般,*,*,*,ノンコミュニスツ,ノンコミュニスツ,ノンコミュニスツ,0/7,* +non-discrimination,1348,1348,-488,名詞,固有名詞,一般,*,*,*,ノンディスクリミネーション,ノンディスクリミネーション,ノンディスクリミネーション,9/11,* +non-governmental,1348,1348,-123,名詞,固有名詞,一般,*,*,*,ノンガバメントゥル,ノンガバメントゥル,ノンガバメントゥル,6/8,* +non-transferable,1348,1348,-123,名詞,固有名詞,一般,*,*,*,ノントランスファラブル,ノントランスファラブル,ノントランスファラブル,8/10,* +o'canas,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,オカナス,オカナス,オカナス,3/4,* +off-duty,1348,1348,166,名詞,固有名詞,一般,*,*,*,オフデューティ,オフデューティ,オフデューティ,4/5,* +one-on-one,1348,1348,-290,名詞,固有名詞,一般,*,*,*,ワンオンワン,ワンオンワン,ワンオンワン,0/6,* +one-year,1348,1348,163,名詞,固有名詞,一般,*,*,*,ワンイヤー,ワンイヤー,ワンイヤー,3/5,* +pee-pee,1348,1348,109,名詞,固有名詞,一般,*,*,*,ピーピー,ピーピー,ピーピー,1/4,* +peeping,1348,1348,504,名詞,固有名詞,一般,*,*,*,ピーピング,ピーピング,ピーピング,0/5,* +peer-to-peer,1348,1348,-14,名詞,固有名詞,一般,*,*,*,ピアトゥピア,ピアトゥピア,ピアトゥピア,5/5,* +rock-and-roll,1348,1348,46,名詞,固有名詞,一般,*,*,*,ロックアンドロール,ロックアンドロール,ロックアンドロール,9/9,* +rpm,1348,1348,287,名詞,固有名詞,一般,*,*,*,アールピーエム,アールピーエム,アールピーエム,7/7,* +saint-saens,1348,1348,711,名詞,固有名詞,一般,*,*,*,サンサーンズ,サンサーンズ,サンサーンズ,4/6,* +science-fiction,1348,1348,-289,名詞,固有名詞,一般,*,*,*,サイエンスフィクション,サイエンスフィクション,サイエンスフィクション,7/9,* +serbo-croatian,1348,1348,800,名詞,固有名詞,一般,*,*,*,セルボクロアチアン,セルボクロアチアン,セルボクロアチアン,7/9,* +sheath-knife,1348,1348,384,名詞,固有名詞,一般,*,*,*,シースナイフ,シースナイフ,シースナイフ,5/6,* +sisters-in-law,1348,1348,89,名詞,固有名詞,一般,*,*,*,シスターズインロウ,シスターズインロウ,シスターズインロウ,7/9,* +sons-in-law,1348,1348,-205,名詞,固有名詞,一般,*,*,*,サンズインロー,サンズインロー,サンズインロー,0/7,* +soviet-union,1348,1348,-1081,名詞,固有名詞,一般,*,*,*,ソビエトユニオン,ソビエトユニオン,ソビエトユニオン,6/8,* +speaking,1348,1348,556,名詞,固有名詞,一般,*,*,*,スピーキング,スピーキング,スピーキング,0/6,* +twenty-three,1348,1348,384,名詞,固有名詞,一般,*,*,*,トゥエンティースリー,トゥエンティースリー,トゥエンティースリー,6/8,* +two-day,1348,1348,109,名詞,固有名詞,一般,*,*,*,ツーデイ,ツーデイ,ツーデイ,0/4,* +two-dimensional,1348,1348,533,名詞,固有名詞,一般,*,*,*,ツーディメンショナル,ツーディメンショナル,ツーディメンショナル,5/8,* +two-thirds,1348,1348,273,名詞,固有名詞,一般,*,*,*,トゥーサーズ,トゥーサーズ,トゥーサーズ,1/5,* +untruths,1348,1348,556,名詞,固有名詞,一般,*,*,*,アントルーズ,アントルーズ,アントルーズ,5/6,* +whiz-zip-bang,1348,1348,46,名詞,固有名詞,一般,*,*,*,ウィズジップバン,ウィズジップバン,ウィズジップバン,7/7,* diff --git a/tools/data/japanese_dictionary_source.json b/tools/data/japanese_dictionary_source.json new file mode 100644 index 00000000..b477ae31 --- /dev/null +++ b/tools/data/japanese_dictionary_source.json @@ -0,0 +1,9911 @@ +{ + "version": "2026.10.16.3", + "language": "ja", + "source": { + "repository": "https://github.com/RVC-Boss/GPT-SoVITS", + "revision": "1b47ca07ef43a284e0b8bd7f29d3e1e3ff76082a", + "path": "GPT_SoVITS/text/ja_userdic/userdict.csv", + "git_blob": "2be580b461185ce18c9000d9ec6c8032623e17fd", + "sha256": "d857e443ee48d9641096816a98996669602895411e4330d7d91d1dbe1103389f", + "bytes": 17180971, + "entry_count": 134201, + "last_dictionary_commit": "eee607b71dae07fb3b29f8f2727490039b3ac984", + "checked_on": "2026-10-16" + }, + "derivation": { + "description": "Upstream PR 1660 describes CMUdict converted to Katakana using ChatGPT and DeepSeek; PR 1670 restored the dictionary after an import fix.", + "references": [ + "https://github.com/RVC-Boss/GPT-SoVITS/pull/1660", + "https://github.com/RVC-Boss/GPT-SoVITS/pull/1670" + ], + "limitations": "Upstream does not identify the exact CMUdict revision or record per-entry human verification. Preserve the existing CMUdict 0.07 notice without claiming that this identifies the translation's exact input revision." + }, + "license_expression": "AGPL-3.0 AND MIT AND BSD-2-Clause", + "license_basis": "Amadeus modifications follow the root AGPL-3.0 license. The upstream CSV relies on the immediate GPT-SoVITS repository-wide MIT grant; retain the CMUdict BSD notice for the stated source chain.", + "normalization": { + "native_runtime": "0.4.1.post9", + "rules": [ + "NFC in lexical reading fields", + "remove only phonetic-neutral separators/format controls from otherwise pure Katakana readings when the existing accent remains valid", + "spell untranslated whole-token initialisms", + "recount morae from one explicit native pronunciation while preserving valid accent nuclei", + "remove only exact duplicate entries", + "canonicalize native MeCab surface spelling only for repaired entries; leave unchanged, unreviewed surfaces intact" + ], + "unresolved_policy": "Flag non-kana remnants and out-of-range accent candidates for review; retain them without guessing a replacement. Preserve different legitimate readings of the same word." + }, + "corrections": [ + { + "word": "follower", + "before": "フォロワーズ", + "after": "フォロワー", + "reason": "Restore the singular noun; the plural entry remains unchanged." + }, + { + "word": "learner", + "before": "ラーナーズ", + "after": "ラーナー", + "reason": "Restore the singular noun; the plural entry remains unchanged." + }, + { + "word": "proposer", + "before": null, + "after": "プロポーザー", + "reason": "Add a complete word instead of the native propose + r split. Flat accent is provisional pending listening review." + }, + { + "word": "abstraction", + "before": "アブストラクションズ", + "after": "アブストラクション", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "acronym", + "before": "アクロニムズ", + "after": "アクロニム", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "adapter", + "before": "アダプターズ", + "after": "アダプター", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "airline", + "before": "エアラインズ", + "after": "エアライン", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "applicator", + "before": "アプリケーターズ", + "after": "アプリケーター", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "bank", + "before": "バンクス", + "after": "バンク", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final S." + }, + { + "word": "component", + "before": "コンポーネンツ", + "after": "コンポーネント", + "cost_before": 608, + "cost_after": 0, + "reason": "Singular T and plural TS are distinct in CMUdict. Native full-pack validation also exposes comp + on + ent splitting at cost 608; cost 0 keeps the corrected whole word." + }, + { + "word": "components", + "before": "コンポーネンツ", + "after": "コンポーネンツ", + "cost_before": 659, + "cost_after": 0, + "reason": "Keep the existing plural reading selected after repairing the singular entry cost; native validation must preserve both whole-word readings." + }, + { + "word": "developer", + "before": "デベロッパーズ", + "after": "デベロッパー", + "reason": "Singular entry copied the plural; CMUdict distinguishes the final Z." + }, + { + "word": "handler", + "before": "ハンドラーズ", + "after": "ハンドラー", + "reason": "Remove the plural reading from the singular surface, retaining its existing correct reading and plural entries." + }, + { + "word": "adhd", + "before": "アドバンズド・ディスオーダー", + "after": "エーディーエイチディー", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 1039, + "cmudict": [ + { + "line": 1127, + "phones": [ + "EY2", + "D", + "IY2", + "EY2", + "CH", + "D", + "IY1" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "エーディーエイチディー", + "reading": "エーディーエイチディー", + "accent": "8/9" + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "allergist's", + "before": "アレルジスト'sス", + "after": "アレルジストス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 2693, + "cmudict": [ + { + "line": 2883, + "phones": [ + "AE1", + "L", + "ER0", + "JH", + "AH0", + "S", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "allergist's", + "split": [ + "allergist", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "allergist'sを確認する。", + "split": [ + "allergist", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはallergist'sです。", + "split": [ + "allergist", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "amd", + "before": "エイ・イー・エム・ディー", + "after": "エーエムディー", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 3314, + "cmudict": [ + { + "line": 3533, + "phones": [ + "EY1", + "EH2", + "M", + "D", + "IY1" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "エーエムディー", + "reading": "エーエムディー", + "accent": "5/6" + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "bahrainian", + "before": "バーレーン人", + "after": "バーレイニアン", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 7358, + "cmudict": [ + { + "line": 7862, + "phones": [ + "B", + "ER0", + "EY1", + "N", + "IY0", + "AH0", + "N" + ] + }, + { + "line": 7863, + "phones": [ + "B", + "AY0", + "R", + "AA1", + "N", + "IY0", + "AH0", + "N" + ] + }, + { + "line": 7864, + "phones": [ + "B", + "AA0", + "R", + "EY1", + "N", + "IY0", + "AH0", + "N" + ] + } + ], + "reading_reference": { + "word": "bahrainian", + "baseline_line": 7359, + "accent": "3/7", + "cmudict": [ + { + "line": 7862, + "phones": [ + "B", + "ER0", + "EY1", + "N", + "IY0", + "AH0", + "N" + ] + }, + { + "line": 7863, + "phones": [ + "B", + "AY0", + "R", + "AA1", + "N", + "IY0", + "AH0", + "N" + ] + }, + { + "line": 7864, + "phones": [ + "B", + "AA0", + "R", + "EY1", + "N", + "IY0", + "AH0", + "N" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "ballet's", + "before": "バレエ'sズ", + "after": "バレエズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 7621, + "cmudict": [ + { + "line": 8131, + "phones": [ + "B", + "AE0", + "L", + "EY1", + "Z" + ] + } + ], + "reading_reference": { + "word": "ballets", + "baseline_line": 7622, + "accent": "1/4", + "cmudict": [ + { + "line": 8132, + "phones": [ + "B", + "AE0", + "L", + "EY1", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "ballet's", + "split": [ + "ballet", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "ballet'sを確認する。", + "split": [ + "ballet", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはballet'sです。", + "split": [ + "ballet", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "balloonist", + "before": "バルー二スト", + "after": "バルーニスト", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 7642, + "cmudict": [ + { + "line": 8152, + "phones": [ + "B", + "AH0", + "L", + "UW1", + "N", + "IH0", + "S", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "barbecued", + "before": "バーベキューど", + "after": "バーベキュード", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 8021, + "cmudict": [ + { + "line": 8555, + "phones": [ + "B", + "AA1", + "R", + "B", + "IH0", + "K", + "Y", + "UW2", + "D" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "business's", + "before": "ビジネス'esズ", + "after": "ビジネシズ", + "reason": "Spell the CMU S IH Z ending; the apostrophe/es text is not pronunciation.", + "evidence": { + "baseline_line": 16080, + "cmudict": [ + { + "line": 16919, + "phones": [ + "B", + "IH1", + "Z", + "N", + "IH0", + "S", + "IH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "business's", + "split": [ + "business", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "business'sを確認する。", + "split": [ + "business", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはbusiness'sです。", + "split": [ + "business", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "busloads", + "before": "バスローズ゙ズ", + "after": "バスローズ", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 16105, + "cmudict": [ + { + "line": 16946, + "phones": [ + "B", + "AH0", + "S", + "L", + "OW1", + "D", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "busts", + "before": "バストSス", + "after": "バストス", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 16141, + "cmudict": [ + { + "line": 16985, + "phones": [ + "B", + "AH1", + "S", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "cajoling", + "before": "カジョーリンぐ", + "after": "カジョーリング", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 16606, + "cmudict": [ + { + "line": 17480, + "phones": [ + "K", + "AH0", + "JH", + "OW1", + "L", + "IH0", + "NG" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "cds", + "before": "CDSズ", + "after": "シーディーズ", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 18636, + "cmudict": [ + { + "line": 19635, + "phones": [ + "S", + "IY2", + "D", + "IY1", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "chamonix", + "before": "シャモニ(revised to シャモニー)", + "after": "シャモニー", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 19169, + "cmudict": [ + { + "line": 20196, + "phones": [ + "CH", + "AE1", + "M", + "AH0", + "N", + "IH0", + "K", + "S" + ] + }, + { + "line": 20197, + "phones": [ + "SH", + "AE0", + "M", + "OW0", + "N", + "IY0" + ] + } + ], + "reading_reference": { + "word": "chamonix", + "baseline_line": 19168, + "accent": "0/4", + "cmudict": [ + { + "line": 20196, + "phones": [ + "CH", + "AE1", + "M", + "AH0", + "N", + "IH0", + "K", + "S" + ] + }, + { + "line": 20197, + "phones": [ + "SH", + "AE0", + "M", + "OW0", + "N", + "IY0" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "christ's", + "before": "クライスト'sス", + "after": "クライストス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 20270, + "cmudict": [ + { + "line": 21366, + "phones": [ + "K", + "R", + "AY1", + "S", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "christ's", + "split": [ + "christ", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "christ'sを確認する。", + "split": [ + "christ", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはchrist'sです。", + "split": [ + "christ", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "circuit's", + "before": "サーキット'sス", + "after": "サーキッツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 20630, + "cmudict": [ + { + "line": 21740, + "phones": [ + "S", + "ER1", + "K", + "AH0", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "circuits", + "baseline_line": 20634, + "accent": "3/5", + "cmudict": [ + { + "line": 21744, + "phones": [ + "S", + "ER1", + "K", + "AH0", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "circuit's", + "split": [ + "circuit", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "circuit'sを確認する。", + "split": [ + "circuit", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはcircuit'sです。", + "split": [ + "circuit", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "condo", + "before": "コンド(con-do)", + "after": "コンド", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 22968, + "cmudict": [ + { + "line": 24178, + "phones": [ + "K", + "AA1", + "N", + "D", + "OW0" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "コンド", + "reading": "コンド", + "accent": "1/3" + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "condo", + "split": [ + "c", + "on", + "do" + ], + "split_boundary_cost": -870, + "whole_word_boundary_cost": -751 + }, + { + "text": "condoを確認する。", + "split": [ + "c", + "on", + "do" + ], + "split_boundary_cost": -3891, + "whole_word_boundary_cost": -3772 + }, + { + "text": "これはcondoです。", + "split": [ + "c", + "on", + "do" + ], + "split_boundary_cost": -1231, + "whole_word_boundary_cost": -1112 + } + ] + } + }, + "cost_before": 398, + "cost_after": 278 + }, + { + "word": "corps", + "before": "コア(replace the final \"r\" sound with a small \"u\" to indicate the silent \"r\")", + "after": "コア", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 24318, + "cmudict": [ + { + "line": 25613, + "phones": [ + "K", + "AO1", + "R" + ] + }, + { + "line": 25614, + "phones": [ + "K", + "AO1", + "R", + "Z" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "コア", + "reading": "コア", + "accent": "1/2" + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "credit's", + "before": "クレジット'sス", + "after": "クレジッツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 25312, + "cmudict": [ + { + "line": 26654, + "phones": [ + "K", + "R", + "EH1", + "D", + "IH0", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "credits", + "baseline_line": 25325, + "accent": "0/5", + "cmudict": [ + { + "line": 26668, + "phones": [ + "K", + "R", + "EH1", + "D", + "IH0", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "credit's", + "split": [ + "credit", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "credit'sを確認する。", + "split": [ + "credit", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはcredit'sです。", + "split": [ + "credit", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "cricket's", + "before": "クリケット'sス", + "after": "クリケッツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 25455, + "cmudict": [ + { + "line": 26803, + "phones": [ + "K", + "R", + "IH1", + "K", + "AH0", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "crickets", + "baseline_line": 25456, + "accent": "3/5", + "cmudict": [ + { + "line": 26804, + "phones": [ + "K", + "R", + "IH1", + "K", + "AH0", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "cricket's", + "split": [ + "cricket", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "cricket'sを確認する。", + "split": [ + "cricket", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはcricket'sです。", + "split": [ + "cricket", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "criminalist's", + "before": "クリミナリスト'sス", + "after": "クリミナリスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 25480, + "cmudict": [ + { + "line": 26828, + "phones": [ + "K", + "R", + "IH1", + "M", + "AH0", + "N", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "criminalists", + "baseline_line": 25482, + "accent": "5/7", + "cmudict": [ + { + "line": 26830, + "phones": [ + "K", + "R", + "IH1", + "M", + "AH0", + "N", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "criminalist's", + "split": [ + "criminalist", + "’", + "s" + ], + "split_boundary_cost": -2196, + "whole_word_boundary_cost": -336 + }, + { + "text": "criminalist'sを確認する。", + "split": [ + "criminalist", + "’", + "s" + ], + "split_boundary_cost": -5217, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはcriminalist'sです。", + "split": [ + "criminalist", + "’", + "s" + ], + "split_boundary_cost": -2557, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": -1048 + }, + { + "word": "debarment", + "before": "ディベート's", + "after": "ディバーメント", + "reason": "The source contains an unrelated debate possessive; restore D IH B AA R M AH N T, retaining its valid nucleus.", + "evidence": { + "baseline_line": 27660, + "cmudict": [ + { + "line": 29115, + "phones": [ + "D", + "IH0", + "B", + "AA1", + "R", + "M", + "AH0", + "N", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "debate's", + "before": "ディベイト'sス", + "after": "ディベーツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 27673, + "cmudict": [ + { + "line": 29128, + "phones": [ + "D", + "AH0", + "B", + "EY1", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "debates", + "baseline_line": 27677, + "accent": "2/4", + "cmudict": [ + { + "line": 29132, + "phones": [ + "D", + "AH0", + "B", + "EY1", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "debate's", + "split": [ + "debate", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "debate'sを確認する。", + "split": [ + "debate", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはdebate'sです。", + "split": [ + "debate", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "dentist", + "before": "デンティスト's", + "after": "デンティスト", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 29132, + "cmudict": [ + { + "line": 30639, + "phones": [ + "D", + "EH1", + "N", + "T", + "AH0", + "S", + "T" + ] + }, + { + "line": 30640, + "phones": [ + "D", + "EH1", + "N", + "T", + "IH0", + "S", + "T" + ] + }, + { + "line": 30641, + "phones": [ + "D", + "EH1", + "N", + "IH0", + "S", + "T" + ] + } + ], + "reading_reference": { + "word": "dentist", + "baseline_line": 29131, + "accent": "3/5", + "cmudict": [ + { + "line": 30639, + "phones": [ + "D", + "EH1", + "N", + "T", + "AH0", + "S", + "T" + ] + }, + { + "line": 30640, + "phones": [ + "D", + "EH1", + "N", + "T", + "IH0", + "S", + "T" + ] + }, + { + "line": 30641, + "phones": [ + "D", + "EH1", + "N", + "IH0", + "S", + "T" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "documented", + "before": "ドキュメントed", + "after": "ドキュメンテッド", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 31868, + "cmudict": [ + { + "line": 33630, + "phones": [ + "D", + "AA1", + "K", + "Y", + "AH0", + "M", + "EH2", + "N", + "T", + "AH0", + "D" + ] + }, + { + "line": 33631, + "phones": [ + "D", + "AA1", + "K", + "Y", + "AH0", + "M", + "EH2", + "N", + "AH0", + "D" + ] + }, + { + "line": 33632, + "phones": [ + "D", + "AA1", + "K", + "Y", + "UW0", + "M", + "EH2", + "N", + "T", + "AH0", + "D" + ] + }, + { + "line": 33633, + "phones": [ + "D", + "AA1", + "K", + "Y", + "UW0", + "M", + "EH2", + "N", + "AH0", + "D" + ] + } + ], + "reading_reference": { + "word": "documented", + "baseline_line": 31869, + "accent": "5/7", + "cmudict": [ + { + "line": 33630, + "phones": [ + "D", + "AA1", + "K", + "Y", + "AH0", + "M", + "EH2", + "N", + "T", + "AH0", + "D" + ] + }, + { + "line": 33631, + "phones": [ + "D", + "AA1", + "K", + "Y", + "AH0", + "M", + "EH2", + "N", + "AH0", + "D" + ] + }, + { + "line": 33632, + "phones": [ + "D", + "AA1", + "K", + "Y", + "UW0", + "M", + "EH2", + "N", + "T", + "AH0", + "D" + ] + }, + { + "line": 33633, + "phones": [ + "D", + "AA1", + "K", + "Y", + "UW0", + "M", + "EH2", + "N", + "AH0", + "D" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "dolomite's", + "before": "ドロマイト'sス", + "after": "ドロマイツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 32055, + "cmudict": [ + { + "line": 33834, + "phones": [ + "D", + "OW1", + "L", + "AH0", + "M", + "AY2", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "dolomites", + "baseline_line": 32056, + "accent": "3/5", + "cmudict": [ + { + "line": 33835, + "phones": [ + "D", + "OW1", + "L", + "AH0", + "M", + "AY2", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "dolomite's", + "split": [ + "dolomite", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "dolomite'sを確認する。", + "split": [ + "dolomite", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはdolomite'sです。", + "split": [ + "dolomite", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "economist's", + "before": "エコノミスト'sス", + "after": "エコノミスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 34340, + "cmudict": [ + { + "line": 36249, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 36250, + "phones": [ + "IY0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 36251, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + }, + { + "line": 36252, + "phones": [ + "IY0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + } + ], + "reading_reference": { + "word": "economists", + "baseline_line": 34342, + "accent": "4/6", + "cmudict": [ + { + "line": 36253, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 36254, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + }, + { + "line": 36255, + "phones": [ + "IY0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "economist's", + "split": [ + "economist", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "economist'sを確認する。", + "split": [ + "economist", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはeconomist'sです。", + "split": [ + "economist", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "economists'", + "before": "エコノミスト's", + "after": "エコノミスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 34344, + "cmudict": [ + { + "line": 36256, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 36257, + "phones": [ + "IY0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "economists", + "baseline_line": 34342, + "accent": "4/6", + "cmudict": [ + { + "line": 36253, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 36254, + "phones": [ + "IH0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + }, + { + "line": 36255, + "phones": [ + "IY0", + "K", + "AA1", + "N", + "AH0", + "M", + "IH0", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "economists'", + "split": [ + "economists", + "’" + ], + "split_boundary_cost": -3369, + "whole_word_boundary_cost": -438 + }, + { + "text": "これはeconomists'です。", + "split": [ + "economists", + "’" + ], + "split_boundary_cost": -1358, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -2221 + }, + { + "word": "embassy's", + "before": "エンバシー'sズ", + "after": "エンバシーズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 35489, + "cmudict": [ + { + "line": 37477, + "phones": [ + "EH1", + "M", + "B", + "AH0", + "S", + "IY0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "embassy's", + "split": [ + "embassy", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "embassy'sを確認する。", + "split": [ + "embassy", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはembassy'sです。", + "split": [ + "embassy", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "endocrinologist's", + "before": "エンドクリノロジスト'sス", + "after": "エンドクリノロジスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 35943, + "cmudict": [ + { + "line": 38013, + "phones": [ + "EH2", + "N", + "D", + "OW0", + "K", + "R", + "AH0", + "N", + "AA1", + "L", + "AH0", + "JH", + "AH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "endocrinologists", + "baseline_line": 35944, + "accent": "8/10", + "cmudict": [ + { + "line": 38014, + "phones": [ + "EH2", + "N", + "D", + "OW0", + "K", + "R", + "AH0", + "N", + "AA1", + "L", + "AH0", + "JH", + "AH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "endocrinologist's", + "split": [ + "endocrinologist", + "’", + "s" + ], + "split_boundary_cost": -1993, + "whole_word_boundary_cost": -133 + }, + { + "text": "endocrinologist'sを確認する。", + "split": [ + "endocrinologist", + "’", + "s" + ], + "split_boundary_cost": -5014, + "whole_word_boundary_cost": -3154 + }, + { + "text": "これはendocrinologist'sです。", + "split": [ + "endocrinologist", + "’", + "s" + ], + "split_boundary_cost": -2354, + "whole_word_boundary_cost": -494 + } + ] + } + }, + "cost_before": 1016, + "cost_after": -845 + }, + { + "word": "ernest's", + "before": "アーネスト'sス", + "after": "アーネスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 36812, + "cmudict": [ + { + "line": 38964, + "phones": [ + "ER1", + "N", + "AH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "ernests", + "baseline_line": 36815, + "accent": "0/5", + "cmudict": [ + { + "line": 38967, + "phones": [ + "ER1", + "N", + "AH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "ernest's", + "split": [ + "ernest", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "ernest'sを確認する。", + "split": [ + "ernest", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはernest'sです。", + "split": [ + "ernest", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "everest's", + "before": "エヴェレスト'sス", + "after": "エヴェレストス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 37500, + "cmudict": [ + { + "line": 39730, + "phones": [ + "EH1", + "V", + "ER0", + "AH0", + "S", + "T", + "S" + ] + }, + { + "line": 39731, + "phones": [ + "EH1", + "V", + "R", + "AH0", + "S", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "everest's", + "split": [ + "everest", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "everest'sを確認する。", + "split": [ + "everest", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはeverest'sです。", + "split": [ + "everest", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "experienced", + "before": "エクスぺリエンスト", + "after": "エクスペリエンスト", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 37982, + "cmudict": [ + { + "line": 40258, + "phones": [ + "IH0", + "K", + "S", + "P", + "IH1", + "R", + "IY0", + "AH0", + "N", + "S", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "faraday's", + "before": "ファラデー'sズ", + "after": "ファラデーズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 38761, + "cmudict": [ + { + "line": 41075, + "phones": [ + "F", + "AE1", + "R", + "AH0", + "D", + "EY2", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "faraday's", + "split": [ + "faraday", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "faraday'sを確認する。", + "split": [ + "faraday", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはfaraday'sです。", + "split": [ + "faraday", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "fluoresce", + "before": "フローレス fluoresce", + "after": "フローレス", + "reason": "Remove the repeated English source word; retain this CMU-supported vowel variant and the distinct existing フルオレス variant.", + "evidence": { + "baseline_line": 41172, + "cmudict": [ + { + "line": 43604, + "phones": [ + "F", + "L", + "UH2", + "R", + "EH1", + "S" + ] + }, + { + "line": 43605, + "phones": [ + "F", + "L", + "AO2", + "R", + "EH1", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "gadhafi's", + "before": "カダフィ'sズ", + "after": "カダフィズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 43362, + "cmudict": [ + { + "line": 45944, + "phones": [ + "G", + "AH0", + "D", + "AA1", + "F", + "IY0", + "Z" + ] + }, + { + "line": 45945, + "phones": [ + "G", + "AH0", + "D", + "HH", + "AA1", + "F", + "IY0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "gadhafi's", + "split": [ + "gadhafi", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "gadhafi'sを確認する。", + "split": [ + "gadhafi", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはgadhafi'sです。", + "split": [ + "gadhafi", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "grant's", + "before": "グラント'sス", + "after": "グランツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 47178, + "cmudict": [ + { + "line": 49929, + "phones": [ + "G", + "R", + "AE1", + "N", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "grants", + "baseline_line": 47187, + "accent": "2/4", + "cmudict": [ + { + "line": 49942, + "phones": [ + "G", + "R", + "AE1", + "N", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "grant's", + "split": [ + "grant", + "’", + "s" + ], + "split_boundary_cost": -2509, + "whole_word_boundary_cost": -645 + }, + { + "text": "grant'sを確認する。", + "split": [ + "grant", + "’", + "s" + ], + "split_boundary_cost": -5530, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはgrant'sです。", + "split": [ + "grant", + "’", + "s" + ], + "split_boundary_cost": -2870, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": -1361 + }, + { + "word": "hermetically", + "before": "ハーメチックally", + "after": "ハーメチックリー", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 51766, + "cmudict": [ + { + "line": 54695, + "phones": [ + "HH", + "ER0", + "M", + "EH1", + "T", + "IH0", + "K", + "AH0", + "L", + "IY0" + ] + }, + { + "line": 54696, + "phones": [ + "HH", + "ER0", + "M", + "EH1", + "T", + "IH0", + "K", + "L", + "IY0" + ] + } + ], + "reading_reference": { + "word": "hermetically", + "baseline_line": 51767, + "accent": "4/8", + "cmudict": [ + { + "line": 54695, + "phones": [ + "HH", + "ER0", + "M", + "EH1", + "T", + "IH0", + "K", + "AH0", + "L", + "IY0" + ] + }, + { + "line": 54696, + "phones": [ + "HH", + "ER0", + "M", + "EH1", + "T", + "IH0", + "K", + "L", + "IY0" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "hgh", + "before": "ヘッジhogehoge", + "after": "エイチジーエイチ", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 52063, + "cmudict": [ + { + "line": 55010, + "phones": [ + "EY1", + "CH", + "JH", + "IY1", + "EY1", + "CH" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "hirohito's", + "before": "ヒロヒト'sズ", + "after": "ヒロヒトズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 52546, + "cmudict": [ + { + "line": 55510, + "phones": [ + "HH", + "IH2", + "R", + "OW0", + "HH", + "IY1", + "T", + "OW2", + "Z" + ] + }, + { + "line": 55511, + "phones": [ + "HH", + "IH2", + "R", + "AH0", + "HH", + "IY1", + "T", + "OW2", + "Z" + ] + } + ], + "reading_reference": { + "word": "hirohito's", + "baseline_line": 52547, + "accent": "3/5", + "cmudict": [ + { + "line": 55510, + "phones": [ + "HH", + "IH2", + "R", + "OW0", + "HH", + "IY1", + "T", + "OW2", + "Z" + ] + }, + { + "line": 55511, + "phones": [ + "HH", + "IH2", + "R", + "AH0", + "HH", + "IY1", + "T", + "OW2", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "hirohito's", + "split": [ + "hirohito", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "hirohito'sを確認する。", + "split": [ + "hirohito", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはhirohito'sです。", + "split": [ + "hirohito", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "hoechst's", + "before": "ヘキスト'sス", + "after": "ヘキスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 52823, + "cmudict": [ + { + "line": 55799, + "phones": [ + "HH", + "OW1", + "K", + "S", + "T", + "S" + ] + }, + { + "line": 55800, + "phones": [ + "HH", + "OW1", + "SH", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "hoechst's", + "baseline_line": 52822, + "accent": "0/4", + "cmudict": [ + { + "line": 55799, + "phones": [ + "HH", + "OW1", + "K", + "S", + "T", + "S" + ] + }, + { + "line": 55800, + "phones": [ + "HH", + "OW1", + "SH", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "hoechst's", + "split": [ + "hoechst", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "hoechst'sを確認する。", + "split": [ + "hoechst", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはhoechst'sです。", + "split": [ + "hoechst", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "hospice's", + "before": "ホスピス'sズ", + "after": "ホスピシズ", + "reason": "Spell the CMU S IH Z ending; preserve the existing kana stem and valid nucleus.", + "evidence": { + "baseline_line": 53782, + "cmudict": [ + { + "line": 56789, + "phones": [ + "HH", + "AA1", + "S", + "P", + "AH0", + "S", + "IH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "hospice's", + "split": [ + "hospice", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "hospice'sを確認する。", + "split": [ + "hospice", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはhospice'sです。", + "split": [ + "hospice", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "illusionist's", + "before": "イリュージョニスト'sス", + "after": "イリュージョニスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 55339, + "cmudict": [ + { + "line": 58460, + "phones": [ + "IH2", + "L", + "UW1", + "ZH", + "AH0", + "N", + "AH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "illusionists", + "baseline_line": 55340, + "accent": "5/7", + "cmudict": [ + { + "line": 58461, + "phones": [ + "IH2", + "L", + "UW1", + "ZH", + "AH0", + "N", + "AH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "illusionist's", + "split": [ + "illusionist", + "’", + "s" + ], + "split_boundary_cost": -2196, + "whole_word_boundary_cost": -336 + }, + { + "text": "illusionist'sを確認する。", + "split": [ + "illusionist", + "’", + "s" + ], + "split_boundary_cost": -5217, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはillusionist'sです。", + "split": [ + "illusionist", + "’", + "s" + ], + "split_boundary_cost": -2557, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": -1048 + }, + { + "word": "intruder", + "before": "インtrウーダー", + "after": "イントルーダー", + "reason": "Complete the untranslated TR consonants consistently with IH N T R UW D ER.", + "evidence": { + "baseline_line": 57422, + "cmudict": [ + { + "line": 60793, + "phones": [ + "IH2", + "N", + "T", + "R", + "UW1", + "D", + "ER0" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "intrusive", + "before": "インtrusive", + "after": "イントルーシヴ", + "reason": "Replace untranslated letters using IH N T R UW S IH V; retain the original valid nucleus.", + "evidence": { + "baseline_line": 57428, + "cmudict": [ + { + "line": 60799, + "phones": [ + "IH2", + "N", + "T", + "R", + "UW1", + "S", + "IH0", + "V" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "intrusiveness", + "before": "インtrウsイヴネス", + "after": "イントルーシヴネス", + "reason": "Complete the same intrusive stem followed by N EH S; retain the original flat nucleus.", + "evidence": { + "baseline_line": 57429, + "cmudict": [ + { + "line": 60800, + "phones": [ + "IH2", + "N", + "T", + "R", + "UW1", + "S", + "IH0", + "V", + "N", + "EH0", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "iodide's", + "before": "アイアイオ゙ダイツ", + "after": "アイオダイズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 57580, + "cmudict": [ + { + "line": 60961, + "phones": [ + "AY1", + "AH0", + "D", + "AY2", + "D", + "Z" + ] + } + ], + "reading_reference": { + "word": "iodides", + "baseline_line": 57581, + "accent": "4/6", + "cmudict": [ + { + "line": 60962, + "phones": [ + "AY1", + "AH0", + "D", + "AY2", + "D", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "iodide's", + "split": [ + "iodide", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "iodide'sを確認する。", + "split": [ + "iodide", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはiodide'sです。", + "split": [ + "iodide", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "jfet", + "before": "Jフェット", + "after": "ジェイフェット", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 58944, + "cmudict": [ + { + "line": 62411, + "phones": [ + "JH", + "F", + "EH1", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "journalist's", + "before": "ジャーナリスト'sス", + "after": "ジャーナリスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 59340, + "cmudict": [ + { + "line": 62825, + "phones": [ + "JH", + "ER1", + "N", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "journalists", + "baseline_line": 59343, + "accent": "4/6", + "cmudict": [ + { + "line": 62829, + "phones": [ + "JH", + "ER1", + "N", + "AH0", + "L", + "AH0", + "S", + "T", + "S" + ] + }, + { + "line": 62830, + "phones": [ + "JH", + "ER1", + "N", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "journalist's", + "split": [ + "journalist", + "’", + "s" + ], + "split_boundary_cost": -2248, + "whole_word_boundary_cost": -387 + }, + { + "text": "journalist'sを確認する。", + "split": [ + "journalist", + "’", + "s" + ], + "split_boundary_cost": -5269, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはjournalist'sです。", + "split": [ + "journalist", + "’", + "s" + ], + "split_boundary_cost": -2609, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -1100 + }, + { + "word": "lawrence", + "before": "ローレンス's", + "after": "ローレンス", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 65225, + "cmudict": [ + { + "line": 68949, + "phones": [ + "L", + "AO1", + "R", + "AH0", + "N", + "S" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "ローレンス", + "reading": "ローレンス", + "accent": "1/5" + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "lieutenant's", + "before": "リューテナンツ'sス", + "after": "リューテナンツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 66705, + "cmudict": [ + { + "line": 70481, + "phones": [ + "L", + "UW0", + "T", + "EH1", + "N", + "AH0", + "N", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "lieutenants", + "baseline_line": 66706, + "accent": "4/6", + "cmudict": [ + { + "line": 70482, + "phones": [ + "L", + "UW0", + "T", + "EH1", + "N", + "AH0", + "N", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "lieutenant's", + "split": [ + "lieutenant", + "’", + "s" + ], + "split_boundary_cost": -2248, + "whole_word_boundary_cost": -387 + }, + { + "text": "lieutenant'sを確認する。", + "split": [ + "lieutenant", + "’", + "s" + ], + "split_boundary_cost": -5269, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはlieutenant'sです。", + "split": [ + "lieutenant", + "’", + "s" + ], + "split_boundary_cost": -2609, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -1100 + }, + { + "word": "luxottica's", + "before": "ルクソティカ'sズ", + "after": "ルクソティカズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 68972, + "cmudict": [ + { + "line": 72801, + "phones": [ + "L", + "AH2", + "K", + "S", + "OW0", + "T", + "IY1", + "K", + "AH0", + "Z" + ] + }, + { + "line": 72802, + "phones": [ + "L", + "AH2", + "K", + "S", + "AA1", + "T", + "IH0", + "K", + "AH0", + "Z" + ] + } + ], + "reading_reference": { + "word": "luxottica's", + "baseline_line": 68971, + "accent": "0/6", + "cmudict": [ + { + "line": 72801, + "phones": [ + "L", + "AH2", + "K", + "S", + "OW0", + "T", + "IY1", + "K", + "AH0", + "Z" + ] + }, + { + "line": 72802, + "phones": [ + "L", + "AH2", + "K", + "S", + "AA1", + "T", + "IH0", + "K", + "AH0", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "luxottica's", + "split": [ + "luxottica", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "luxottica'sを確認する。", + "split": [ + "luxottica", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはluxottica'sです。", + "split": [ + "luxottica", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "marlboro's", + "before": "マールボロ'sズ", + "after": "マールボロズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 71020, + "cmudict": [ + { + "line": 74932, + "phones": [ + "M", + "AA1", + "R", + "L", + "B", + "ER0", + "R", + "OW0", + "Z" + ] + } + ], + "reading_reference": { + "word": "marlboros", + "baseline_line": 71021, + "accent": "4/6", + "cmudict": [ + { + "line": 74933, + "phones": [ + "M", + "AA1", + "R", + "L", + "B", + "ER0", + "R", + "OW0", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "marlboro's", + "split": [ + "marlboro", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "marlboro'sを確認する。", + "split": [ + "marlboro", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはmarlboro'sです。", + "split": [ + "marlboro", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "marriott's", + "before": "マリオット'sス", + "after": "マリオットス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 71119, + "cmudict": [ + { + "line": 75032, + "phones": [ + "M", + "EH1", + "R", + "IY0", + "AH0", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "marriott's", + "split": [ + "marriott", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "marriott'sを確認する。", + "split": [ + "marriott", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはmarriott'sです。", + "split": [ + "marriott", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "matsushita's", + "before": "マツシタ'sズ", + "after": "マツシタズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 71752, + "cmudict": [ + { + "line": 75678, + "phones": [ + "M", + "AA2", + "T", + "S", + "UW0", + "SH", + "IY1", + "T", + "AH0", + "Z" + ] + }, + { + "line": 75679, + "phones": [ + "M", + "AA0", + "T", + "S", + "UW1", + "SH", + "IY0", + "T", + "AH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "matsushita's", + "split": [ + "matsushita", + "’", + "s" + ], + "split_boundary_cost": -2248, + "whole_word_boundary_cost": -387 + }, + { + "text": "matsushita'sを確認する。", + "split": [ + "matsushita", + "’", + "s" + ], + "split_boundary_cost": -5269, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはmatsushita'sです。", + "split": [ + "matsushita", + "’", + "s" + ], + "split_boundary_cost": -2609, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -1100 + }, + { + "word": "medalist's", + "before": "メダリスト'sス", + "after": "メダリスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 73271, + "cmudict": [ + { + "line": 77230, + "phones": [ + "M", + "EH1", + "D", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "medalists", + "baseline_line": 73272, + "accent": "3/5", + "cmudict": [ + { + "line": 77231, + "phones": [ + "M", + "EH1", + "D", + "AH0", + "L", + "IH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "medalist's", + "split": [ + "medalist", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "medalist'sを確認する。", + "split": [ + "medalist", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはmedalist'sです。", + "split": [ + "medalist", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "meteorologist", + "before": "メテオロジスト's", + "after": "メテオロジスト", + "reason": "Remove an extraneous possessive marker from the non-possessive surface.", + "evidence": { + "baseline_line": 74322, + "cmudict": [ + { + "line": 78325, + "phones": [ + "M", + "IY2", + "T", + "IY0", + "ER0", + "AA1", + "L", + "AH0", + "JH", + "IH0", + "S", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "meteorologist's", + "before": "メテオロジスト'sス", + "after": "メテオロジスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 74323, + "cmudict": [ + { + "line": 78326, + "phones": [ + "M", + "IY2", + "T", + "IY0", + "ER0", + "AA1", + "L", + "AH0", + "JH", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "meteorologists", + "baseline_line": 74324, + "accent": "5/7", + "cmudict": [ + { + "line": 78327, + "phones": [ + "M", + "IY2", + "T", + "IY0", + "ER0", + "AA1", + "L", + "AH0", + "JH", + "IH0", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "meteorologist's", + "split": [ + "meteorologist", + "’", + "s" + ], + "split_boundary_cost": -2094, + "whole_word_boundary_cost": -235 + }, + { + "text": "meteorologist'sを確認する。", + "split": [ + "meteorologist", + "’", + "s" + ], + "split_boundary_cost": -5115, + "whole_word_boundary_cost": -3256 + }, + { + "text": "これはmeteorologist'sです。", + "split": [ + "meteorologist", + "’", + "s" + ], + "split_boundary_cost": -2455, + "whole_word_boundary_cost": -596 + } + ] + } + }, + "cost_before": 914, + "cost_after": -946 + }, + { + "word": "mississippi's", + "before": "ミシシッピ'sズ", + "after": "ミシシッピーズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 75798, + "cmudict": [ + { + "line": 79867, + "phones": [ + "M", + "IH2", + "S", + "IH0", + "S", + "IH1", + "P", + "IY0", + "Z" + ] + } + ], + "reading_reference": { + "word": "mississippis", + "baseline_line": 75801, + "accent": "0/7", + "cmudict": [ + { + "line": 79870, + "phones": [ + "M", + "IH2", + "S", + "IH0", + "S", + "IH1", + "P", + "IY0", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "mississippi's", + "split": [ + "mississippi", + "’", + "s" + ], + "split_boundary_cost": -2196, + "whole_word_boundary_cost": -336 + }, + { + "text": "mississippi'sを確認する。", + "split": [ + "mississippi", + "’", + "s" + ], + "split_boundary_cost": -5217, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはmississippi'sです。", + "split": [ + "mississippi", + "’", + "s" + ], + "split_boundary_cost": -2557, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": -1048 + }, + { + "word": "mormonism", + "before": "モルモン教", + "after": "モルモニズム", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 77037, + "cmudict": [ + { + "line": 81183, + "phones": [ + "M", + "AO1", + "R", + "M", + "AH0", + "N", + "IH0", + "Z", + "M" + ] + }, + { + "line": 81184, + "phones": [ + "M", + "AO1", + "R", + "M", + "AH0", + "N", + "IH0", + "Z", + "AH0", + "M" + ] + } + ], + "reading_reference": { + "word": "mormonism", + "baseline_line": 77036, + "accent": "4/6", + "cmudict": [ + { + "line": 81183, + "phones": [ + "M", + "AO1", + "R", + "M", + "AH0", + "N", + "IH0", + "Z", + "M" + ] + }, + { + "line": 81184, + "phones": [ + "M", + "AO1", + "R", + "M", + "AH0", + "N", + "IH0", + "Z", + "AH0", + "M" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "narrates", + "before": "ナレートSス", + "after": "ナレイツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 78638, + "cmudict": [ + { + "line": 82866, + "phones": [ + "N", + "EH0", + "R", + "EY1", + "T", + "S" + ] + }, + { + "line": 82867, + "phones": [ + "N", + "EH1", + "R", + "EY0", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "narrates", + "baseline_line": 78637, + "accent": "2/4", + "cmudict": [ + { + "line": 82866, + "phones": [ + "N", + "EH0", + "R", + "EY1", + "T", + "S" + ] + }, + { + "line": 82867, + "phones": [ + "N", + "EH1", + "R", + "EY0", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "nike's", + "before": "ナイキ'sズ", + "after": "ナイキズ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 80007, + "cmudict": [ + { + "line": 84324, + "phones": [ + "N", + "AY1", + "K", + "IY0", + "Z" + ] + } + ], + "reading_reference": { + "word": "nikes", + "baseline_line": 80009, + "accent": "1/4", + "cmudict": [ + { + "line": 84325, + "phones": [ + "N", + "AY1", + "K", + "S" + ] + }, + { + "line": 84326, + "phones": [ + "N", + "AY1", + "K", + "IY0", + "Z" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "nike's", + "split": [ + "nike", + "’", + "s" + ], + "split_boundary_cost": -2563, + "whole_word_boundary_cost": -698 + }, + { + "text": "nike'sを確認する。", + "split": [ + "nike", + "’", + "s" + ], + "split_boundary_cost": -5584, + "whole_word_boundary_cost": -3719 + }, + { + "text": "これはnike'sです。", + "split": [ + "nike", + "’", + "s" + ], + "split_boundary_cost": -2924, + "whole_word_boundary_cost": -1059 + } + ] + } + }, + "cost_before": 451, + "cost_after": -1415 + }, + { + "word": "ninth", + "before": "ナイんス", + "after": "ナインス", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 80092, + "cmudict": [ + { + "line": 84412, + "phones": [ + "N", + "AY1", + "N", + "TH" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "norway's", + "before": "ノルウェー'sズ", + "after": "ノルウェーズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 80917, + "cmudict": [ + { + "line": 85260, + "phones": [ + "N", + "AO1", + "R", + "W", + "EY2", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "norway's", + "split": [ + "norway", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "norway'sを確認する。", + "split": [ + "norway", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはnorway'sです。", + "split": [ + "norway", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "office's", + "before": "オフィス'sズ", + "after": "オフィシズ", + "reason": "Spell the CMU S IH Z ending; the apostrophe/s text is not pronunciation.", + "evidence": { + "baseline_line": 82032, + "cmudict": [ + { + "line": 86431, + "phones": [ + "AO1", + "F", + "AH0", + "S", + "IH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "office's", + "split": [ + "office", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "office'sを確認する。", + "split": [ + "office", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはoffice'sです。", + "split": [ + "office", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "personalizes", + "before": "パーソナライズ(es)ズ", + "after": "パーソナライズィズ", + "reason": "Spell the CMU Z IH Z ending instead of leaving the parenthetical es letters in the reading.", + "evidence": { + "baseline_line": 86833, + "cmudict": [ + { + "line": 91433, + "phones": [ + "P", + "ER1", + "S", + "AH0", + "N", + "AH0", + "L", + "AY2", + "Z", + "IH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "pinnochio", + "before": "ピノキオ's", + "after": "ピノキオ", + "reason": "Remove an extraneous possessive marker from the non-possessive surface.", + "evidence": { + "baseline_line": 88024, + "cmudict": [ + { + "line": 92673, + "phones": [ + "P", + "IH0", + "N", + "OW1", + "K", + "IY2", + "OW2" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "pinnochio's", + "before": "ピノキオ'sズ", + "after": "ピノキオズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 88025, + "cmudict": [ + { + "line": 92674, + "phones": [ + "P", + "IH0", + "N", + "OW1", + "K", + "IY2", + "OW2", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "pinnochio's", + "split": [ + "pinnochio", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "pinnochio'sを確認する。", + "split": [ + "pinnochio", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはpinnochio'sです。", + "split": [ + "pinnochio", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "politz", + "before": "ポリツ\tTokenNameス", + "after": "ポリツ", + "reason": "Remove the literal TokenName placeholder and its unsupported extra S; retain the existing kana transcription of P AA L IH T S.", + "evidence": { + "baseline_line": 89074, + "cmudict": [ + { + "line": 93769, + "phones": [ + "P", + "AA1", + "L", + "IH0", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "poppy's", + "before": "ポッピー'sズ", + "after": "ポッピーズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 89419, + "cmudict": [ + { + "line": 94123, + "phones": [ + "P", + "AA1", + "P", + "IY0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "poppy's", + "split": [ + "poppy", + "’", + "s" + ], + "split_boundary_cost": -2509, + "whole_word_boundary_cost": -645 + }, + { + "text": "poppy'sを確認する。", + "split": [ + "poppy", + "’", + "s" + ], + "split_boundary_cost": -5530, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはpoppy'sです。", + "split": [ + "poppy", + "’", + "s" + ], + "split_boundary_cost": -2870, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": -1361 + }, + { + "word": "post's", + "before": "ポスト'sス", + "after": "ポスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 89643, + "cmudict": [ + { + "line": 94357, + "phones": [ + "P", + "OW1", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "posts", + "baseline_line": 89707, + "accent": "1/3", + "cmudict": [ + { + "line": 94432, + "phones": [ + "P", + "OW1", + "S", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "post's", + "split": [ + "post", + "’", + "s" + ], + "split_boundary_cost": -2563, + "whole_word_boundary_cost": -698 + }, + { + "text": "post'sを確認する。", + "split": [ + "post", + "’", + "s" + ], + "split_boundary_cost": -5584, + "whole_word_boundary_cost": -3719 + }, + { + "text": "これはpost'sです。", + "split": [ + "post", + "’", + "s" + ], + "split_boundary_cost": -2924, + "whole_word_boundary_cost": -1059 + } + ] + } + }, + "cost_before": 451, + "cost_after": -1415 + }, + { + "word": "purchaser", + "before": "パーチェイサー (パーチェイザー)", + "after": "パーチェイサー", + "reason": "Keep the first kana option; CMU has S, not the Z in the parenthetical option.", + "evidence": { + "baseline_line": 91868, + "cmudict": [ + { + "line": 96823, + "phones": [ + "P", + "ER1", + "CH", + "AH0", + "S", + "ER0" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "sake's", + "before": "サケ'sス", + "after": "セイクス", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 99718, + "cmudict": [ + { + "line": 105241, + "phones": [ + "S", + "EY1", + "K", + "S" + ] + } + ], + "reading_reference": { + "word": "sakes", + "baseline_line": 99720, + "accent": "1/4", + "cmudict": [ + { + "line": 105243, + "phones": [ + "S", + "EY1", + "K", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "sake's", + "split": [ + "sake", + "’", + "s" + ], + "split_boundary_cost": -2563, + "whole_word_boundary_cost": -698 + }, + { + "text": "sake'sを確認する。", + "split": [ + "sake", + "’", + "s" + ], + "split_boundary_cost": -5584, + "whole_word_boundary_cost": -3719 + }, + { + "text": "これはsake'sです。", + "split": [ + "sake", + "’", + "s" + ], + "split_boundary_cost": -2924, + "whole_word_boundary_cost": -1059 + } + ] + } + }, + "cost_before": 451, + "cost_after": -1415 + }, + { + "word": "sarajevo's", + "before": "サラエボ'sズ", + "after": "サラエボズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 100380, + "cmudict": [ + { + "line": 105925, + "phones": [ + "S", + "AA2", + "R", + "AH0", + "Y", + "EY1", + "V", + "OW0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "sarajevo's", + "split": [ + "sarajevo", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "sarajevo'sを確認する。", + "split": [ + "sarajevo", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはsarajevo'sです。", + "split": [ + "sarajevo", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "schubert's", + "before": "シューベルト'sス", + "after": "シューベルトス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 101720, + "cmudict": [ + { + "line": 107307, + "phones": [ + "SH", + "UW1", + "B", + "ER0", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "schubert's", + "split": [ + "schubert", + "’", + "s" + ], + "split_boundary_cost": -2351, + "whole_word_boundary_cost": -490 + }, + { + "text": "schubert'sを確認する。", + "split": [ + "schubert", + "’", + "s" + ], + "split_boundary_cost": -5372, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはschubert'sです。", + "split": [ + "schubert", + "’", + "s" + ], + "split_boundary_cost": -2712, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -1203 + }, + { + "word": "scientist's", + "before": "サイエンティスト'sス", + "after": "サイエンティスツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 101939, + "cmudict": [ + { + "line": 107532, + "phones": [ + "S", + "AY1", + "AH0", + "N", + "T", + "IH0", + "S", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "scientists", + "baseline_line": 101940, + "accent": "5/7", + "cmudict": [ + { + "line": 107533, + "phones": [ + "S", + "AY1", + "AH0", + "N", + "T", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 107534, + "phones": [ + "S", + "AY1", + "N", + "T", + "IH0", + "S", + "T", + "S" + ] + }, + { + "line": 107535, + "phones": [ + "S", + "AY1", + "N", + "T", + "IH0", + "S" + ] + }, + { + "line": 107536, + "phones": [ + "S", + "AY1", + "AH0", + "N", + "T", + "IH0", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "scientist's", + "split": [ + "scientist", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "scientist'sを確認する。", + "split": [ + "scientist", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはscientist'sです。", + "split": [ + "scientist", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "scout's", + "before": "スカウツ'sス", + "after": "スカウツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 102090, + "cmudict": [ + { + "line": 107689, + "phones": [ + "S", + "K", + "AW1", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "scouts", + "baseline_line": 102097, + "accent": "2/4", + "cmudict": [ + { + "line": 107696, + "phones": [ + "S", + "K", + "AW1", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "scout's", + "split": [ + "scout", + "’", + "s" + ], + "split_boundary_cost": -2509, + "whole_word_boundary_cost": -645 + }, + { + "text": "scout'sを確認する。", + "split": [ + "scout", + "’", + "s" + ], + "split_boundary_cost": -5530, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはscout'sです。", + "split": [ + "scout", + "’", + "s" + ], + "split_boundary_cost": -2870, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": -1361 + }, + { + "word": "shevardnadze's", + "before": "シェヴァルドナーゼ'sズ", + "after": "シェヴァルドナーゼズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 104331, + "cmudict": [ + { + "line": 110048, + "phones": [ + "SH", + "EH2", + "V", + "ER0", + "D", + "N", + "AA1", + "D", + "Z", + "IY0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "shevardnadze's", + "split": [ + "shevardnadze", + "’", + "s" + ], + "split_boundary_cost": -2145, + "whole_word_boundary_cost": -286 + }, + { + "text": "shevardnadze'sを確認する。", + "split": [ + "shevardnadze", + "’", + "s" + ], + "split_boundary_cost": -5166, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはshevardnadze'sです。", + "split": [ + "shevardnadze", + "’", + "s" + ], + "split_boundary_cost": -2506, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": -997 + }, + { + "word": "sizes", + "before": "サイズ(es)ズ", + "after": "サイズィズ", + "reason": "Spell the CMU Z IH Z plural ending instead of leaving the parenthetical es letters in the reading.", + "evidence": { + "baseline_line": 105864, + "cmudict": [ + { + "line": 111627, + "phones": [ + "S", + "AY1", + "Z", + "AH0", + "Z" + ] + }, + { + "line": 111628, + "phones": [ + "S", + "AY1", + "Z", + "IH0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "smith's", + "before": "スミス'sス", + "after": "スミス", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 106661, + "cmudict": [ + { + "line": 112440, + "phones": [ + "S", + "M", + "IH1", + "TH", + "S" + ] + } + ], + "reading_reference": { + "word": "smiths", + "baseline_line": 106674, + "accent": "1/3", + "cmudict": [ + { + "line": 112453, + "phones": [ + "S", + "M", + "IH1", + "TH", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "smith's", + "split": [ + "smith", + "’", + "s" + ], + "split_boundary_cost": -2509, + "whole_word_boundary_cost": -645 + }, + { + "text": "smith'sを確認する。", + "split": [ + "smith", + "’", + "s" + ], + "split_boundary_cost": -5530, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはsmith'sです。", + "split": [ + "smith", + "’", + "s" + ], + "split_boundary_cost": -2870, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": -1361 + }, + { + "word": "soviet's", + "before": "ソヴィエツのス", + "after": "ソビエツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 107809, + "cmudict": [ + { + "line": 113623, + "phones": [ + "S", + "OW1", + "V", + "IY0", + "EH2", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "soviets", + "baseline_line": 107813, + "accent": "0/4", + "cmudict": [ + { + "line": 113627, + "phones": [ + "S", + "OW1", + "V", + "IY0", + "EH2", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "soviet's", + "split": [ + "soviet", + "’", + "s" + ], + "split_boundary_cost": -2456, + "whole_word_boundary_cost": -593 + }, + { + "text": "soviet'sを確認する。", + "split": [ + "soviet", + "’", + "s" + ], + "split_boundary_cost": -5477, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはsoviet'sです。", + "split": [ + "soviet", + "’", + "s" + ], + "split_boundary_cost": -2817, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": -1308 + }, + { + "word": "stowaway", + "before": "ス トウエイズ", + "after": "ストウアウェイ", + "reason": "Restore S T OW AH W EY; the source contains a stray space and an unsupported final plural Z.", + "evidence": { + "baseline_line": 110538, + "cmudict": [ + { + "line": 116432, + "phones": [ + "S", + "T", + "OW1", + "AH0", + "W", + "EY2" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "superintendent's", + "before": "スーパーリンテンデント'sス", + "after": "スーパーリンテンデンツ", + "reason": "Replace the non-phonetic residue with the reviewed existing pronunciation; preserve other readings of this surface.", + "evidence": { + "baseline_line": 111937, + "cmudict": [ + { + "line": 117878, + "phones": [ + "S", + "UW2", + "P", + "ER0", + "IH0", + "N", + "T", + "EH1", + "N", + "D", + "AH0", + "N", + "T", + "S" + ] + } + ], + "reading_reference": { + "word": "superintendents", + "baseline_line": 111939, + "accent": "7/11", + "cmudict": [ + { + "line": 117879, + "phones": [ + "S", + "UW2", + "P", + "ER0", + "AH0", + "N", + "T", + "EH1", + "N", + "D", + "AH0", + "N", + "T", + "S" + ] + }, + { + "line": 117880, + "phones": [ + "S", + "UW2", + "P", + "ER0", + "IH0", + "N", + "T", + "EH1", + "N", + "D", + "AH0", + "N", + "T", + "S" + ] + } + ] + }, + "accent_policy": "reuse documented existing/native nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "superintendent's", + "split": [ + "superintendent", + "’", + "s" + ], + "split_boundary_cost": -2044, + "whole_word_boundary_cost": -184 + }, + { + "text": "superintendent'sを確認する。", + "split": [ + "superintendent", + "’", + "s" + ], + "split_boundary_cost": -5065, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはsuperintendent'sです。", + "split": [ + "superintendent", + "’", + "s" + ], + "split_boundary_cost": -2405, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": -896 + }, + { + "word": "suvs", + "before": "SUVズ", + "after": "エスユーブイズ", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 112299, + "cmudict": [ + { + "line": 118275, + "phones": [ + "EH2", + "S", + "Y", + "UW2", + "V", + "IY1", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "swords", + "before": "ソーズ゙ズ", + "after": "ソーズ", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 112683, + "cmudict": [ + { + "line": 118674, + "phones": [ + "S", + "AO1", + "R", + "D", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "synergy's", + "before": "シナジー'sズ", + "after": "シナジーズ", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 112850, + "cmudict": [ + { + "line": 118846, + "phones": [ + "S", + "IH1", + "N", + "ER0", + "JH", + "IY0", + "Z" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "synergy's", + "split": [ + "synergy", + "’", + "s" + ], + "split_boundary_cost": -2403, + "whole_word_boundary_cost": -541 + }, + { + "text": "synergy'sを確認する。", + "split": [ + "synergy", + "’", + "s" + ], + "split_boundary_cost": -5424, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはsynergy'sです。", + "split": [ + "synergy", + "’", + "s" + ], + "split_boundary_cost": -2764, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": -1255 + }, + { + "word": "t-shirt", + "before": "Tシャツ", + "after": "ティーシャツ", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 112972, + "cmudict": [ + { + "line": 118972, + "phones": [ + "T", + "IY1", + "S", + "ER2", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "t-shirt", + "split": [ + "t", + "−", + "shirt" + ], + "split_boundary_cost": -1057, + "whole_word_boundary_cost": -645 + }, + { + "text": "t-shirtを確認する。", + "split": [ + "t", + "−", + "shirt" + ], + "split_boundary_cost": -4078, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはt-shirtです。", + "split": [ + "t", + "−", + "shirt" + ], + "split_boundary_cost": -1418, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": 91 + }, + { + "word": "t-shirts", + "before": "Tシャツ", + "after": "ティーシャツ", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 112973, + "cmudict": [ + { + "line": 118973, + "phones": [ + "T", + "IY1", + "S", + "ER2", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "t-shirts", + "split": [ + "t", + "−", + "shirts" + ], + "split_boundary_cost": -1004, + "whole_word_boundary_cost": -593 + }, + { + "text": "t-shirtsを確認する。", + "split": [ + "t", + "−", + "shirts" + ], + "split_boundary_cost": -4025, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはt-shirtsです。", + "split": [ + "t", + "−", + "shirts" + ], + "split_boundary_cost": -1365, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 144 + }, + { + "word": "thrusts", + "before": "スラストsス", + "after": "スラストス", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 115278, + "cmudict": [ + { + "line": 121406, + "phones": [ + "TH", + "R", + "AH1", + "S", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "tlc", + "before": "テレビ番組", + "after": "ティーエルシー", + "reason": "Replace a gloss, placeholder or untranslated letter with existing Japanese letter-name conventions; retain the original valid accent nucleus.", + "evidence": { + "baseline_line": 115821, + "cmudict": [ + { + "line": 121965, + "phones": [ + "T", + "IY2", + "EH2", + "L", + "S", + "IY1" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "unfocused", + "before": "アンフォーカスト(unfocussed)", + "after": "アンフォーカスト", + "reason": "Remove the parenthetical English spelling variant, which is commentary rather than pronunciation.", + "evidence": { + "baseline_line": 119164, + "cmudict": [ + { + "line": 125467, + "phones": [ + "AH0", + "N", + "F", + "OW1", + "K", + "AH0", + "S", + "T" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "untruthful", + "before": "アン**ト**ル**ー**フル", + "after": "アントゥルースフル", + "reason": "Remove formatting and restore the missing TH before F in AH N T R UW TH F AH L; retain the flat nucleus.", + "evidence": { + "baseline_line": 119704, + "cmudict": [ + { + "line": 126039, + "phones": [ + "AH0", + "N", + "T", + "R", + "UW1", + "TH", + "F", + "AH0", + "L" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "voldemort's", + "before": "ヴォルデモート'sス", + "after": "ヴォルデモートス", + "reason": "Remove the literal possessive letters; retain the existing kana stem, its already written final S/Z and its valid nucleus. CMU confirms that exact consonant suffix.", + "evidence": { + "baseline_line": 122149, + "cmudict": [ + { + "line": 128608, + "phones": [ + "V", + "OW1", + "L", + "D", + "AH0", + "M", + "AO2", + "R", + "T", + "S" + ] + } + ], + "accent_policy": "preserve existing valid nucleus", + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "voldemort's", + "split": [ + "voldemort", + "’", + "s" + ], + "split_boundary_cost": -2299, + "whole_word_boundary_cost": -438 + }, + { + "text": "voldemort'sを確認する。", + "split": [ + "voldemort", + "’", + "s" + ], + "split_boundary_cost": -5320, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはvoldemort'sです。", + "split": [ + "voldemort", + "’", + "s" + ], + "split_boundary_cost": -2660, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -1151 + }, + { + "word": "wurtzel", + "before": "ワ―ツェル", + "after": "ワーツェル", + "reason": "Repair a mixed-script character, duplicate voicing/inflection remnant or misencoded long-vowel mark; CMU phones support the retained kana sequence.", + "evidence": { + "baseline_line": 126357, + "cmudict": [ + { + "line": 133186, + "phones": [ + "W", + "ER1", + "T", + "Z", + "AH0", + "L" + ] + } + ], + "accent_policy": "preserve existing valid nucleus" + } + }, + { + "word": "yomiuri", + "before": "読売", + "after": "ヨミウリ", + "reason": "Replace an unrelated expansion, source annotation or untranslated spelling; reuse the native single-word Japanese entry and its accent.", + "evidence": { + "baseline_line": 126984, + "cmudict": [ + { + "line": 133850, + "phones": [ + "Y", + "OW2", + "M", + "IY1", + "ER0", + "IY0" + ] + } + ], + "reading_reference": { + "native_runtime": "0.4.1.post9", + "input": "読売", + "reading": "ヨミウリ", + "accent": "0/4" + }, + "accent_policy": "reuse documented existing/native nucleus" + } + }, + { + "word": "adl-tabataba", + "before": "アドル・タバタバ", + "after": "アドルタバタバ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 1099, + "cmudict": [ + { + "line": 1189, + "phones": [ + "AA2", + "D", + "AH0", + "L", + "T", + "AA2", + "B", + "AH0", + "T", + "AA1", + "B", + "AH0" + ] + } + ] + } + }, + { + "word": "adl-tabatabai", + "before": "アドル・タバタバイ", + "after": "アドルタバタバイ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 1100, + "cmudict": [ + { + "line": 1190, + "phones": [ + "AA2", + "D", + "AH0", + "L", + "T", + "AA2", + "B", + "AH0", + "T", + "AH2", + "B", + "AY1" + ] + } + ] + } + }, + { + "word": "aix-la-chapelle", + "before": "エクス・ラ・シャペル", + "after": "エクスラシャペル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 2079, + "cmudict": [ + { + "line": 2234, + "phones": [ + "EY1", + "K", + "S", + "L", + "AA2", + "SH", + "AH0", + "P", + "EH1", + "L" + ] + }, + { + "line": 2235, + "phones": [ + "EH1", + "K", + "S", + "L", + "AA2", + "SH", + "AH0", + "P", + "EH1", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "aix-la-chapelle", + "split": [ + "aix", + "−", + "la", + "−", + "chapelle" + ], + "split_boundary_cost": -1007, + "whole_word_boundary_cost": -235 + }, + { + "text": "aix-la-chapelleを確認する。", + "split": [ + "aix", + "−", + "la", + "−", + "chapelle" + ], + "split_boundary_cost": -4028, + "whole_word_boundary_cost": -3256 + }, + { + "text": "これはaix-la-chapelleです。", + "split": [ + "aix", + "−", + "la", + "−", + "chapelle" + ], + "split_boundary_cost": -1368, + "whole_word_boundary_cost": -596 + } + ] + } + }, + "cost_before": 914, + "cost_after": 141 + }, + { + "word": "anglo-catholicism", + "before": "アングロ・カトリシズム", + "after": "アングロカトリシズム", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 3988, + "cmudict": [ + { + "line": 4248, + "phones": [ + "AE1", + "NG", + "G", + "L", + "OW0", + "K", + "AH0", + "TH", + "AO1", + "L", + "AH0", + "S", + "IH2", + "Z", + "AH0", + "M" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "anglo-catholicism", + "split": [ + "anglo", + "−", + "catholicism" + ], + "split_boundary_cost": -504, + "whole_word_boundary_cost": -133 + }, + { + "text": "anglo-catholicismを確認する。", + "split": [ + "anglo", + "−", + "catholicism" + ], + "split_boundary_cost": -3525, + "whole_word_boundary_cost": -3154 + }, + { + "text": "これはanglo-catholicismです。", + "split": [ + "anglo", + "−", + "catholicism" + ], + "split_boundary_cost": -865, + "whole_word_boundary_cost": -494 + } + ] + } + }, + "cost_before": 1016, + "cost_after": 644 + }, + { + "word": "bar-mitzvah", + "before": "バー・ミツバー", + "after": "バーミツバー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 7973, + "cmudict": [ + { + "line": 8502, + "phones": [ + "B", + "AA1", + "R", + "M", + "IH1", + "T", + "S", + "V", + "AH0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "bar-mitzvah", + "split": [ + "bar", + "−", + "mitzvah" + ], + "split_boundary_cost": -822, + "whole_word_boundary_cost": -438 + }, + { + "text": "bar-mitzvahを確認する。", + "split": [ + "bar", + "−", + "mitzvah" + ], + "split_boundary_cost": -3843, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはbar-mitzvahです。", + "split": [ + "bar", + "−", + "mitzvah" + ], + "split_boundary_cost": -1183, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": 326 + }, + { + "word": "bas-reliefs", + "before": "バス・リリーフス", + "after": "バスリリーフス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 8455, + "cmudict": [ + { + "line": 9033, + "phones": [ + "B", + "AA1", + "R", + "IY0", + "L", + "IY1", + "F", + "S" + ] + }, + { + "line": 9034, + "phones": [ + "B", + "AA1", + "S", + "R", + "IY0", + "L", + "IY1", + "F", + "S" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "bas-reliefs", + "split": [ + "b", + "as", + "−", + "reliefs" + ], + "split_boundary_cost": -517, + "whole_word_boundary_cost": -438 + }, + { + "text": "bas-reliefsを確認する。", + "split": [ + "b", + "as", + "−", + "reliefs" + ], + "split_boundary_cost": -3538, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはbas-reliefsです。", + "split": [ + "ba", + "s", + "−", + "reliefs" + ], + "split_boundary_cost": -878, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": 631 + }, + { + "word": "broken-tooth", + "before": "ブローケン-トゥース", + "after": "ブローケントゥース", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 14703, + "cmudict": [ + { + "line": 15494, + "phones": [ + "B", + "R", + "OW1", + "K", + "AH0", + "N", + "T", + "UW2", + "TH" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "broken-tooth", + "split": [ + "broken", + "−", + "tooth" + ], + "split_boundary_cost": -764, + "whole_word_boundary_cost": -387 + }, + { + "text": "broken-toothを確認する。", + "split": [ + "broken", + "−", + "tooth" + ], + "split_boundary_cost": -3785, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはbroken-toothです。", + "split": [ + "broken", + "−", + "tooth" + ], + "split_boundary_cost": -1125, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 384 + }, + { + "word": "brothers-in-law", + "before": "ブラザーズ・イン・ロウ", + "after": "ブラザーズインロウ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 14847, + "cmudict": [ + { + "line": 15646, + "phones": [ + "B", + "R", + "AH1", + "DH", + "ER0", + "Z", + "IH0", + "N", + "L", + "AO2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "brothers-in-law", + "split": [ + "brothers", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1007, + "whole_word_boundary_cost": -235 + }, + { + "text": "brothers-in-lawを確認する。", + "split": [ + "brothers", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -4028, + "whole_word_boundary_cost": -3256 + }, + { + "text": "これはbrothers-in-lawです。", + "split": [ + "brothers", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1368, + "whole_word_boundary_cost": -596 + } + ] + } + }, + "cost_before": 914, + "cost_after": 141 + }, + { + "word": "carpe-diem", + "before": "カーペ・ディエム", + "after": "カーペディエム", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 17853, + "cmudict": [ + { + "line": 18788, + "phones": [ + "K", + "AA1", + "R", + "P", + "AH0", + "D", + "IY1", + "AH0", + "M" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "carpe-diem", + "split": [ + "carp", + "e", + "−", + "diem" + ], + "split_boundary_cost": -560, + "whole_word_boundary_cost": -490 + }, + { + "text": "carpe-diemを確認する。", + "split": [ + "carp", + "e", + "−", + "diem" + ], + "split_boundary_cost": -3581, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはcarpe-diemです。", + "split": [ + "carp", + "e", + "−", + "diem" + ], + "split_boundary_cost": -921, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": 588 + }, + { + "word": "champaign-urbana", + "before": "シャンペーン・アーバナ", + "after": "シャンペーンアーバナ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 19177, + "cmudict": [ + { + "line": 20206, + "phones": [ + "CH", + "AE0", + "M", + "P", + "EY1", + "N", + "ER0", + "B", + "AE1", + "N", + "AH0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "champaign-urbana", + "split": [ + "champaign", + "−", + "urbana" + ], + "split_boundary_cost": -554, + "whole_word_boundary_cost": -184 + }, + { + "text": "champaign-urbanaを確認する。", + "split": [ + "champaign", + "−", + "urbana" + ], + "split_boundary_cost": -3575, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはchampaign-urbanaです。", + "split": [ + "champaign", + "−", + "urbana" + ], + "split_boundary_cost": -915, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": 594 + }, + { + "word": "churchhouse", + "before": " チャーチハウス", + "after": "チャーチハウス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 20441, + "cmudict": [ + { + "line": 21547, + "phones": [ + "CH", + "ER1", + "CH", + "HH", + "AW2", + "S" + ] + } + ] + } + }, + { + "word": "coca-cola", + "before": "コカ・コーラ", + "after": "コカコーラ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 21613, + "cmudict": [ + { + "line": 22758, + "phones": [ + "K", + "OW2", + "K", + "AH0", + "K", + "OW1", + "L", + "AA0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "coca-cola", + "split": [ + "coca", + "−", + "cola" + ], + "split_boundary_cost": -925, + "whole_word_boundary_cost": -541 + }, + { + "text": "coca-colaを確認する。", + "split": [ + "coca", + "−", + "cola" + ], + "split_boundary_cost": -3946, + "whole_word_boundary_cost": -3562 + }, + { + "text": "これはcoca-colaです。", + "split": [ + "coca", + "−", + "cola" + ], + "split_boundary_cost": -1286, + "whole_word_boundary_cost": -902 + } + ] + } + }, + "cost_before": 608, + "cost_after": 223 + }, + { + "word": "computer-generated", + "before": "コンピューター・ジェネレーテッド", + "after": "コンピュータージェネレーテッド", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 22775, + "cmudict": [ + { + "line": 23977, + "phones": [ + "K", + "AH0", + "M", + "P", + "Y", + "UW1", + "T", + "ER0", + "JH", + "EH1", + "N", + "ER0", + "EY2", + "T", + "AH0", + "D" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "computer-generated", + "split": [ + "computer", + "−", + "generated" + ], + "split_boundary_cost": -449, + "whole_word_boundary_cost": -83 + }, + { + "text": "computer-generatedを確認する。", + "split": [ + "computer", + "−", + "generated" + ], + "split_boundary_cost": -3470, + "whole_word_boundary_cost": -3104 + }, + { + "text": "これはcomputer-generatedです。", + "split": [ + "computer", + "−", + "generated" + ], + "split_boundary_cost": -810, + "whole_word_boundary_cost": -444 + } + ] + } + }, + "cost_before": 1066, + "cost_after": 699 + }, + { + "word": "daughters-in-law", + "before": "ドーターズ・イン・ロー", + "after": "ドーターズインロー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 27386, + "cmudict": [ + { + "line": 28836, + "phones": [ + "D", + "AO1", + "T", + "ER0", + "Z", + "IH0", + "N", + "L", + "AO2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "daughters-in-law", + "split": [ + "daughters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -955, + "whole_word_boundary_cost": -184 + }, + { + "text": "daughters-in-lawを確認する。", + "split": [ + "daughters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -3976, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはdaughters-in-lawです。", + "split": [ + "daughters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1316, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": 193 + }, + { + "word": "diaz-calderon", + "before": "ディアス・カルデロン", + "after": "ディアスカルデロン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 30206, + "cmudict": [ + { + "line": 31792, + "phones": [ + "D", + "IY1", + "AE2", + "Z", + "K", + "AE2", + "L", + "D", + "ER0", + "OW1", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "diaz-calderon", + "split": [ + "diaz", + "−", + "calderon" + ], + "split_boundary_cost": -713, + "whole_word_boundary_cost": -336 + }, + { + "text": "diaz-calderonを確認する。", + "split": [ + "diaz", + "−", + "calder", + "on" + ], + "split_boundary_cost": -5193, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはdiaz-calderonです。", + "split": [ + "diaz", + "−", + "calder", + "on" + ], + "split_boundary_cost": -2368, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": -1024 + }, + { + "word": "diego-garcia", + "before": "ディエゴ・ガルシア", + "after": "ディエゴガルシア", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 30357, + "cmudict": [ + { + "line": 31952, + "phones": [ + "D", + "IY0", + "EY1", + "G", + "OW0", + "G", + "AA2", + "R", + "S", + "IY1", + "AH0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "diego-garcia", + "split": [ + "diego", + "−", + "garcia" + ], + "split_boundary_cost": -764, + "whole_word_boundary_cost": -387 + }, + { + "text": "diego-garciaを確認する。", + "split": [ + "diego", + "−", + "garcia" + ], + "split_boundary_cost": -3785, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはdiego-garciaです。", + "split": [ + "diego", + "−", + "garcia" + ], + "split_boundary_cost": -1125, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 384 + }, + { + "word": "dumb-ass", + "before": "ダム・アス", + "after": "ダムアス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 33483, + "cmudict": [ + { + "line": 35330, + "phones": [ + "D", + "AH1", + "M", + "B", + "AE2", + "S" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "dumb-ass", + "split": [ + "dumb", + "−", + "ass" + ], + "split_boundary_cost": -982, + "whole_word_boundary_cost": -593 + }, + { + "text": "dumb-assを確認する。", + "split": [ + "dumb", + "−", + "ass" + ], + "split_boundary_cost": -4003, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはdumb-assです。", + "split": [ + "dumb", + "−", + "ass" + ], + "split_boundary_cost": -1343, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 166 + }, + { + "word": "el-greco", + "before": "エル・グレコ", + "after": "エルグレコ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 34920, + "cmudict": [ + { + "line": 36875, + "phones": [ + "EH1", + "L", + "G", + "R", + "EH1", + "K", + "OW0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "el-greco", + "split": [ + "el", + "−", + "greco" + ], + "split_boundary_cost": -988, + "whole_word_boundary_cost": -593 + }, + { + "text": "el-grecoを確認する。", + "split": [ + "el", + "−", + "greco" + ], + "split_boundary_cost": -4009, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはel-grecoです。", + "split": [ + "el", + "−", + "greco" + ], + "split_boundary_cost": -1349, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 160 + }, + { + "word": "ex-boyfriends", + "before": "エクス・ボーイフレンズ", + "after": "エクスボーイフレンズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 37626, + "cmudict": [ + { + "line": 39871, + "phones": [ + "EH1", + "K", + "S", + "B", + "OY1", + "F", + "R", + "EH2", + "N", + "D", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "ex-boyfriends", + "split": [ + "ex", + "−", + "boyfriends" + ], + "split_boundary_cost": -727, + "whole_word_boundary_cost": -336 + }, + { + "text": "ex-boyfriendsを確認する。", + "split": [ + "ex", + "−", + "boyfriends" + ], + "split_boundary_cost": -3748, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはex-boyfriendsです。", + "split": [ + "ex", + "−", + "boyfriends" + ], + "split_boundary_cost": -1088, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": 421 + }, + { + "word": "ex-girlfriends", + "before": "エックス・ガールフレンズ", + "after": "エックスガールフレンズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 37629, + "cmudict": [ + { + "line": 39874, + "phones": [ + "EH1", + "K", + "Z", + "G", + "ER1", + "L", + "F", + "R", + "EH2", + "N", + "D", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "ex-girlfriends", + "split": [ + "ex", + "−", + "girlfriends" + ], + "split_boundary_cost": -675, + "whole_word_boundary_cost": -286 + }, + { + "text": "ex-girlfriendsを確認する。", + "split": [ + "ex", + "−", + "girlfriends" + ], + "split_boundary_cost": -3696, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはex-girlfriendsです。", + "split": [ + "ex", + "−", + "girlfriends" + ], + "split_boundary_cost": -1036, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 473 + }, + { + "word": "face-to-face", + "before": "フェイス・トゥ・フェイス", + "after": "フェイストゥフェイス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 38359, + "cmudict": [ + { + "line": 40655, + "phones": [ + "F", + "EY1", + "S", + "T", + "UW0", + "F", + "EY1", + "S" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "face-to-face", + "split": [ + "face", + "−", + "to", + "−", + "face" + ], + "split_boundary_cost": -1162, + "whole_word_boundary_cost": -387 + }, + { + "text": "face-to-faceを確認する。", + "split": [ + "face", + "−", + "to", + "−", + "face" + ], + "split_boundary_cost": -4183, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはface-to-faceです。", + "split": [ + "face", + "−", + "to", + "−", + "face" + ], + "split_boundary_cost": -1523, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -14 + }, + { + "word": "foot-and-mouth", + "before": "フート・アンド・マウス", + "after": "フートアンドマウス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 41481, + "cmudict": [ + { + "line": 43925, + "phones": [ + "F", + "UH2", + "T", + "AH0", + "N", + "D", + "M", + "AW1", + "TH" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "foot-and-mouth", + "split": [ + "foot", + "−", + "and", + "−", + "mouth" + ], + "split_boundary_cost": -1048, + "whole_word_boundary_cost": -286 + }, + { + "text": "foot-and-mouthを確認する。", + "split": [ + "foot", + "−", + "and", + "−", + "mouth" + ], + "split_boundary_cost": -4069, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはfoot-and-mouthです。", + "split": [ + "foot", + "−", + "and", + "−", + "mouth" + ], + "split_boundary_cost": -1409, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 100 + }, + { + "word": "french-polynesia", + "before": "フランス・ポリネシア", + "after": "フランスポリネシア", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 42488, + "cmudict": [ + { + "line": 45016, + "phones": [ + "F", + "R", + "EH1", + "N", + "CH", + "P", + "AA2", + "L", + "IH0", + "N", + "IY1", + "ZH", + "AH0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "french-polynesia", + "split": [ + "french", + "−", + "polynesia" + ], + "split_boundary_cost": -554, + "whole_word_boundary_cost": -184 + }, + { + "text": "french-polynesiaを確認する。", + "split": [ + "french", + "−", + "polynesia" + ], + "split_boundary_cost": -3575, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはfrench-polynesiaです。", + "split": [ + "french", + "−", + "polynesia" + ], + "split_boundary_cost": -915, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": 594 + }, + { + "word": "goings-ons", + "before": "ゴーイングス・オンズ", + "after": "ゴーイングスオンズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 46264, + "cmudict": [ + { + "line": 48967, + "phones": [ + "G", + "OW1", + "IH0", + "NG", + "Z", + "AA1", + "N", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "goings-ons", + "split": [ + "goings", + "−", + "ons" + ], + "split_boundary_cost": -875, + "whole_word_boundary_cost": -490 + }, + { + "text": "goings-onsを確認する。", + "split": [ + "goings", + "−", + "ons" + ], + "split_boundary_cost": -3896, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはgoings-onsです。", + "split": [ + "goings", + "−", + "ons" + ], + "split_boundary_cost": -1236, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": 273 + }, + { + "word": "great-grandfather", + "before": "グレイト・グランドファーザー", + "after": "グレイトグランドファーザー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 47373, + "cmudict": [ + { + "line": 50131, + "phones": [ + "G", + "R", + "EY2", + "T", + "G", + "R", + "AE1", + "N", + "D", + "F", + "AA2", + "DH", + "ER0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "great-grandfather", + "split": [ + "great", + "−", + "grandfather" + ], + "split_boundary_cost": -504, + "whole_word_boundary_cost": -133 + }, + { + "text": "great-grandfatherを確認する。", + "split": [ + "great", + "−", + "grandfather" + ], + "split_boundary_cost": -3525, + "whole_word_boundary_cost": -3154 + }, + { + "text": "これはgreat-grandfatherです。", + "split": [ + "great", + "−", + "grandfather" + ], + "split_boundary_cost": -865, + "whole_word_boundary_cost": -494 + } + ] + } + }, + "cost_before": 1016, + "cost_after": 644 + }, + { + "word": "hit-and-run", + "before": "ヒット・アンド・ラン", + "after": "ヒットアンドラン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 52625, + "cmudict": [ + { + "line": 55595, + "phones": [ + "HH", + "IH1", + "T", + "AE0", + "N", + "R", + "AH2", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "hit-and-run", + "split": [ + "hit", + "−", + "and", + "−", + "run" + ], + "split_boundary_cost": -1216, + "whole_word_boundary_cost": -438 + }, + { + "text": "hit-and-runを確認する。", + "split": [ + "hit", + "−", + "and", + "−", + "run" + ], + "split_boundary_cost": -4237, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはhit-and-runです。", + "split": [ + "hit", + "−", + "and", + "−", + "run" + ], + "split_boundary_cost": -1577, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -68 + }, + { + "word": "homo-erectus", + "before": "ホモ・エレクトゥス", + "after": "ホモエレクトゥス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 53344, + "cmudict": [ + { + "line": 56339, + "phones": [ + "HH", + "OW1", + "M", + "OW0", + "IH0", + "R", + "EH1", + "K", + "T", + "AH0", + "S" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "homo-erectus", + "split": [ + "homo", + "−", + "erect", + "us" + ], + "split_boundary_cost": -437, + "whole_word_boundary_cost": -387 + }, + { + "text": "homo-erectusを確認する。", + "split": [ + "homo", + "−", + "erect", + "us" + ], + "split_boundary_cost": -3458, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはhomo-erectusです。", + "split": [ + "homo", + "−", + "erect", + "us" + ], + "split_boundary_cost": -798, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 711 + }, + { + "word": "ieee", + "before": "アイ トリプルイー", + "after": "アイトリプルイー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 55189, + "cmudict": [ + { + "line": 58307, + "phones": [ + "AY2", + "T", + "R", + "IH2", + "P", + "L", + "AH0", + "IY1" + ] + } + ] + } + }, + { + "word": "jean-luc", + "before": "ジャン=リュック", + "after": "ジャンリュック", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 58630, + "cmudict": [ + { + "line": 62084, + "phones": [ + "ZH", + "EH0", + "N", + "L", + "UW1", + "K" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "jean-luc", + "split": [ + "jean", + "−", + "luc" + ], + "split_boundary_cost": -982, + "whole_word_boundary_cost": -593 + }, + { + "text": "jean-lucを確認する。", + "split": [ + "jean", + "−", + "luc" + ], + "split_boundary_cost": -4003, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはjean-lucです。", + "split": [ + "jean", + "−", + "luc" + ], + "split_boundary_cost": -1343, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 166 + }, + { + "word": "jean-michel", + "before": "ジャン・ミシェル", + "after": "ジャンミシェル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 58631, + "cmudict": [ + { + "line": 62085, + "phones": [ + "ZH", + "AA2", + "N", + "M", + "AH0", + "SH", + "EH1", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "jean-michel", + "split": [ + "jean", + "−", + "michel" + ], + "split_boundary_cost": -818, + "whole_word_boundary_cost": -438 + }, + { + "text": "jean-michelを確認する。", + "split": [ + "jean", + "−", + "michel" + ], + "split_boundary_cost": -3839, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはjean-michelです。", + "split": [ + "jean", + "−", + "michel" + ], + "split_boundary_cost": -1179, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": 330 + }, + { + "word": "jean-michele", + "before": "ジャン・ミッシェル", + "after": "ジャンミッシェル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 58632, + "cmudict": [ + { + "line": 62086, + "phones": [ + "ZH", + "AA2", + "N", + "M", + "AH0", + "SH", + "EH1", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "jean-michele", + "split": [ + "jean", + "−", + "michele" + ], + "split_boundary_cost": -765, + "whole_word_boundary_cost": -387 + }, + { + "text": "jean-micheleを確認する。", + "split": [ + "jean", + "−", + "michele" + ], + "split_boundary_cost": -3786, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはjean-micheleです。", + "split": [ + "jean", + "−", + "michele" + ], + "split_boundary_cost": -1126, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 383 + }, + { + "word": "juarez-espin", + "before": "フアレス・エスピン", + "after": "フアレスエスピン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 59402, + "cmudict": [ + { + "line": 62891, + "phones": [ + "W", + "AA2", + "R", + "EH1", + "Z", + "EH1", + "S", + "P", + "IH0", + "N" + ] + }, + { + "line": 62892, + "phones": [ + "HH", + "W", + "AA2", + "R", + "EH1", + "Z", + "EH1", + "S", + "P", + "IH0", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "juarez-espin", + "split": [ + "juarez", + "−", + "e", + "spin" + ], + "split_boundary_cost": -453, + "whole_word_boundary_cost": -387 + }, + { + "text": "juarez-espinを確認する。", + "split": [ + "juarez", + "−", + "e", + "spin" + ], + "split_boundary_cost": -3474, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはjuarez-espinです。", + "split": [ + "juarez", + "−", + "e", + "spin" + ], + "split_boundary_cost": -814, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 695 + }, + { + "word": "kebab-n-curry", + "before": "ケバブ・ン・カレー", + "after": "ケバブンカレー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 60527, + "cmudict": [ + { + "line": 64078, + "phones": [ + "K", + "IH0", + "B", + "AA2", + "B", + "AH0", + "N", + "K", + "ER1", + "IY0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "kebab-n-curry", + "split": [ + "kebab", + "−", + "n", + "−", + "curry" + ], + "split_boundary_cost": -1123, + "whole_word_boundary_cost": -336 + }, + { + "text": "kebab-n-curryを確認する。", + "split": [ + "kebab", + "−", + "n", + "−", + "curry" + ], + "split_boundary_cost": -4144, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはkebab-n-curryです。", + "split": [ + "kebab", + "−", + "n", + "−", + "curry" + ], + "split_boundary_cost": -1484, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": 25 + }, + { + "word": "life-affirming", + "before": "ライフ・アファーミング", + "after": "ライフアファーミング", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 66709, + "cmudict": [ + { + "line": 70485, + "phones": [ + "L", + "AY1", + "F", + "AH0", + "F", + "ER2", + "M", + "IH0", + "NG" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "life-affirming", + "split": [ + "life", + "−", + "affirming" + ], + "split_boundary_cost": -661, + "whole_word_boundary_cost": -286 + }, + { + "text": "life-affirmingを確認する。", + "split": [ + "life", + "−", + "affirming" + ], + "split_boundary_cost": -3682, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはlife-affirmingです。", + "split": [ + "life", + "−", + "affirming" + ], + "split_boundary_cost": -1022, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 487 + }, + { + "word": "might-have-beens", + "before": "マイト・ハブ・ビーンズ", + "after": "マイトハブビーンズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 74827, + "cmudict": [ + { + "line": 78846, + "phones": [ + "M", + "AY1", + "T", + "AH0", + "V", + "B", + "IH2", + "N", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "might-have-beens", + "split": [ + "might", + "−", + "have", + "−", + "beens" + ], + "split_boundary_cost": -937, + "whole_word_boundary_cost": -184 + }, + { + "text": "might-have-beensを確認する。", + "split": [ + "might", + "−", + "have", + "−", + "beens" + ], + "split_boundary_cost": -3958, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはmight-have-beensです。", + "split": [ + "might", + "−", + "have", + "−", + "beens" + ], + "split_boundary_cost": -1298, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": 211 + }, + { + "word": "modus-operandi", + "before": "モーダス・オペランディ", + "after": "モーダスオペランディ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 76173, + "cmudict": [ + { + "line": 80286, + "phones": [ + "M", + "OW1", + "D", + "AH0", + "AO2", + "P", + "ER0", + "AE1", + "N", + "D", + "IY0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "modus-operandi", + "split": [ + "modus", + "−", + "operandi" + ], + "split_boundary_cost": -659, + "whole_word_boundary_cost": -286 + }, + { + "text": "modus-operandiを確認する。", + "split": [ + "modus", + "−", + "operandi" + ], + "split_boundary_cost": -3680, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはmodus-operandiです。", + "split": [ + "modus", + "−", + "operandi" + ], + "split_boundary_cost": -1020, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 489 + }, + { + "word": "moholy-nagy", + "before": "モホリ・ナギ", + "after": "モホリナギ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 76250, + "cmudict": [ + { + "line": 80364, + "phones": [ + "M", + "OW0", + "HH", + "OW1", + "L", + "IY0", + "N", + "EY1", + "G", + "IY0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "moholy-nagy", + "split": [ + "mo", + "holy", + "−", + "nagy" + ], + "split_boundary_cost": -491, + "whole_word_boundary_cost": -438 + }, + { + "text": "moholy-nagyを確認する。", + "split": [ + "mo", + "holy", + "−", + "nagy" + ], + "split_boundary_cost": -3512, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはmoholy-nagyです。", + "split": [ + "mo", + "holy", + "−", + "nagy" + ], + "split_boundary_cost": -852, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": 657 + }, + { + "word": "moseley-braun", + "before": "モーズリー・ブラウン", + "after": "モーズリーブラウン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 77182, + "cmudict": [ + { + "line": 81338, + "phones": [ + "M", + "OW1", + "Z", + "L", + "IY0", + "B", + "R", + "AA1", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "moseley-braun", + "split": [ + "moseley", + "−", + "braun" + ], + "split_boundary_cost": -711, + "whole_word_boundary_cost": -336 + }, + { + "text": "moseley-braunを確認する。", + "split": [ + "moseley", + "−", + "braun" + ], + "split_boundary_cost": -3732, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはmoseley-braunです。", + "split": [ + "moseley", + "−", + "braun" + ], + "split_boundary_cost": -1072, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": 437 + }, + { + "word": "non-communists", + "before": "ノン・コミュニスツ", + "after": "ノンコミュニスツ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 80395, + "cmudict": [ + { + "line": 84731, + "phones": [ + "N", + "AA1", + "N", + "K", + "AA1", + "M", + "Y", + "UW0", + "N", + "IH0", + "S", + "T", + "S" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "non-communists", + "split": [ + "n", + "on", + "−", + "communists" + ], + "split_boundary_cost": -1374, + "whole_word_boundary_cost": -286 + }, + { + "text": "non-communistsを確認する。", + "split": [ + "n", + "on", + "−", + "communists" + ], + "split_boundary_cost": -4395, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはnon-communistsです。", + "split": [ + "n", + "on", + "−", + "communists" + ], + "split_boundary_cost": -1735, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": -226 + }, + { + "word": "non-discrimination", + "before": "ノン・ディスクリミネーション", + "after": "ノンディスクリミネーション", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 80414, + "cmudict": [ + { + "line": 84750, + "phones": [ + "N", + "AA0", + "N", + "D", + "IH2", + "S", + "K", + "R", + "IH0", + "M", + "IH0", + "N", + "EY1", + "SH", + "AH0", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "non-discrimination", + "split": [ + "n", + "on", + "−", + "discrimination" + ], + "split_boundary_cost": -1170, + "whole_word_boundary_cost": -83 + }, + { + "text": "non-discriminationを確認する。", + "split": [ + "n", + "on", + "−", + "dis", + "crimi", + "n", + "ati", + "on" + ], + "split_boundary_cost": -4657, + "whole_word_boundary_cost": -3104 + }, + { + "text": "これはnon-discriminationです。", + "split": [ + "n", + "on", + "−", + "dis", + "crimi", + "n", + "ati", + "on" + ], + "split_boundary_cost": -1832, + "whole_word_boundary_cost": -444 + } + ] + } + }, + "cost_before": 1066, + "cost_after": -488 + }, + { + "word": "non-governmental", + "before": "ノン・ガバメントゥル", + "after": "ノンガバメントゥル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 80436, + "cmudict": [ + { + "line": 84773, + "phones": [ + "N", + "AA0", + "N", + "G", + "AH2", + "V", + "ER0", + "N", + "M", + "EH1", + "N", + "T", + "AH0", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "non-governmental", + "split": [ + "n", + "on", + "−", + "governmental" + ], + "split_boundary_cost": -1271, + "whole_word_boundary_cost": -184 + }, + { + "text": "non-governmentalを確認する。", + "split": [ + "n", + "on", + "−", + "governmental" + ], + "split_boundary_cost": -4292, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはnon-governmentalです。", + "split": [ + "n", + "on", + "−", + "governmental" + ], + "split_boundary_cost": -1632, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": -123 + }, + { + "word": "non-transferable", + "before": "ノン・トランスファラブル", + "after": "ノントランスファラブル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 80520, + "cmudict": [ + { + "line": 84857, + "phones": [ + "N", + "AA2", + "N", + "T", + "R", + "AE2", + "N", + "S", + "F", + "ER1", + "AH0", + "B", + "AH0", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "non-transferable", + "split": [ + "n", + "on", + "−", + "transferable" + ], + "split_boundary_cost": -1271, + "whole_word_boundary_cost": -184 + }, + { + "text": "non-transferableを確認する。", + "split": [ + "n", + "on", + "−", + "transferable" + ], + "split_boundary_cost": -4292, + "whole_word_boundary_cost": -3205 + }, + { + "text": "これはnon-transferableです。", + "split": [ + "n", + "on", + "−", + "transferable" + ], + "split_boundary_cost": -1632, + "whole_word_boundary_cost": -545 + } + ] + } + }, + "cost_before": 965, + "cost_after": -123 + }, + { + "word": "o'canas", + "before": "オ'カナス", + "after": "オカナス", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 81425, + "cmudict": [ + { + "line": 85780, + "phones": [ + "OW0", + "K", + "AE1", + "N", + "AH0", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "o'canas", + "split": [ + "o", + "’", + "canas" + ], + "split_boundary_cost": -2509, + "whole_word_boundary_cost": -645 + }, + { + "text": "o'canasを確認する。", + "split": [ + "o", + "’", + "canas" + ], + "split_boundary_cost": -5530, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはo'canasです。", + "split": [ + "o", + "’", + "canas" + ], + "split_boundary_cost": -2870, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": -1361 + }, + { + "word": "off-duty", + "before": "オフ・デューティ", + "after": "オフデューティ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 82001, + "cmudict": [ + { + "line": 86396, + "phones": [ + "AO2", + "F", + "D", + "UW1", + "T", + "IY0" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "off-duty", + "split": [ + "off", + "−", + "duty" + ], + "split_boundary_cost": -982, + "whole_word_boundary_cost": -593 + }, + { + "text": "off-dutyを確認する。", + "split": [ + "off", + "−", + "duty" + ], + "split_boundary_cost": -4003, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはoff-dutyです。", + "split": [ + "off", + "−", + "duty" + ], + "split_boundary_cost": -1343, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 166 + }, + { + "word": "one-on-one", + "before": "ワン・オン・ワン", + "after": "ワンオンワン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 82544, + "cmudict": [ + { + "line": 86969, + "phones": [ + "W", + "AH1", + "N", + "AH0", + "N", + "AA1", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "one-on-one", + "split": [ + "one", + "−", + "on", + "−", + "one" + ], + "split_boundary_cost": -1435, + "whole_word_boundary_cost": -490 + }, + { + "text": "one-on-oneを確認する。", + "split": [ + "one", + "−", + "on", + "−", + "one" + ], + "split_boundary_cost": -4456, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはone-on-oneです。", + "split": [ + "on", + "e", + "−", + "on", + "−", + "one" + ], + "split_boundary_cost": -1799, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": -290 + }, + { + "word": "one-year", + "before": "ワン・イヤー", + "after": "ワンイヤー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 82551, + "cmudict": [ + { + "line": 86976, + "phones": [ + "W", + "AH1", + "N", + "Y", + "IH2", + "R" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "one-year", + "split": [ + "one", + "−", + "year" + ], + "split_boundary_cost": -982, + "whole_word_boundary_cost": -593 + }, + { + "text": "one-yearを確認する。", + "split": [ + "one", + "−", + "year" + ], + "split_boundary_cost": -4003, + "whole_word_boundary_cost": -3614 + }, + { + "text": "これはone-yearです。", + "split": [ + "on", + "e", + "−", + "year" + ], + "split_boundary_cost": -1346, + "whole_word_boundary_cost": -954 + } + ] + } + }, + "cost_before": 556, + "cost_after": 163 + }, + { + "word": "pee-pee", + "before": "ピー ピー", + "after": "ピーピー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 86039, + "cmudict": [ + { + "line": 90604, + "phones": [ + "P", + "IY1", + "P", + "IY1" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "pee-pee", + "split": [ + "pee", + "−", + "pee" + ], + "split_boundary_cost": -1039, + "whole_word_boundary_cost": -645 + }, + { + "text": "pee-peeを確認する。", + "split": [ + "pee", + "−", + "pee" + ], + "split_boundary_cost": -4060, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはpee-peeです。", + "split": [ + "pee", + "−", + "pee" + ], + "split_boundary_cost": -1400, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": 109 + }, + { + "word": "peeping", + "before": "ピー​​ピング", + "after": "ピーピング", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 86060, + "cmudict": [ + { + "line": 90625, + "phones": [ + "P", + "IY1", + "P", + "IH0", + "NG" + ] + } + ] + } + }, + { + "word": "peer-to-peer", + "before": "ピア・トゥ・ピア", + "after": "ピアトゥピア", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 86064, + "cmudict": [ + { + "line": 90629, + "phones": [ + "P", + "IH1", + "R", + "T", + "UW0", + "P", + "IH1", + "R" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "peer-to-peer", + "split": [ + "peer", + "−", + "to", + "−", + "peer" + ], + "split_boundary_cost": -1162, + "whole_word_boundary_cost": -387 + }, + { + "text": "peer-to-peerを確認する。", + "split": [ + "peer", + "−", + "to", + "−", + "peer" + ], + "split_boundary_cost": -4183, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはpeer-to-peerです。", + "split": [ + "peer", + "−", + "to", + "−", + "peer" + ], + "split_boundary_cost": -1523, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -14 + }, + { + "word": "rock-and-roll", + "before": "ロック・アンド・ロール", + "after": "ロックアンドロール", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 97692, + "cmudict": [ + { + "line": 103147, + "phones": [ + "R", + "AA1", + "K", + "AE1", + "N", + "D", + "R", + "OW1", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "rock-and-roll", + "split": [ + "rock", + "−", + "and", + "−", + "roll" + ], + "split_boundary_cost": -1102, + "whole_word_boundary_cost": -336 + }, + { + "text": "rock-and-rollを確認する。", + "split": [ + "rock", + "−", + "and", + "−", + "roll" + ], + "split_boundary_cost": -4123, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはrock-and-rollです。", + "split": [ + "rock", + "−", + "and", + "−", + "roll" + ], + "split_boundary_cost": -1463, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": 46 + }, + { + "word": "rpm", + "before": "アールピー エム", + "after": "アールピーエム", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 98755, + "cmudict": [ + { + "line": 104243, + "phones": [ + "AA1", + "R", + "P", + "IY1", + "EH1", + "M" + ] + } + ] + } + }, + { + "word": "saint-saens", + "before": "サン=サーンズ", + "after": "サンサーンズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 99692, + "cmudict": [ + { + "line": 105215, + "phones": [ + "S", + "EY1", + "N", + "T", + "S", + "AA1", + "N", + "Z" + ] + } + ] + } + }, + { + "word": "science-fiction", + "before": "サイエンス・フィクション", + "after": "サイエンスフィクション", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 101931, + "cmudict": [ + { + "line": 107522, + "phones": [ + "S", + "AY1", + "AH0", + "N", + "S", + "F", + "IH2", + "K", + "SH", + "AH0", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "science-fiction", + "split": [ + "science", + "−", + "fiction" + ], + "split_boundary_cost": -605, + "whole_word_boundary_cost": -235 + }, + { + "text": "science-fictionを確認する。", + "split": [ + "science", + "−", + "fi", + "c", + "ti", + "on" + ], + "split_boundary_cost": -4458, + "whole_word_boundary_cost": -3256 + }, + { + "text": "これはscience-fictionです。", + "split": [ + "science", + "−", + "fi", + "c", + "ti", + "on" + ], + "split_boundary_cost": -1633, + "whole_word_boundary_cost": -596 + } + ] + } + }, + "cost_before": 914, + "cost_after": -289 + }, + { + "word": "serbo-croatian", + "before": "セルボ・クロアチアン", + "after": "セルボクロアチアン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 103356, + "cmudict": [ + { + "line": 109043, + "phones": [ + "S", + "ER1", + "B", + "OW0", + "K", + "R", + "OW2", + "EY0", + "SH", + "AH0", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "serbo-croatian", + "split": [ + "serb", + "o", + "−", + "croatian" + ], + "split_boundary_cost": -348, + "whole_word_boundary_cost": -286 + }, + { + "text": "serbo-croatianを確認する。", + "split": [ + "serb", + "o", + "−", + "croatian" + ], + "split_boundary_cost": -3369, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはserbo-croatianです。", + "split": [ + "serb", + "o", + "−", + "croatian" + ], + "split_boundary_cost": -709, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 800 + }, + { + "word": "sheath-knife", + "before": "シース・ナイフ", + "after": "シースナイフ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 104082, + "cmudict": [ + { + "line": 109794, + "phones": [ + "SH", + "IY1", + "TH", + "N", + "AY2", + "F" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "sheath-knife", + "split": [ + "sheath", + "−", + "knife" + ], + "split_boundary_cost": -764, + "whole_word_boundary_cost": -387 + }, + { + "text": "sheath-knifeを確認する。", + "split": [ + "sheath", + "−", + "knife" + ], + "split_boundary_cost": -3785, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはsheath-knifeです。", + "split": [ + "sheath", + "−", + "knife" + ], + "split_boundary_cost": -1125, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 384 + }, + { + "word": "sisters-in-law", + "before": "シスターズ イン ロウ", + "after": "シスターズインロウ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 105769, + "cmudict": [ + { + "line": 111530, + "phones": [ + "S", + "IH1", + "S", + "T", + "ER0", + "Z", + "IH0", + "N", + "L", + "AO2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "sisters-in-law", + "split": [ + "sisters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1059, + "whole_word_boundary_cost": -286 + }, + { + "text": "sisters-in-lawを確認する。", + "split": [ + "sisters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -4080, + "whole_word_boundary_cost": -3307 + }, + { + "text": "これはsisters-in-lawです。", + "split": [ + "sisters", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1420, + "whole_word_boundary_cost": -647 + } + ] + } + }, + "cost_before": 863, + "cost_after": 89 + }, + { + "word": "sons-in-law", + "before": "サンズ・イン・ロー", + "after": "サンズインロー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 107531, + "cmudict": [ + { + "line": 113336, + "phones": [ + "S", + "AH1", + "N", + "IH0", + "N", + "L", + "AO2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "sons-in-law", + "split": [ + "s", + "on", + "s", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1353, + "whole_word_boundary_cost": -438 + }, + { + "text": "sons-in-lawを確認する。", + "split": [ + "s", + "on", + "s", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -4374, + "whole_word_boundary_cost": -3459 + }, + { + "text": "これはsons-in-lawです。", + "split": [ + "s", + "on", + "s", + "−", + "in", + "−", + "law" + ], + "split_boundary_cost": -1714, + "whole_word_boundary_cost": -799 + } + ] + } + }, + "cost_before": 711, + "cost_after": -205 + }, + { + "word": "soviet-union", + "before": "ソビエト・ユニオン", + "after": "ソビエトユニオン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 107810, + "cmudict": [ + { + "line": 113624, + "phones": [ + "S", + "OW1", + "V", + "IY0", + "EH2", + "T", + "Y", + "UW1", + "N", + "Y", + "AH0", + "N" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "soviet-union", + "split": [ + "soviet", + "−", + "union" + ], + "split_boundary_cost": -764, + "whole_word_boundary_cost": -387 + }, + { + "text": "soviet-unionを確認する。", + "split": [ + "soviet", + "−", + "uni", + "on" + ], + "split_boundary_cost": -5250, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはsoviet-unionです。", + "split": [ + "soviet", + "−", + "uni", + "on" + ], + "split_boundary_cost": -2425, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": -1081 + }, + { + "word": "speaking", + "before": "スピー​​キング", + "after": "スピーキング", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 108047, + "cmudict": [ + { + "line": 113873, + "phones": [ + "S", + "P", + "IY1", + "K", + "IH0", + "NG" + ] + } + ] + } + }, + { + "word": "twenty-three", + "before": "トゥエンティー・スリー", + "after": "トゥエンティースリー", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 118218, + "cmudict": [ + { + "line": 124490, + "phones": [ + "T", + "W", + "EH1", + "N", + "T", + "IY0", + "TH", + "R", + "IY2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "twenty-three", + "split": [ + "twenty", + "−", + "three" + ], + "split_boundary_cost": -764, + "whole_word_boundary_cost": -387 + }, + { + "text": "twenty-threeを確認する。", + "split": [ + "twenty", + "−", + "three" + ], + "split_boundary_cost": -3785, + "whole_word_boundary_cost": -3408 + }, + { + "text": "これはtwenty-threeです。", + "split": [ + "twenty", + "−", + "three" + ], + "split_boundary_cost": -1125, + "whole_word_boundary_cost": -748 + } + ] + } + }, + "cost_before": 762, + "cost_after": 384 + }, + { + "word": "two-day", + "before": "ツー・デイ", + "after": "ツーデイ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 118283, + "cmudict": [ + { + "line": 124557, + "phones": [ + "T", + "UW1", + "D", + "EY2" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "two-day", + "split": [ + "two", + "−", + "day" + ], + "split_boundary_cost": -1039, + "whole_word_boundary_cost": -645 + }, + { + "text": "two-dayを確認する。", + "split": [ + "two", + "−", + "day" + ], + "split_boundary_cost": -4060, + "whole_word_boundary_cost": -3666 + }, + { + "text": "これはtwo-dayです。", + "split": [ + "two", + "−", + "day" + ], + "split_boundary_cost": -1400, + "whole_word_boundary_cost": -1006 + } + ] + } + }, + "cost_before": 504, + "cost_after": 109 + }, + { + "word": "two-dimensional", + "before": "ツー・ディメンショナル", + "after": "ツーディメンショナル", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 118284, + "cmudict": [ + { + "line": 124558, + "phones": [ + "T", + "UW2", + "D", + "IH0", + "M", + "EH1", + "N", + "SH", + "AH0", + "N", + "AH0", + "L" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "two-dimensional", + "split": [ + "two", + "−", + "dimensional" + ], + "split_boundary_cost": -615, + "whole_word_boundary_cost": -235 + }, + { + "text": "two-dimensionalを確認する。", + "split": [ + "two", + "−", + "dimensional" + ], + "split_boundary_cost": -3636, + "whole_word_boundary_cost": -3256 + }, + { + "text": "これはtwo-dimensionalです。", + "split": [ + "two", + "−", + "dimensional" + ], + "split_boundary_cost": -976, + "whole_word_boundary_cost": -596 + } + ] + } + }, + "cost_before": 914, + "cost_after": 533 + }, + { + "word": "two-thirds", + "before": "トゥー・サーズ", + "after": "トゥーサーズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 118285, + "cmudict": [ + { + "line": 124559, + "phones": [ + "T", + "UW2", + "TH", + "ER1", + "D", + "Z" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "two-thirds", + "split": [ + "two", + "−", + "thirds" + ], + "split_boundary_cost": -875, + "whole_word_boundary_cost": -490 + }, + { + "text": "two-thirdsを確認する。", + "split": [ + "two", + "−", + "thirds" + ], + "split_boundary_cost": -3896, + "whole_word_boundary_cost": -3511 + }, + { + "text": "これはtwo-thirdsです。", + "split": [ + "two", + "−", + "thirds" + ], + "split_boundary_cost": -1236, + "whole_word_boundary_cost": -851 + } + ] + } + }, + "cost_before": 659, + "cost_after": 273 + }, + { + "word": "untruths", + "before": "アン​トルーズ", + "after": "アントルーズ", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 119705, + "cmudict": [ + { + "line": 126040, + "phones": [ + "AH0", + "N", + "T", + "R", + "UW1", + "TH", + "S" + ] + }, + { + "line": 126041, + "phones": [ + "AH0", + "N", + "T", + "R", + "UW1", + "DH", + "S" + ] + } + ] + } + }, + { + "word": "whiz-zip-bang", + "before": "ウィズ・ジップ・バン", + "after": "ウィズジップバン", + "reason": "Remove kana component separators or invisible format controls; retain the exact kana sequence and valid existing accent nucleus. This does not independently certify the lexical transcription.", + "kind": "representation_repair", + "evidence": { + "baseline_line": 124599, + "cmudict": [ + { + "line": 131350, + "phones": [ + "W", + "IH1", + "Z", + "Z", + "IH1", + "P", + "B", + "AE1", + "NG" + ] + } + ], + "native_cost_calibration": { + "runtime": "0.4.1.post9", + "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", + "observations": [ + { + "text": "whiz-zip-bang", + "split": [ + "whiz", + "−", + "zip", + "−", + "bang" + ], + "split_boundary_cost": -1102, + "whole_word_boundary_cost": -336 + }, + { + "text": "whiz-zip-bangを確認する。", + "split": [ + "whiz", + "−", + "zip", + "−", + "bang" + ], + "split_boundary_cost": -4123, + "whole_word_boundary_cost": -3357 + }, + { + "text": "これはwhiz-zip-bangです。", + "split": [ + "whiz", + "−", + "zip", + "−", + "bang" + ], + "split_boundary_cost": -1463, + "whole_word_boundary_cost": -697 + } + ] + } + }, + "cost_before": 813, + "cost_after": 46 + } + ], + "review": { + "date": "2026-10-16", + "baseline_version": "2026.10.16.2", + "baseline_sha256": "91b3a3c1aa37b7eea96b5815b40e48c0f6a15d02136f956d9deb340df35131f2", + "baseline_review_entries": 317, + "policy": "Exact bad-reading repairs preserve other lexical alternatives. Unconfirmed lexical transcriptions or accent nuclei are retained. Native compilation and labels do not replace human listening.", + "cost_calibration_completed": true, + "lexical_repairs": 94, + "representation_repairs": 65, + "retained_entries": 158, + "calibrated_entry_costs": 109, + "evidence_files": { + "cmudict": { + "path": "GPT_SoVITS/text/cmudict-fast.rep", + "sha256": "d6701402ea9249e4ae1e23ab8a4c216e847d49a30bd9bb206a33b3c7830f0a4a" + }, + "native_dictionary": { + "runtime": "pyopenjtalk-plus 0.4.1.post9", + "files": { + "sys.dic": "0328ca62355100aba6df13af56912ad88cf10246e2a286005b89cb8ce2cef652", + "matrix.bin": "62fd16b4f64c851d5dc352ef0d5740c5fc83ddc7c203b2b0b1fc5271969a14ce", + "char.bin": "888ee94c5a8a7a26d24ab3f1b7155441351954fd51ea06b4a2f78bd742492b2f" + } + } + } + } +} diff --git a/tools/external_assets.py b/tools/external_assets.py index 871528c6..f99fcd3b 100644 --- a/tools/external_assets.py +++ b/tools/external_assets.py @@ -85,7 +85,13 @@ def _resolved_source_files( spec: AssetPackSpec, ) -> tuple[Path, ...]: asset_root = project_root / "assets" - if spec.validator == "tts_reference_pack": + if spec.validator == "japanese_dictionary": + from tts.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack + try: + files = set(load_dictionary_pack(asset_path(asset_root, spec.trees[0])).files()) + except JapaneseDictionaryError as exc: + raise AssetPackageError(f"{spec.id}: {exc}") from exc + elif spec.validator == "tts_reference_pack": from tts.reference_pack import ReferencePackError, load_reference_pack try: pack = load_reference_pack(asset_path(asset_root, spec.trees[0])) @@ -524,6 +530,7 @@ def main() -> int: print( f"{row['id']}: {row['state']} " f"({row['present_required']}/{row['required_count']} required files)" + + (f" version={row['version']}" if row.get("version") else "") ) return 0 if args.command == "build": diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py new file mode 100644 index 00000000..55eb7e9f --- /dev/null +++ b/tools/prepare_japanese_dictionary.py @@ -0,0 +1,123 @@ +"""Prepare the pinned optional Japanese dictionary offline; never publish it.""" +from __future__ import annotations + +import argparse +import csv +import hashlib +import io +import json +from pathlib import Path +import sys +import unicodedata +from typing import Callable + +PROJECT_ROOT = Path(__file__).resolve().parents[1] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from tts.japanese_dictionary import ( # noqa: E402 + PACK_TREE, JapaneseDictionaryError, dictionary_rows, load_dictionary_pack, +) +from tools.audit_japanese_dictionary import native_mora_counter, native_surface_normalizer, normalize_rows # noqa: E402 + +RECIPE_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_source.json" +CORRECTIONS_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_corrections.csv" + + +def prepare_dictionary(source: Path, *, project_root: Path = PROJECT_ROOT, + recipe_path: Path = RECIPE_PATH, corrections_path: Path = CORRECTIONS_PATH, + overwrite: bool = False, count_mora: Callable[[str], int | None] | None = None, + normalize_surface: Callable[[str], str] | None = None) -> Path: + recipe = json.loads(Path(recipe_path).read_text(encoding="utf-8")) + data = Path(source).read_bytes().replace(b"\r\n", b"\n") + if hashlib.sha256(data).hexdigest() != recipe["source"]["sha256"]: + raise JapaneseDictionaryError("Source CSV differs from the pinned upstream; personal data must not be bundled") + rows = list(dictionary_rows(data)) + if len(rows) != recipe["source"]["entry_count"]: + raise JapaneseDictionaryError("Source entry count differs from the pinned upstream") + def normalize(value: str) -> str: + return unicodedata.normalize("NFKC", value).lower() + correction_data = Path(corrections_path).read_bytes().replace(b"\r\n", b"\n") + corrections = {normalize(row[0]): row for row in dictionary_rows(correction_data)} + before = {normalize(item["word"]): item["before"] for item in recipe["corrections"]} + if count_mora is None: + count_mora, runtime_version = native_mora_counter() + if runtime_version != recipe["normalization"]["native_runtime"]: + raise JapaneseDictionaryError("Prepare with the recipe's pinned pyopenjtalk-plus runtime") + normalize_surface = native_surface_normalizer() if normalize_surface is None else normalize_surface + else: + runtime_version = "injected" + # A repair targets the reviewed bad pronunciation, not every reading of + # the same surface. Preserve legitimate alternatives and their costs. + corrected = [] + repaired_surfaces = 0 + for row in rows: + word = normalize(row[0]) + expected = before.get(word) + matches = expected is not None and unicodedata.normalize("NFC", row[12]) == unicodedata.normalize("NFC", expected) + if word in corrections and matches: + row = list(corrections[word]) + if normalize_surface is not None: + surface = normalize_surface(row[0]) + repaired_surfaces += surface != row[0] + row[0] = surface + corrected.append(row) + existing = {normalize(row[0]) for row in rows} + for word, correction in corrections.items(): + if word not in existing: + row = list(correction) + if normalize_surface is not None: + surface = normalize_surface(row[0]) + repaired_surfaces += surface != row[0] + row[0] = surface + corrected.append(row) + corrected, normalization = normalize_rows(corrected, count_mora, normalize_surface) + normalization["repaired_surfaces_normalized"] += repaired_surfaces + buffer = io.StringIO(newline="") + csv.writer(buffer, lineterminator="\n").writerows(corrected) + derivative = buffer.getvalue().encode("utf-8") + metadata = {**recipe, "sha256": hashlib.sha256(derivative).hexdigest(), "entry_count": len(corrected), + "corrections_sha256": hashlib.sha256(correction_data).hexdigest(), + "normalization": {**recipe["normalization"], + "results": {key: value for key, value in normalization.items() if key != "unresolved"}}, + "native_runtime": runtime_version} + files = { + "userdict.csv": derivative, + "provenance.json": (json.dumps(metadata, ensure_ascii=False, indent=2) + "\n").encode("utf-8"), + "NOTICE.md": (PROJECT_ROOT / "LICENSES/JAPANESE-DICTIONARY.md").read_bytes().replace(b"\r\n", b"\n"), + "GPT-SoVITS-MIT.txt": (PROJECT_ROOT / "LICENSES/GPT-SoVITS-MIT.txt").read_bytes().replace(b"\r\n", b"\n"), + "CMUdict-BSD-2-Clause.txt": (PROJECT_ROOT / "LICENSES/CMUdict-BSD-2-Clause.txt").read_bytes().replace(b"\r\n", b"\n"), + "Amadeus-AGPL-3.0.txt": (PROJECT_ROOT / "LICENSE").read_bytes().replace(b"\r\n", b"\n"), + } + destination = Path(project_root).resolve() / "assets" / PACK_TREE + if not destination.resolve().is_relative_to(Path(project_root).resolve()): + raise JapaneseDictionaryError("Dictionary output escapes the project root") + for name, content in files.items(): + target = destination / name + if target.is_symlink(): + raise JapaneseDictionaryError(f"Dictionary output must not be a symlink: {name}") + if target.exists() and target.read_bytes() != content and not overwrite: + raise JapaneseDictionaryError(f"Existing dictionary pack differs: {name}; use --overwrite explicitly") + destination.mkdir(parents=True, exist_ok=True) + for name, content in files.items(): + (destination / name).write_bytes(content) + load_dictionary_pack(destination) + return destination + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True, help="Existing upstream CSV; no download is performed") + parser.add_argument("--project-root", type=Path, default=PROJECT_ROOT) + parser.add_argument("--overwrite", action="store_true") + args = parser.parse_args() + try: + output = prepare_dictionary(args.source, project_root=args.project_root, overwrite=args.overwrite) + except (JapaneseDictionaryError, OSError, ValueError) as exc: + parser.exit(1, f"ERROR: {exc}\n") + print(f"Prepared optional dictionary: {output}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/tts_text_processor.py b/tools/tts_text_processor.py index ee32f7ad..cc26953b 100644 --- a/tools/tts_text_processor.py +++ b/tools/tts_text_processor.py @@ -11,6 +11,7 @@ """ import re +import unicodedata # --------------------------------------------------------------------------- # 情绪预设 @@ -47,11 +48,11 @@ 'Y': 'ワイ', 'Z': 'ゼット', } -# 常见缩写 / 品牌名特殊映射(按长度降序排列,确保长的先匹配) +# 现有缩写 / 品牌名读音;只匹配完整词,大小写变体共用同一条记录。 _SPECIAL_CASES: dict[str, str] = { # Consensus / distributed systems terms - 'Paxos': 'パクソス', 'PAXOS': 'パクソス', 'paxos': 'パクソス', - 'Raft': 'ラフト', 'RAFT': 'ラフト', 'raft': 'ラフト', + 'Paxos': 'パクソス', + 'Raft': 'ラフト', # 技术缩写 'AI': 'エーアイ', 'GPT': 'ジーピーティー', 'CPU': 'シーピーユー', 'GPU': 'ジーピーユー', 'API': 'エーピーアイ', 'URL': 'ユーアールエル', @@ -75,8 +76,7 @@ 'OpenAI': 'オープンエーアイ', 'ChatGPT': 'チャットジーピーティー', 'Gemini': 'ジェミニ', 'Claude': 'クロード', 'Copilot': 'コパイロット', 'GitHub': 'ギットハブ', 'Docker': 'ドッカー', - 'Bilibili': 'ビリビリ', 'bilibili': 'ビリビリ', 'BiliBili': 'ビリビリ', - 'BILIBILI': 'ビリビリ', + 'Bilibili': 'ビリビリ', 'Windows': 'ウィンドウズ', 'Android': 'アンドロイド', 'Linux': 'リナックス', 'Bluetooth': 'ブルートゥース', 'Google': 'グーグル', 'Apple': 'アップル', 'Microsoft': 'マイクロソフト', 'Amazon': 'アマゾン', 'Spotify': 'スポティファイ', @@ -88,33 +88,34 @@ 'Figma': 'フィグマ', 'PS': 'ピーエス', 'AE': 'エーイー', } -# 按长度降序排列,确保长缩写先匹配 -_SPECIAL_CASES_SORTED = sorted(_SPECIAL_CASES.items(), key=lambda x: len(x[0]), reverse=True) - -# 通用大写缩写正则(2-6 个大写字母,可带数字后缀) -_ABBR_PATTERN = re.compile(r'\b[A-Z]{2,6}(?:[0-9]+[A-Z]*)?\b') +# Japanese characters are word characters in Python, so \b cannot delimit an +# initialism next to Japanese. Match complete Latin tokens instead; a known +# spelling such as API must never replace a substring of CAPITAL. +_LATIN_LETTERS = "A-Za-zA-Za-z" +_LATIN_TOKEN_PATTERN = re.compile(rf"[{_LATIN_LETTERS}]+(?:['’'][{_LATIN_LETTERS}]+)*(?:[0-90-9]+[{_LATIN_LETTERS}]*)?") +_INITIALISM_PATTERN = re.compile(r"[A-Z]{2,6}(?:[0-9]+[A-Za-z]*)?") +_SPECIAL_CASES_FOLDED = {word.casefold(): reading for word, reading in _SPECIAL_CASES.items()} def convert_english_abbreviations_to_katakana(text: str) -> str: """ 将英文缩写转换为片假名,使 TTS 逐字母正确朗读。 - 处理顺序: - 1. 特殊映射(优先级最高,按长度降序) - 2. 通用大写缩写(2-6 字母)逐字母转换 + 按完整英文词匹配;普通单词留给日语前端和可选词典处理。 """ - # 1. 特殊映射 - for abbr, katakana in _SPECIAL_CASES_SORTED: - text = text.replace(abbr, katakana) - - # 2. 通用大写缩写 def _convert(match: re.Match) -> str: + token = unicodedata.normalize("NFKC", match.group(0)) + known = _SPECIAL_CASES_FOLDED.get(token.casefold()) + if known: + return known + if not _INITIALISM_PATTERN.fullmatch(token): + return token return "".join( _LETTER_TO_KATAKANA.get(ch.upper(), ch) if ch.isalpha() else ch - for ch in match.group(0) + for ch in token ) - return _ABBR_PATTERN.sub(_convert, text) + return _LATIN_TOKEN_PATTERN.sub(_convert, text) def correct_pronunciation_for_tts(text: str) -> str: @@ -125,7 +126,7 @@ def correct_pronunciation_for_tts(text: str) -> str: - 专有名词读音(如「牧瀬紅莉栖」→「牧瀬クリス」) - 英文缩写 → 片假名(调用 convert_english_abbreviations_to_katakana) - 如需添加新的修正,在 replacements 字典中追加即可。 + 英文单词的读音修复应放在可选词典的数据中;此处保留现有名称兼容。 """ replacements = { "牧瀬紅莉栖": "牧瀬クリス", diff --git a/tts/first_sentence_audio_cache.py b/tts/first_sentence_audio_cache.py index 048fa4d1..e2b4212e 100644 --- a/tts/first_sentence_audio_cache.py +++ b/tts/first_sentence_audio_cache.py @@ -77,7 +77,7 @@ def key_payload(self, text: str, params: dict[str, Any]) -> dict[str, Any]: output_language = current_tts_language_code() except Exception: output_language = "en" if TTS_OUTPUT_LANGUAGE == "英文" else "ja" - return { + payload = { "text": text, # Audio produced before this decoder invariant/guard revision may # already contain a collapsed long vowel. Keep it out of the new @@ -88,6 +88,10 @@ def key_payload(self, text: str, params: dict[str, Any]) -> dict[str, Any]: "sovits_model": TTS_SOVITS_MODEL_PATH, "params": {field: params.get(field) for field in _KEY_FIELDS}, } + if output_language == "ja": + from tts.japanese_dictionary import dictionary_cache_identity + payload["japanese_dictionary"] = dictionary_cache_identity() + return payload def _fingerprint(self, text: str, params: dict[str, Any]) -> str: return self._fingerprint_payload(self.key_payload(text, params)) diff --git a/tts/japanese_dictionary.py b/tts/japanese_dictionary.py new file mode 100644 index 00000000..9f0bf4a1 --- /dev/null +++ b/tts/japanese_dictionary.py @@ -0,0 +1,182 @@ +"""Optional Japanese pronunciation data and its local OpenJTalk compilation. + +The official CSV is an external asset. Compiled dictionaries are disposable +cache files; neither installation nor compilation modifies personal sources. +""" +from __future__ import annotations + +import csv +import hashlib +import io +import json +import logging +import os +from dataclasses import dataclass +from functools import lru_cache +from pathlib import Path +import tempfile +from typing import Iterator + +from config.asset_paths import PROJECT_ROOT + +logger = logging.getLogger(__name__) +PACK_ID = "japanese-pronunciation" +PACK_TREE = "text/ja/english" +PACK_FILES = ( + "userdict.csv", "provenance.json", "NOTICE.md", + "GPT-SoVITS-MIT.txt", "CMUdict-BSD-2-Clause.txt", "Amadeus-AGPL-3.0.txt", +) +_runtime_status = {"state": "not_loaded", "ready": False, "version": "", + "detail": "Japanese frontend has not loaded."} + + +class JapaneseDictionaryError(ValueError): + pass + + +def dictionary_rows(data: bytes) -> Iterator[list[str]]: + """Check the native 15-column OpenJTalk CSV without importing model extras.""" + try: + count = 0 + for number, row in enumerate(csv.reader(io.StringIO(data.decode("utf-8-sig")), strict=True), 1): + if len(row) != 15 or not row[0] or not row[11] or not row[12]: + raise JapaneseDictionaryError(f"Invalid dictionary entry at line {number}: expected 15 columns and a reading") + try: + for value in row[1:4]: + if value not in {"", "*"}: + int(value) + accent, mora = map(int, row[13].split("/")) + if not 0 <= accent <= mora or mora <= 0: + raise ValueError + except ValueError as exc: + raise JapaneseDictionaryError(f"Invalid dictionary cost/accent at line {number}") from exc + count += 1 + yield row + if not count: + raise JapaneseDictionaryError("Dictionary CSV is empty") + except (UnicodeError, csv.Error) as exc: + raise JapaneseDictionaryError(f"Cannot read dictionary CSV: {exc}") from exc + + +@dataclass(frozen=True) +class JapaneseDictionaryPack: + root: Path + version: str + sha256: str + entry_count: int + + def files(self) -> tuple[Path, ...]: + return tuple(self.root / name for name in PACK_FILES) + + +def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: + root = Path(root).resolve() + try: + for name in PACK_FILES: + path = root / name + if path.is_symlink() or not path.is_file() or path.stat().st_size == 0: + raise JapaneseDictionaryError(f"Missing or empty dictionary pack member: {name}") + raw = json.loads((root / "provenance.json").read_text(encoding="utf-8")) + if not isinstance(raw, dict) or raw.get("language") != "ja" or not isinstance(raw.get("version"), str) or not raw["version"].strip(): + raise JapaneseDictionaryError("Dictionary provenance requires a version and Japanese language") + data = (root / "userdict.csv").read_bytes() + digest = hashlib.sha256(data).hexdigest() + count = sum(1 for _ in dictionary_rows(data)) + if raw.get("sha256") != digest or raw.get("entry_count") != count: + raise JapaneseDictionaryError("Dictionary content does not match its provenance hash/count") + source = raw.get("source") + if not isinstance(source, dict) or any(not source.get(key) for key in ("repository", "revision", "path", "sha256")): + raise JapaneseDictionaryError("Dictionary provenance requires a pinned source and source hash") + return JapaneseDictionaryPack(root, raw["version"], digest, count) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise JapaneseDictionaryError(f"Cannot load dictionary pack: {exc}") from exc + + +@lru_cache(maxsize=8) +def _sources(project_root: Path) -> tuple[tuple[Path, ...], str]: + # Resource installation and the enable switch require a backend restart. + official = project_root / "assets" / PACK_TREE + legacy = project_root / "GPT_SoVITS/text/ja_userdic/userdict.csv" + personal = project_root / "assets/text/ja/user.csv" + paths: list[Path] = [] + version = "" + if official.exists(): + pack = load_dictionary_pack(official) + paths.append(official / "userdict.csv") + version = pack.version + elif legacy.is_file(): + # Existing installations still use this source. New packs replace its + # role without copying, editing, or deleting the installation's data. + paths.append(legacy) + version = "legacy-local" + if personal.is_file(): + paths.append(personal) + return tuple(paths), version + + +def _enabled() -> bool: + from config.settings import ENABLE_JAPANESE_USER_DICTIONARY + return ENABLE_JAPANESE_USER_DICTIONARY + + +@lru_cache(maxsize=16) +def _digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def dictionary_cache_identity() -> str: + """Speech cache identity; does not import pyopenjtalk in the host process.""" + if not _enabled(): + return "disabled" + try: + paths, _ = _sources(PROJECT_ROOT) + return ":".join(_digest(path) for path in paths) or "none" + except (JapaneseDictionaryError, OSError): + return "invalid" + + +def dictionary_runtime_status() -> dict: + return dict(_runtime_status) + + +def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, enabled: bool | None = None) -> dict: + """Install all selected dictionaries once when the Japanese frontend loads.""" + global _runtime_status + enabled = _enabled() if enabled is None else enabled + state = "not_installed" + version = "" + detail = "Optional Japanese dictionary is not installed." + try: + if not enabled: + jtalk.unset_user_dict() + state, detail = "disabled", "Japanese user dictionaries are disabled." + else: + paths, version = _sources(Path(project_root).resolve()) + compiled = [] + jtalk.unset_user_dict() + for source in paths: + for _ in dictionary_rows(source.read_bytes()): + pass + base = Path(os.fsdecode(jtalk.OPEN_JTALK_DICT_DIR)) + system = base / "sys.dic" + system_key = (str(base), system.stat().st_size, system.stat().st_mtime_ns) + key = hashlib.sha256(repr((jtalk.__version__, system_key, _digest(source))).encode()).hexdigest() + cache = Path(project_root) / ".cache/japanese_dictionary" + target = cache / f"{key}.dict" + if not target.is_file(): + cache.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(prefix="compile-", dir=cache) as temporary: + output = Path(temporary) / "user.dict" + jtalk.mecab_dict_index(str(source), str(output)) + os.replace(output, target) + compiled.append(str(target)) + if compiled: + jtalk.update_global_jtalk_with_user_dict(compiled) + state, detail = "ready", f"Japanese dictionary {version or 'personal'} loaded ({len(compiled)} source(s))." + logger.info("[JapaneseDictionary] %s", detail) + except (JapaneseDictionaryError, OSError, RuntimeError, ValueError) as exc: + jtalk.unset_user_dict() + state, detail = "invalid", str(exc) + logger.warning("[JapaneseDictionary] Optional dictionary unavailable: %s", exc) + _runtime_status = {"state": state, "ready": state == "ready", "version": version, "detail": detail} + return dictionary_runtime_status() diff --git a/tts/pipeline.py b/tts/pipeline.py index 0c667f3a..11ec3468 100644 --- a/tts/pipeline.py +++ b/tts/pipeline.py @@ -133,7 +133,8 @@ def current_tts_epoch() -> int: def _prepare_synthesis_text(text: str) -> str: - return correct_pronunciation_for_tts(_strip_tts_fullwidth_parentheses(text)).strip() + prepared = _strip_tts_fullwidth_parentheses(text).strip() + return correct_pronunciation_for_tts(prepared) if TTS_OUTPUT_LANGUAGE == "日文" else prepared def _prepare_synthesis_request(text: str, is_first_sentence: bool = False, *, enhanced: bool = False): From bb4b65bc3f9d28fd62a8d6c862541d830b4277c1 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Sun, 18 Oct 2026 07:48:49 +1300 Subject: [PATCH 02/10] fix(tts): keep dictionary validation in the shared data layer --- config/asset_packages.py | 2 +- config/japanese_dictionary.py | 79 ++++++++++++++++++++++++++++ server/handlers/system_handler.py | 3 +- tests/test_japanese_dictionary.py | 47 ++++++++++------- tests/test_release_tooling.py | 2 +- tools/audit_japanese_dictionary.py | 2 +- tools/external_assets.py | 2 +- tools/maintainability_ratchet.py | 1 + tools/prepare_japanese_dictionary.py | 2 +- tts/japanese_dictionary.py | 76 ++------------------------ 10 files changed, 117 insertions(+), 99 deletions(-) create mode 100644 config/japanese_dictionary.py diff --git a/config/asset_packages.py b/config/asset_packages.py index 42576906..377efa1f 100644 --- a/config/asset_packages.py +++ b/config/asset_packages.py @@ -154,7 +154,7 @@ def external_asset_pack_status( message = str(exc) if state == "installed" and spec.validator == "japanese_dictionary": - from tts.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack + from config.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack try: version = load_dictionary_pack(asset_path(asset_root, spec.trees[0])).version except JapaneseDictionaryError as exc: diff --git a/config/japanese_dictionary.py b/config/japanese_dictionary.py new file mode 100644 index 00000000..472b02e2 --- /dev/null +++ b/config/japanese_dictionary.py @@ -0,0 +1,79 @@ +"""Model-free data contract for the optional Japanese dictionary asset pack.""" +from __future__ import annotations + +import csv +from dataclasses import dataclass +import hashlib +import io +import json +from pathlib import Path +from typing import Iterator + +PACK_ID = "japanese-pronunciation" +PACK_TREE = "text/ja/english" +PACK_FILES = ( + "userdict.csv", "provenance.json", "NOTICE.md", + "GPT-SoVITS-MIT.txt", "CMUdict-BSD-2-Clause.txt", "Amadeus-AGPL-3.0.txt", +) + + +class JapaneseDictionaryError(ValueError): + pass + + +def dictionary_rows(data: bytes) -> Iterator[list[str]]: + """Check the native 15-column OpenJTalk CSV without importing model extras.""" + try: + count = 0 + for number, row in enumerate(csv.reader(io.StringIO(data.decode("utf-8-sig")), strict=True), 1): + if len(row) != 15 or not row[0] or not row[11] or not row[12]: + raise JapaneseDictionaryError(f"Invalid dictionary entry at line {number}: expected 15 columns and a reading") + try: + for value in row[1:4]: + if value not in {"", "*"}: + int(value) + accent, mora = map(int, row[13].split("/")) + if not 0 <= accent <= mora or mora <= 0: + raise ValueError + except ValueError as exc: + raise JapaneseDictionaryError(f"Invalid dictionary cost/accent at line {number}") from exc + count += 1 + yield row + if not count: + raise JapaneseDictionaryError("Dictionary CSV is empty") + except (UnicodeError, csv.Error) as exc: + raise JapaneseDictionaryError(f"Cannot read dictionary CSV: {exc}") from exc + + +@dataclass(frozen=True) +class JapaneseDictionaryPack: + root: Path + version: str + sha256: str + entry_count: int + + def files(self) -> tuple[Path, ...]: + return tuple(self.root / name for name in PACK_FILES) + + +def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: + root = Path(root).resolve() + try: + for name in PACK_FILES: + path = root / name + if path.is_symlink() or not path.is_file() or path.stat().st_size == 0: + raise JapaneseDictionaryError(f"Missing or empty dictionary pack member: {name}") + raw = json.loads((root / "provenance.json").read_text(encoding="utf-8")) + if not isinstance(raw, dict) or raw.get("language") != "ja" or not isinstance(raw.get("version"), str) or not raw["version"].strip(): + raise JapaneseDictionaryError("Dictionary provenance requires a version and Japanese language") + data = (root / "userdict.csv").read_bytes() + digest = hashlib.sha256(data).hexdigest() + count = sum(1 for _ in dictionary_rows(data)) + if raw.get("sha256") != digest or raw.get("entry_count") != count: + raise JapaneseDictionaryError("Dictionary content does not match its provenance hash/count") + source = raw.get("source") + if not isinstance(source, dict) or any(not source.get(key) for key in ("repository", "revision", "path", "sha256")): + raise JapaneseDictionaryError("Dictionary provenance requires a pinned source and source hash") + return JapaneseDictionaryPack(root, raw["version"], digest, count) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise JapaneseDictionaryError(f"Cannot load dictionary pack: {exc}") from exc diff --git a/server/handlers/system_handler.py b/server/handlers/system_handler.py index 9f3dd157..b9b8a6cb 100644 --- a/server/handlers/system_handler.py +++ b/server/handlers/system_handler.py @@ -129,7 +129,8 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No emotion_pack = external_asset_pack_status(PACK_ID) emotion_runtime = tts_pipeline.emotion_reference_status() emotion_requested = bool(settings.ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING) - from tts.japanese_dictionary import PACK_ID as DICTIONARY_PACK_ID, dictionary_runtime_status + from config.japanese_dictionary import PACK_ID as DICTIONARY_PACK_ID + from tts.japanese_dictionary import dictionary_runtime_status dictionary_pack = external_asset_pack_status(DICTIONARY_PACK_ID) dictionary_runtime = dictionary_runtime_status() dictionary_enabled = bool(settings.ENABLE_JAPANESE_USER_DICTIONARY) diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py index 6ab49b91..8ffdff72 100644 --- a/tests/test_japanese_dictionary.py +++ b/tests/test_japanese_dictionary.py @@ -10,6 +10,7 @@ import pytest from config.asset_packages import AssetPackageError, external_asset_pack_status +from config import japanese_dictionary as dictionary_pack from tools.audit_japanese_dictionary import audit_rows, native_mora_counter, native_surface_normalizer, normalize_rows from tools.external_assets import build_bundle, install_bundle, verify_bundle from tools.prepare_japanese_dictionary import prepare_dictionary @@ -48,7 +49,7 @@ def _prepare(root: Path, *, native: bool = False) -> Path: data += (",".join(row) + "\n").encode() source.write_bytes(data) recipe = json.loads((ROOT / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) - recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(list(dictionary.dictionary_rows(data)))) + recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(list(dictionary_pack.dictionary_rows(data)))) recipe_path = root / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") return prepare_dictionary(source, project_root=root, recipe_path=recipe_path, @@ -84,10 +85,10 @@ def test_pipeline_applies_japanese_readings_only_to_japanese_speech(monkeypatch) def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): pack_root = _prepare(tmp_path) assert (tmp_path / "upstream.csv").read_text(encoding="utf-8") == FOLLOWER + LEARNER - pack = dictionary.load_dictionary_pack(pack_root) - corrections = list(dictionary.dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) + pack = dictionary_pack.load_dictionary_pack(pack_root) + corrections = list(dictionary_pack.dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) assert pack.entry_count == len(corrections) - readings = {row[0]: row[11] for row in dictionary.dictionary_rows((pack_root / "userdict.csv").read_bytes())} + readings = {row[0]: row[11] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes())} assert readings == {row[0]: row[11] for row in corrections} assert readings["follower"] == "フォロワー" assert readings["learner"] == "ラーナー" @@ -97,7 +98,7 @@ def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): assert all(path.is_file() for path in pack.files()) # A maintainer cannot accidentally publish a customized installation as the # pinned upstream base, even if it has the expected CSV shape. - with pytest.raises(dictionary.JapaneseDictionaryError, match="differs from the pinned"): + with pytest.raises(dictionary_pack.JapaneseDictionaryError, match="differs from the pinned"): prepare_dictionary(tmp_path / "upstream.csv", project_root=tmp_path / "rejected") assert not (tmp_path / "rejected/assets").exists() @@ -113,37 +114,37 @@ def test_optional_bundle_round_trip_preserves_personal_and_legacy_sources(tmp_pa legacy = recipient / "GPT_SoVITS/text/ja_userdic/userdict.csv" legacy.parent.mkdir(parents=True) legacy.write_text(LEARNER, encoding="utf-8") - assert external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "not_installed" + assert external_asset_pack_status(dictionary_pack.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "not_installed" archive = tmp_path / "dictionary.zip" - manifest = build_bundle(project_root=publisher, output=archive, pack_ids=[dictionary.PACK_ID], index_path=INDEX) - assert manifest["file_count"] == len(dictionary.PACK_FILES) + manifest = build_bundle(project_root=publisher, output=archive, pack_ids=[dictionary_pack.PACK_ID], index_path=INDEX) + assert manifest["file_count"] == len(dictionary_pack.PACK_FILES) with zipfile.ZipFile(archive) as bundle: assert not any(name.endswith("user.dict") or name.endswith("user.csv") for name in bundle.namelist()) assert verify_bundle(archive, index_path=INDEX) == manifest result = install_bundle(archive, project_root=recipient, index_path=INDEX) - assert result["installed_files"] == len(dictionary.PACK_FILES) + assert result["installed_files"] == len(dictionary_pack.PACK_FILES) assert install_bundle(archive, project_root=recipient, index_path=INDEX)["installed_files"] == 0 - status = external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX) - assert status["installed"] and status["version"] == dictionary.load_dictionary_pack(pack_root).version + status = external_asset_pack_status(dictionary_pack.PACK_ID, asset_root=recipient / "assets", index_path=INDEX) + assert status["installed"] and status["version"] == dictionary_pack.load_dictionary_pack(pack_root).version assert personal.read_text(encoding="utf-8") == FOLLOWER assert legacy.read_text(encoding="utf-8") == LEARNER # Existing different data is refused before any recipient file changes. - installed_csv = recipient / "assets" / dictionary.PACK_TREE / "userdict.csv" + installed_csv = recipient / "assets" / dictionary_pack.PACK_TREE / "userdict.csv" installed_csv.write_bytes(b"private edit") with pytest.raises(AssetPackageError, match="different|exists|conflict"): install_bundle(archive, project_root=recipient, index_path=INDEX) assert installed_csv.read_bytes() == b"private edit" - assert external_asset_pack_status(dictionary.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "invalid" + assert external_asset_pack_status(dictionary_pack.PACK_ID, asset_root=recipient / "assets", index_path=INDEX)["state"] == "invalid" @pytest.mark.parametrize("bad", [b"", b"not,an,entry\n", b"\xff"]) def test_dictionary_contract_rejects_invalid_csv(bad): - with pytest.raises(dictionary.JapaneseDictionaryError): - list(dictionary.dictionary_rows(bad)) + with pytest.raises(dictionary_pack.JapaneseDictionaryError): + list(dictionary_pack.dictionary_rows(bad)) def _row(surface, pronunciation, accent="0/4"): - row = next(dictionary.dictionary_rows(FOLLOWER.encode())) + row = next(dictionary_pack.dictionary_rows(FOLLOWER.encode())) row[0], row[13] = surface, accent row[10:13] = [pronunciation] * 3 return row @@ -216,7 +217,7 @@ def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surfac recipe_path = tmp_path / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, count_mora=lambda _: None) - readings = [row[12] for row in dictionary.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0] == "corps"] + readings = [row[12] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0] == "corps"] assert set(readings) == {"コア", "コアズ"} assert source.read_bytes() == data @@ -289,6 +290,12 @@ def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_ jtalk.__version__ = "test-2" dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) assert len(jtalk.compiled) == 4, "a changed native runtime must rebuild its cache" + for path in jtalk.registered[-1]: + Path(path).unlink() + assert dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True)["ready"] + assert len(jtalk.compiled) == 6, "deleting disposable compiled caches must rebuild from the selected CSVs" + assert legacy.read_text(encoding="utf-8") == FOLLOWER + assert personal.read_text(encoding="utf-8") == LEARNER dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=False) assert dictionary.dictionary_runtime_status()["state"] == "disabled" and jtalk.registered[-1] == [] @@ -339,7 +346,7 @@ def test_settings_expose_dictionary_failures_and_the_disable_switch(monkeypatch, monkeypatch.setattr(asset_packages, "external_asset_pack_status", lambda pack_id, **kwargs: { "installed": installed, "state": "installed" if installed else "invalid", "version": "test", "message": "" if installed else "CSV checksum failed", - } if pack_id == dictionary.PACK_ID else original(pack_id, **kwargs)) + } if pack_id == dictionary_pack.PACK_ID else original(pack_id, **kwargs)) monkeypatch.setattr(settings, "ENABLE_JAPANESE_USER_DICTIONARY", enabled) monkeypatch.setattr(dictionary, "_runtime_status", { "state": runtime_state, "ready": False, "version": "test", "detail": "Dictionary compilation failed", @@ -355,7 +362,7 @@ def test_native_runtime_reads_corrected_words_and_keeps_japanese_intact(tmp_path jtalk = pytest.importorskip("pyopenjtalk") root = tmp_path / "日本語" _prepare(root, native=True) - original_source = (root / "assets" / dictionary.PACK_TREE / "userdict.csv").read_bytes() + original_source = (root / "assets" / dictionary_pack.PACK_TREE / "userdict.csv").read_bytes() try: jtalk.unset_user_dict() japanese = jtalk.g2p("今日は生ビールを飲みます。") @@ -369,7 +376,7 @@ def test_native_runtime_reads_corrected_words_and_keeps_japanese_intact(tmp_path prepared = correct_pronunciation_for_tts("LeaderとMLPを確認する。") assert "エムエルピー" in "".join(feature["read"] for feature in jtalk.run_frontend(prepared.lower())) assert jtalk.g2p("今日は生ビールを飲みます。") == japanese - assert (root / "assets" / dictionary.PACK_TREE / "userdict.csv").read_bytes() == original_source + assert (root / "assets" / dictionary_pack.PACK_TREE / "userdict.csv").read_bytes() == original_source finally: jtalk.unset_user_dict() diff --git a/tests/test_release_tooling.py b/tests/test_release_tooling.py index 60954f83..25d71a19 100644 --- a/tests/test_release_tooling.py +++ b/tests/test_release_tooling.py @@ -351,7 +351,7 @@ def test_gpt_sovits_release_boundary_excludes_optional_or_unverified_material() def test_optional_japanese_dictionary_sources_and_notices_have_separate_release_boundaries() -> None: policy = json.loads((ROOT / "release/source_release_policy.json").read_text(encoding="utf-8")) sources = ["assets/text/ja/english/userdict.csv", "assets/text/ja/user.csv"] - code_and_notices = ["tools/data/japanese_dictionary_corrections.csv", "tools/audit_japanese_dictionary.py", + code_and_notices = ["config/japanese_dictionary.py", "tools/data/japanese_dictionary_corrections.csv", "tools/audit_japanese_dictionary.py", "LICENSES/JAPANESE-DICTIONARY.md", "docs/japanese_pronunciation_dictionary.md"] selected, excluded = select_paths([*sources, *code_and_notices], policy) assert excluded == sources diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py index 76361855..b8e2a8f3 100644 --- a/tools/audit_japanese_dictionary.py +++ b/tools/audit_japanese_dictionary.py @@ -18,7 +18,7 @@ sys.path.insert(0, str(PROJECT_ROOT)) from tools.tts_text_processor import convert_english_abbreviations_to_katakana # noqa: E402 -from tts.japanese_dictionary import dictionary_rows # noqa: E402 +from config.japanese_dictionary import dictionary_rows # noqa: E402 _KANA = re.compile(r"[\u30a1-\u30fa\u30fc]+") _MORA_PHONES = frozenset({"a", "i", "u", "e", "o", "A", "I", "U", "E", "O", "N", "cl"}) diff --git a/tools/external_assets.py b/tools/external_assets.py index f99fcd3b..94507dac 100644 --- a/tools/external_assets.py +++ b/tools/external_assets.py @@ -86,7 +86,7 @@ def _resolved_source_files( ) -> tuple[Path, ...]: asset_root = project_root / "assets" if spec.validator == "japanese_dictionary": - from tts.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack + from config.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack try: files = set(load_dictionary_pack(asset_path(asset_root, spec.trees[0])).files()) except JapaneseDictionaryError as exc: diff --git a/tools/maintainability_ratchet.py b/tools/maintainability_ratchet.py index 74e1c9bf..81f2bb85 100644 --- a/tools/maintainability_ratchet.py +++ b/tools/maintainability_ratchet.py @@ -27,6 +27,7 @@ ("agent_host/acp_skills.py", "skill_launch_arguments", "json_text"): "Disposable process-launch overlay in TemporaryDirectory; no persistent state or rehydration, removed after Agent exit.", ("config/durable_io.py", "_replace", "replace"): "The shared commit point.", ("render/texture_cache.py", "TextureDiskCache._publish", "replace"): "Rebuildable cache with contention and quota rules.", + ("tts/japanese_dictionary.py", "configure_japanese_dictionary", "replace"): "Rebuildable native dictionary under .cache/japanese_dictionary, keyed by source/system dictionary/runtime; source CSVs are never replaced.", ("server/work_export_service.py", "WorkExportService._publish_atomic_replace", "replace"): "Approved export replacement with target identity and hash verification.", ("core/character_rag.py", "build_index", "json_text"): "Offline index generation; rebuildable metadata.", ("server/interaction_branch.py", "InteractionBranchCoordinator._persist", "json_text"): "Diagnostic snapshots, no runtime rehydration; per-event writes.", diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py index 55eb7e9f..e10979d7 100644 --- a/tools/prepare_japanese_dictionary.py +++ b/tools/prepare_japanese_dictionary.py @@ -15,7 +15,7 @@ if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) -from tts.japanese_dictionary import ( # noqa: E402 +from config.japanese_dictionary import ( # noqa: E402 PACK_TREE, JapaneseDictionaryError, dictionary_rows, load_dictionary_pack, ) from tools.audit_japanese_dictionary import native_mora_counter, native_surface_normalizer, normalize_rows # noqa: E402 diff --git a/tts/japanese_dictionary.py b/tts/japanese_dictionary.py index 9f0bf4a1..5b312b69 100644 --- a/tts/japanese_dictionary.py +++ b/tts/japanese_dictionary.py @@ -5,93 +5,23 @@ """ from __future__ import annotations -import csv import hashlib -import io -import json import logging import os -from dataclasses import dataclass from functools import lru_cache from pathlib import Path import tempfile -from typing import Iterator from config.asset_paths import PROJECT_ROOT +from config.japanese_dictionary import ( + PACK_TREE, JapaneseDictionaryError, dictionary_rows, load_dictionary_pack, +) logger = logging.getLogger(__name__) -PACK_ID = "japanese-pronunciation" -PACK_TREE = "text/ja/english" -PACK_FILES = ( - "userdict.csv", "provenance.json", "NOTICE.md", - "GPT-SoVITS-MIT.txt", "CMUdict-BSD-2-Clause.txt", "Amadeus-AGPL-3.0.txt", -) _runtime_status = {"state": "not_loaded", "ready": False, "version": "", "detail": "Japanese frontend has not loaded."} -class JapaneseDictionaryError(ValueError): - pass - - -def dictionary_rows(data: bytes) -> Iterator[list[str]]: - """Check the native 15-column OpenJTalk CSV without importing model extras.""" - try: - count = 0 - for number, row in enumerate(csv.reader(io.StringIO(data.decode("utf-8-sig")), strict=True), 1): - if len(row) != 15 or not row[0] or not row[11] or not row[12]: - raise JapaneseDictionaryError(f"Invalid dictionary entry at line {number}: expected 15 columns and a reading") - try: - for value in row[1:4]: - if value not in {"", "*"}: - int(value) - accent, mora = map(int, row[13].split("/")) - if not 0 <= accent <= mora or mora <= 0: - raise ValueError - except ValueError as exc: - raise JapaneseDictionaryError(f"Invalid dictionary cost/accent at line {number}") from exc - count += 1 - yield row - if not count: - raise JapaneseDictionaryError("Dictionary CSV is empty") - except (UnicodeError, csv.Error) as exc: - raise JapaneseDictionaryError(f"Cannot read dictionary CSV: {exc}") from exc - - -@dataclass(frozen=True) -class JapaneseDictionaryPack: - root: Path - version: str - sha256: str - entry_count: int - - def files(self) -> tuple[Path, ...]: - return tuple(self.root / name for name in PACK_FILES) - - -def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: - root = Path(root).resolve() - try: - for name in PACK_FILES: - path = root / name - if path.is_symlink() or not path.is_file() or path.stat().st_size == 0: - raise JapaneseDictionaryError(f"Missing or empty dictionary pack member: {name}") - raw = json.loads((root / "provenance.json").read_text(encoding="utf-8")) - if not isinstance(raw, dict) or raw.get("language") != "ja" or not isinstance(raw.get("version"), str) or not raw["version"].strip(): - raise JapaneseDictionaryError("Dictionary provenance requires a version and Japanese language") - data = (root / "userdict.csv").read_bytes() - digest = hashlib.sha256(data).hexdigest() - count = sum(1 for _ in dictionary_rows(data)) - if raw.get("sha256") != digest or raw.get("entry_count") != count: - raise JapaneseDictionaryError("Dictionary content does not match its provenance hash/count") - source = raw.get("source") - if not isinstance(source, dict) or any(not source.get(key) for key in ("repository", "revision", "path", "sha256")): - raise JapaneseDictionaryError("Dictionary provenance requires a pinned source and source hash") - return JapaneseDictionaryPack(root, raw["version"], digest, count) - except (OSError, UnicodeError, json.JSONDecodeError) as exc: - raise JapaneseDictionaryError(f"Cannot load dictionary pack: {exc}") from exc - - @lru_cache(maxsize=8) def _sources(project_root: Path) -> tuple[tuple[Path, ...], str]: # Resource installation and the enable switch require a backend restart. From a451bd2a232cfba0ffe3010dc95c40c195254c73 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Sun, 18 Oct 2026 09:16:20 +1300 Subject: [PATCH 03/10] fix(tts): address dictionary pronunciation and runtime boundaries --- LICENSES/JAPANESE-DICTIONARY.md | 9 +- config/japanese_dictionary.py | 38 ++- docs/japanese_pronunciation_dictionary.md | 74 ++++-- .../src/renderer/components/SettingsPage.tsx | 4 + .../components/voiceConfigurationCatalog.ts | 3 +- .../tests/voiceConfigurationCatalog.test.mjs | 4 + local_tts_infer.py | 4 + server/handlers/system_handler.py | 13 +- tests/test_first_sentence_audio_cache.py | 23 +- tests/test_gpt_sovits_sidecar_backend.py | 6 +- tests/test_japanese_dictionary.py | 38 +-- tests/test_japanese_dictionary_boundaries.py | 251 ++++++++++++++++++ tools/audit_japanese_dictionary.py | 21 +- tools/data/japanese_dictionary_source.json | 5 +- tools/prebuild_first_sentence_audio_cache.py | 3 + tools/prepare_japanese_dictionary.py | 10 - tools/tts_text_processor.py | 11 + tools/validate_japanese_dictionary.py | 117 ++++++++ tts/backends/gpt_sovits.py | 12 + tts/first_sentence_audio_cache.py | 25 +- tts/gpt_sovits_sidecar.py | 5 +- tts/japanese_dictionary.py | 187 +++++++------ tts/pipeline.py | 14 + 23 files changed, 707 insertions(+), 170 deletions(-) create mode 100644 tests/test_japanese_dictionary_boundaries.py create mode 100644 tools/validate_japanese_dictionary.py diff --git a/LICENSES/JAPANESE-DICTIONARY.md b/LICENSES/JAPANESE-DICTIONARY.md index f91aba63..a1646f4e 100644 --- a/LICENSES/JAPANESE-DICTIONARY.md +++ b/LICENSES/JAPANESE-DICTIONARY.md @@ -48,13 +48,16 @@ then removes only identical full rows. Different lexical readings remain. The generated `provenance.json` additionally identifies the derivative's version, hash, entry count, normalization rules and repair counters. -Version 2026.10.16.3 retains the previous 11 singular-reading repairs and +Version 2026.10.18.1 retains the previous 11 singular-reading repairs and complete `proposer` entry, and reviews the remaining 317 flagged entries: 94 lexical/script/annotation repairs, 65 phonetic representation repairs and -158 unchanged entries awaiting stronger evidence. The recipe identifies each +158 entries retaining their reading/cost/accent fields pending stronger evidence. The recipe identifies each reviewed before/after pronunciation, relevant CMU/native evidence and measured word-cost adjustments. Other readings of a corrected surface remain available. -Only repaired entries receive native MeCab surface spelling. `proposer` still +All surfaces receive native MeCab spelling, including 48 of those retained +entries; their other fields are unchanged. One- and two-letter Latin surfaces +yield to the Japanese base dictionary, preserving letter/unit readings. The +prepared pack has 127,811 entries. `proposer` still uses a provisional flat accent; native listening review is still required. This does not claim exhaustive dictionary accuracy or contextual disambiguation of all Japanese homographs. diff --git a/config/japanese_dictionary.py b/config/japanese_dictionary.py index 472b02e2..cd455541 100644 --- a/config/japanese_dictionary.py +++ b/config/japanese_dictionary.py @@ -6,6 +6,7 @@ import hashlib import io import json +from functools import lru_cache from pathlib import Path from typing import Iterator @@ -23,9 +24,11 @@ class JapaneseDictionaryError(ValueError): def dictionary_rows(data: bytes) -> Iterator[list[str]]: """Check the native 15-column OpenJTalk CSV without importing model extras.""" + if data.startswith(b"\xef\xbb\xbf"): + raise JapaneseDictionaryError("Dictionary CSV has a UTF-8 BOM; save as UTF-8 without BOM so the first entry can match") try: count = 0 - for number, row in enumerate(csv.reader(io.StringIO(data.decode("utf-8-sig")), strict=True), 1): + for number, row in enumerate(csv.reader(io.StringIO(data.decode("utf-8")), strict=True), 1): if len(row) != 15 or not row[0] or not row[11] or not row[12]: raise JapaneseDictionaryError(f"Invalid dictionary entry at line {number}: expected 15 columns and a reading") try: @@ -56,8 +59,7 @@ def files(self) -> tuple[Path, ...]: return tuple(self.root / name for name in PACK_FILES) -def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: - root = Path(root).resolve() +def _validate_dictionary_pack(root: Path) -> JapaneseDictionaryPack: try: for name in PACK_FILES: path = root / name @@ -68,12 +70,38 @@ def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: raise JapaneseDictionaryError("Dictionary provenance requires a version and Japanese language") data = (root / "userdict.csv").read_bytes() digest = hashlib.sha256(data).hexdigest() + if raw.get("sha256") != digest: + raise JapaneseDictionaryError("Dictionary content does not match its provenance hash") count = sum(1 for _ in dictionary_rows(data)) - if raw.get("sha256") != digest or raw.get("entry_count") != count: - raise JapaneseDictionaryError("Dictionary content does not match its provenance hash/count") + if raw.get("entry_count") != count: + raise JapaneseDictionaryError("Dictionary content does not match its provenance entry count") source = raw.get("source") if not isinstance(source, dict) or any(not source.get(key) for key in ("repository", "revision", "path", "sha256")): raise JapaneseDictionaryError("Dictionary provenance requires a pinned source and source hash") return JapaneseDictionaryPack(root, raw["version"], digest, count) except (OSError, UnicodeError, json.JSONDecodeError) as exc: raise JapaneseDictionaryError(f"Cannot load dictionary pack: {exc}") from exc + + +@lru_cache(maxsize=8) +def _cached_pack(root: Path, signatures: tuple) -> tuple[JapaneseDictionaryPack | None, str]: + # Cache failures as values too: lru_cache does not retain raised exceptions. + try: + return _validate_dictionary_pack(root), "" + except JapaneseDictionaryError as exc: + return None, str(exc) + + +def load_dictionary_pack(root: Path) -> JapaneseDictionaryPack: + root = Path(root).resolve() + try: + signatures = [] + for name in PACK_FILES: + stat = (root / name).lstat() + signatures.append((stat.st_mode, stat.st_dev, stat.st_ino, stat.st_size, stat.st_mtime_ns, stat.st_ctime_ns)) + except OSError as exc: + raise JapaneseDictionaryError(f"Cannot load dictionary pack: {exc}") from exc + pack, error = _cached_pack(root, tuple(signatures)) + if error: + raise JapaneseDictionaryError(error) + return pack diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index 7e353da0..4741fc80 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -15,8 +15,8 @@ loader before installation. Older applications reject the unknown pack ID. The first compatible application release has not yet been published. ```powershell -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.16.3.zip -python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.18.1.zip +python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.18.1.zip python tools/external_assets.py status ``` @@ -35,9 +35,10 @@ Resource status reports the pack version and validates the CSV shape, provenance hash/count and required notices without importing model libraries. The Japanese frontend logs actual compilation/loading failures and keeps the base frontend usable. The runtime ready state is separate from pack presence. -For subprocess synthesis, the parent settings page may show installation status -before its own frontend has loaded; the sidecar's startup log is the evidence -of actual dictionary activation. +Missing optional data is a normal, inactive state. The card is active only for +Japanese GPT-SoVITS output with a present or loaded source. For subprocess +synthesis, actual native state and identity travel in the existing ready/done +messages; installation status is not evidence of native activation. ## Data and personal files / 数据与个人词条 @@ -45,6 +46,10 @@ of actual dictionary activation. - Personal CSV entries may live at `assets/text/ja/user.csv`, outside the pack. Use OpenJTalk's 15-column CSV format and appropriate entry costs; dictionary registration order alone does not promise precedence for duplicate words. + Save as UTF-8 **without BOM**: native MeCab would otherwise treat the BOM as + part of the first surface. Empty, malformed or uncompileable sources are + individually reported with their relative filename and skipped. A broken + personal CSV does not disable a valid official pack. - If the new pack is absent, the existing `GPT_SoVITS/text/ja_userdic/userdict.csv` remains a supported local source. An installed pack replaces that legacy source's role; an invalid installed @@ -52,9 +57,19 @@ of actual dictionary activation. - Compilation writes only `.cache/japanese_dictionary/`, keyed by source bytes, native runtime version and the installed system dictionary identity. The application does not rewrite or distribute personal/legacy CSVs or binaries. -- First-sentence audio cache keys include the selected dictionary identity, so - installing, disabling or updating data cannot reuse audio with old readings - after restart. Existing audio files are not deleted. + A peer-published binary for the same content key is reused if Windows locks + replacement. Successful loading prunes obsolete managed binaries; files still + mapped by another live process are retried at a later successful startup. +- First-sentence audio cache keys identify the native sources **actually loaded** + in the synthesis process, including its native runtime/system dictionary. + A base fallback has a different identity from a loaded pack. Unknown native + state bypasses both cache reads and writes. Japanese pre-identity legacy + entries are not migrated; the existing English migration remains supported. + Japanese frontend setup precedes backend ready; later lazy language loads + refresh the sidecar's identity at inference completion. +- The optional loader reports unsupported APIs without blocking base frontend + import. User dictionaries require the pinned `pyopenjtalk-plus` API; an older + `TTS_PYTHON` environment is not silently adapted or claimed fully supported. 用户 CSV 与正式包分开,安装和升级不会覆盖。旧词典目录也不会被删除或改写。 编译结果是本机缓存,不能作为可维护的词典源。个人重复词条的优先级须通过 @@ -75,8 +90,9 @@ is explicit. ```powershell python tools/prepare_japanese_dictionary.py --source GPT_SoVITS/text/ja_userdic/userdict.csv -python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.16.3.zip -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.16.3.zip +python tools/validate_japanese_dictionary.py --output output/japanese_dictionary_validation.json +python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.18.1.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.18.1.zip python tools/audit_japanese_dictionary.py --source assets/text/ja/english/userdict.csv --output output/japanese_dictionary_audit.json ``` @@ -87,10 +103,11 @@ model paths or rebuild any existing model/voice archive. ## Quality boundary / 质量范围 -Version 2026.10.16.3 reviews the 317 entries left by version 2026.10.16.2: +Version 2026.10.18.1 retains the review of the 317 entries left by version 2026.10.16.2: 94 receive reviewed lexical/script/annotation repairs, 65 receive phonetic -separator or format-control repairs, and 158 remain unchanged pending stronger -transcription or accent evidence. The local review report records every +separator or format-control repairs, and 158 retain their readings, costs and +accent fields pending stronger evidence. Of these, 48 receive surface spelling +normalization only. The local review report records every disposition. An all-kana alternative alone is not proof of lexical correctness. Examples include `balloonist` (`二` → `ニ`), `ninth` (`ん` → `ン`), untranslated letter names, stray inflection letters and literal LLM explanations. @@ -100,8 +117,9 @@ not replace every pronunciation sharing that word. Legitimate alternatives such as singular/plural `corps` and initialism/plural `CDS/CDs` remain available; no context classifier is added to force a single choice. -Native input converts apostrophes and hyphens to `’` and `−`. Only repaired -entries receive this native surface spelling, and the frontend preserves +Native input converts apostrophes and hyphens to `’` and `−`. All entries receive +this native surface spelling, including previously dormant contractions and +compounds; this changes representation rather than guessing readings. The frontend preserves Latin compound punctuation until native tokenization. Standalone punctuation and Japanese punctuation keep their normal handling. Native MeCab measurements in three synthetic contexts exposed cheap unintended word splits; 109 reviewed @@ -109,7 +127,16 @@ entry costs were reduced just enough to beat those measured splits. The measured costs are stored in the CSV/recipe, with no runtime word-specific pronunciation or cost rules. Unconfirmed entries retain their original rows. -The pack contains 128,111 entries. Re-auditing it produces no further structural +One- and two-letter Latin surfaces are excluded at build time: these fragments +must yield to the base Japanese dictionary's letter and measurement entries. +This excludes 328 pre-deduplication rows (300 distinct previous pack rows), +preventing imported `m`/`w`/`h` fragments from displacing cm/kg/km/mm/ml/kW. +No per-unit or per-word runtime list is added. Unknown short number suffixes +also bypass generic initialism expansion; known names and longer initialisms +such as `3GPUs` retain their existing handling. Native spelling is normalized for +8,998 pre-deduplication surfaces. + +The pack contains 127,811 entries. Re-auditing it produces no further structural repairs and leaves the 158 review entries intact. No accent is clamped or invented for `guangjo`, whose source accent exceeds the native mora count. @@ -146,7 +173,8 @@ such collisions alone do not prove errors (`path` and `paths` can both be Whole-token abbreviation preparation handles `MLP` next to Japanese, fullwidth Latin text and case variants of existing names, and avoids replacing `API` -inside `CAPITAL`. It applies to Japanese synthesis text; English output, +inside `CAPITAL`. Known names/initialisms followed by plural or possessive +suffixes convert only their stem and preserve the suffix. It applies to Japanese synthesis text; English output, subtitles and conversation history retain their original text. The upstream translation is not exhaustively human-verified. This is an English @@ -154,6 +182,18 @@ word/name extension, not a general context-sensitive Japanese homograph model or a voice/timbre replacement. Native pronunciation/prosody comparisons do not replace end-to-end synthesis and human listening acceptance. +The native release gate compares 52 samples, including Latin units and +inflected names/initialisms, against the actual base `japanese.g2p` prosody path. +It also checks 172 reviewed rows and nine common contractions in three contexts +(543 native contexts) and 181 production frontend/native-prosody comparisons. +An additional 32-unit set has no phoneme changes. It records an `rpm` prosody +boundary difference for listening review: the reading is the same, and that +observation alone does not justify changing an existing accent. +It runs offline with a prepared full pack; core unit tests use synthetic data +and do not require downloading dictionary or model assets. Pack validation +checks the hash before parsing CSV and memoizes successes/failures by member +file stats. Audio cache key generation performs no dictionary-file validation. + ## Publication / 发布 Keep each ZIP version immutable and attach it, with a separately published diff --git a/electron/src/renderer/components/SettingsPage.tsx b/electron/src/renderer/components/SettingsPage.tsx index 4553b219..32e91533 100644 --- a/electron/src/renderer/components/SettingsPage.tsx +++ b/electron/src/renderer/components/SettingsPage.tsx @@ -1125,6 +1125,7 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack aecEnabled: desktop?.sources?.AEC_REALTIME_ENABLED === 'user' ? desktop.values.AEC_REALTIME_ENABLED === 'true' : config.aec_realtime_enabled === undefined ? Boolean(desktopCatalogFields.AEC_REALTIME_ENABLED.default) : bool('aec_realtime_enabled'), emotionReferencesEnabled: backendVoiceConfiguration.find(group => group.id === 'tts_emotion_references')?.fields.find(field => field.key === 'ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING')?.value === true, japaneseDictionaryEnabled: typeof dictionaryRuntimeValue === 'boolean' ? dictionaryRuntimeValue : undefined, + ttsOutputLanguage: val('tts_output_language', 'ja'), }, { ...catalogDesktop, extensions: desktop?.extensions }, locale) const voiceConfiguration: ConfigurationGroup[] = voiceCatalog.map(base => { const backend = backendVoiceConfiguration.find(group => group.id === base.id) @@ -1135,6 +1136,9 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack ...backend, ...base, fields, + // This optional capability also requires a present/loaded native source; + // the enabled startup switch alone does not make it active. + ...(base.id === 'tts_japanese_dictionary' ? { active: Boolean(base.active && backend.active) } : {}), configured: backend.configured ?? base.configured, status: backend.status || base.status, status_ok: backend.status_ok ?? base.status_ok, diff --git a/electron/src/renderer/components/voiceConfigurationCatalog.ts b/electron/src/renderer/components/voiceConfigurationCatalog.ts index f0344962..9a5429e1 100644 --- a/electron/src/renderer/components/voiceConfigurationCatalog.ts +++ b/electron/src/renderer/components/voiceConfigurationCatalog.ts @@ -17,6 +17,7 @@ interface VoiceRuntimeSelection { aecEnabled: boolean emotionReferencesEnabled?: boolean japaneseDictionaryEnabled?: boolean + ttsOutputLanguage?: string } export function buildVoiceConfigurationCatalog( @@ -115,7 +116,7 @@ export function buildVoiceConfigurationCatalog( { ...dictionary, - active: ttsBackend === 'gpt_sovits' && dictionaryEnabled, + active: ttsBackend === 'gpt_sovits' && dictionaryEnabled && ['日文', 'ja'].includes(selection.ttsOutputLanguage || value('TTS_OUTPUT_LANGUAGE')), configured: !dictionaryEnabled, status: dictionaryEnabled ? unknown : 'Off', status_ok: !dictionaryEnabled, diff --git a/electron/tests/voiceConfigurationCatalog.test.mjs b/electron/tests/voiceConfigurationCatalog.test.mjs index ce603620..dcbeb02d 100644 --- a/electron/tests/voiceConfigurationCatalog.test.mjs +++ b/electron/tests/voiceConfigurationCatalog.test.mjs @@ -100,6 +100,10 @@ test('Japanese dictionaries are optional and can be disabled with a restart', () const effective = exports.buildVoiceConfigurationCatalog({ ...selection, japaneseDictionaryEnabled: false }, null).find(group => group.id === 'tts_japanese_dictionary') assert.equal(effective.fields[0].value, false) assert.equal(effective.active, false) + const english = exports.buildVoiceConfigurationCatalog(selection, { values: { TTS_OUTPUT_LANGUAGE: '英文' } }).find(group => group.id === 'tts_japanese_dictionary') + assert.equal(english.active, false) + const switched = exports.buildVoiceConfigurationCatalog({ ...selection, ttsOutputLanguage: 'en' }, { values: { TTS_OUTPUT_LANGUAGE: '日文' } }).find(group => group.id === 'tts_japanese_dictionary') + assert.equal(switched.active, false) }) test('effective dotenv emotion setting is visible until a stored override changes it', () => { diff --git a/local_tts_infer.py b/local_tts_infer.py index 1608c124..3c0aab2d 100644 --- a/local_tts_infer.py +++ b/local_tts_infer.py @@ -221,6 +221,10 @@ def __init__(self, # 初始化语言字典 self._init_language_dict() + # Establish the native pronunciation identity before backend ready + # and the host's first-sentence cache lookup, in either process. + if _default_lang_code() == "ja": + __import__("text.japanese") # 初始化SSL模型。BERT 特征只有中文文本需要,日文/英文用零向量, # 所以 BERT 在第一次需要时才加载(_ensure_bert_model)。 diff --git a/server/handlers/system_handler.py b/server/handlers/system_handler.py index b9b8a6cb..6c87c12e 100644 --- a/server/handlers/system_handler.py +++ b/server/handlers/system_handler.py @@ -130,9 +130,8 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No emotion_runtime = tts_pipeline.emotion_reference_status() emotion_requested = bool(settings.ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING) from config.japanese_dictionary import PACK_ID as DICTIONARY_PACK_ID - from tts.japanese_dictionary import dictionary_runtime_status dictionary_pack = external_asset_pack_status(DICTIONARY_PACK_ID) - dictionary_runtime = dictionary_runtime_status() + dictionary_runtime = tts_pipeline.japanese_dictionary_status() dictionary_enabled = bool(settings.ENABLE_JAPANESE_USER_DICTIONARY) dictionary_state = "disabled" if not dictionary_enabled else ( dictionary_runtime["state"] if dictionary_runtime["state"] != "not_loaded" else dictionary_pack["state"] @@ -249,11 +248,15 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No for group in voice_backend_groups()), { **_catalog_configuration("tts_japanese_dictionary", settings), - "active": tts_selected == "gpt_sovits" and dictionary_enabled, + "active": (tts_selected == "gpt_sovits" and tts_pipeline.current_tts_language_code() == "ja" + and dictionary_enabled and dictionary_state not in {"not_installed", "not_loaded"}), "configured": not dictionary_enabled or dictionary_pack["installed"] or dictionary_runtime["ready"], "status": dictionary_state, - "status_ok": dictionary_state in {"disabled", "ready", "installed"}, - "status_detail": (dictionary_pack.get("message") or dictionary_runtime["detail"]) + ( + "status_ok": dictionary_state in {"disabled", "ready", "installed", "not_installed"}, + "status_detail": (dictionary_pack.get("message") or ( + "Optional dictionary is not installed; base Japanese speech remains available." + if dictionary_state == "not_installed" else dictionary_runtime["detail"] + )) + ( " Pack version: " + dictionary_pack["version"] if dictionary_pack.get("version") else "" ), }, diff --git a/tests/test_first_sentence_audio_cache.py b/tests/test_first_sentence_audio_cache.py index 46e884eb..ba1159b8 100644 --- a/tests/test_first_sentence_audio_cache.py +++ b/tests/test_first_sentence_audio_cache.py @@ -6,6 +6,7 @@ from types import SimpleNamespace import numpy as np +import pytest from tts import first_sentence_audio_cache as cache_module from tts.first_sentence_audio_cache import FirstSentenceAudioCache @@ -20,6 +21,7 @@ def test_prebuilt_expanding_pronunciation_hits_runtime_cache(tmp_path, monkeypat monkeypatch.setattr(prebuild, "get_first_sentence_audio_cache", lambda: cache) monkeypatch.setattr(pipeline, "get_first_sentence_audio_cache", lambda: cache) monkeypatch.setattr(pipeline, "TTS_OUTPUT_LANGUAGE", "日文") + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: {"identity": "test-native"}) monkeypatch.setattr(pipeline, "_tts_interrupt_epoch", 7) inferred, played = [], [] @@ -56,8 +58,8 @@ def _current_params() -> dict: "そういえば,まともに自己紹介していませんでしたね……" "牧瀬くりすです.改めまして,よろしく" ), - "text_language": "日文", - "prompt_language": "日文", + "text_language": "英文", + "prompt_language": "英文", "how_to_cut": "不切", "top_p": 1, "top_k": 5, @@ -83,7 +85,7 @@ def _configure_current_identity(monkeypatch) -> None: "TTS_SOVITS_MODEL_PATH", "assets/models/gpt-sovits/weights/sovits/v3/xxx_e2_s174_l32.pth", ) - monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "en") def _write_entry( @@ -131,7 +133,7 @@ def test_legacy_asset_layout_cache_is_reused_and_migrated( cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "ん、" - legacy_payload = cache._legacy_key_payloads(text, params)[0] + legacy_payload = cache._legacy_key_payloads(cache.key_payload(text, params))[0] audio = np.linspace(-0.2, 0.2, 2400, dtype=np.float32) _write_entry(cache, legacy_payload, audio) @@ -160,7 +162,7 @@ def test_legacy_cache_without_metadata_remains_usable( cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "え、" - legacy_payload = cache._legacy_key_payloads(text, params)[0] + legacy_payload = cache._legacy_key_payloads(cache.key_payload(text, params))[0] audio = np.ones(1200, dtype=np.float32) * 0.05 _write_entry(cache, legacy_payload, audio, metadata=False) @@ -180,7 +182,7 @@ def test_legacy_cache_with_mismatched_identity_is_rejected( cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "ああ、" - legacy_payload = cache._legacy_key_payloads(text, params)[0] + legacy_payload = cache._legacy_key_payloads(cache.key_payload(text, params))[0] mismatched = dict(legacy_payload) mismatched["gpt_model"] = "GPT_weights_v3/different-model.ckpt" _write_entry( @@ -194,11 +196,16 @@ def test_legacy_cache_with_mismatched_identity_is_rejected( assert cache.path_for(text, params).exists() is False +@pytest.mark.parametrize("language", ["ja", "en"]) def test_previous_synthesis_revisions_are_not_reused( tmp_path, monkeypatch, + language, ) -> None: _configure_current_identity(monkeypatch) + from tts import pipeline + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: language) + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: {"identity": "native"}) cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "うーん……" @@ -229,7 +236,7 @@ def test_legacy_cache_with_malformed_metadata_is_rejected( cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "そうね、" - legacy_payload = cache._legacy_key_payloads(text, params)[0] + legacy_payload = cache._legacy_key_payloads(cache.key_payload(text, params))[0] path = cache._path_for_payload(legacy_payload) path.parent.mkdir(parents=True, exist_ok=True) with path.open("wb") as stream: @@ -252,7 +259,7 @@ def test_current_cache_identity_takes_precedence_over_legacy( cache = FirstSentenceAudioCache(tmp_path) params = _current_params() text = "わかったわ。" - legacy_payload = cache._legacy_key_payloads(text, params)[0] + legacy_payload = cache._legacy_key_payloads(cache.key_payload(text, params))[0] legacy_audio = np.ones(900, dtype=np.float32) * 0.1 current_audio = np.ones(600, dtype=np.float32) * 0.2 _write_entry(cache, legacy_payload, legacy_audio) diff --git a/tests/test_gpt_sovits_sidecar_backend.py b/tests/test_gpt_sovits_sidecar_backend.py index ce5e59ac..8327e5f1 100644 --- a/tests/test_gpt_sovits_sidecar_backend.py +++ b/tests/test_gpt_sovits_sidecar_backend.py @@ -118,6 +118,7 @@ def chunk(request_id, values, text): "torch": "test+rocm", "hip": "7.test", "cuda_available": True, + "japanese_dictionary": {"state": "ready", "ready": True, "identity": "worker-before"}, }) for line in sys.stdin: message = json.loads(line) @@ -138,7 +139,8 @@ def chunk(request_id, values, text): request["options"]["marker"], ]) chunk(request_id, [0.25, -0.5], observed) - emit({"type": "done", "request_id": request_id}) + emit({"type": "done", "request_id": request_id, + "japanese_dictionary": {"state": "ready", "ready": True, "identity": "worker-native"}}) '''.lstrip(), encoding="utf-8", ) @@ -171,8 +173,10 @@ def test_sidecar_round_trip_preserves_request_and_float32_audio(tmp_path, monkey "torch": "test+rocm", "hip": "7.test", "cuda_available": True, + "japanese_dictionary": {"state": "ready", "ready": True, "identity": "worker-native"}, } assert backend.deployment == "subprocess" + assert backend.japanese_dictionary_status["identity"] == "worker-native" assert TTSRuntimeAdapter(backend).is_rocm is True assert chunk.sample_rate == 24000 assert chunk.audio.dtype == np.float32 diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py index 8ffdff72..8e5fa35a 100644 --- a/tests/test_japanese_dictionary.py +++ b/tests/test_japanese_dictionary.py @@ -26,13 +26,9 @@ @pytest.fixture(autouse=True) def clear_runtime_sources(monkeypatch): monkeypatch.setattr(dictionary, "_runtime_status", { - "state": "not_loaded", "ready": False, "version": "", "detail": "Not loaded in this test.", + "state": "not_loaded", "ready": False, "version": "", "identity": None, "detail": "Not loaded in this test.", }) - dictionary._sources.cache_clear() - dictionary._digest.cache_clear() yield - dictionary._sources.cache_clear() - dictionary._digest.cache_clear() def _prepare(root: Path, *, native: bool = False) -> Path: @@ -66,7 +62,7 @@ def _prepare(root: Path, *, native: bool = False) -> Path: ("mlpとMLPとAPI", "mlpとエムエルピーとエーピーアイ"), ("openaiとOPENAIとBilibili", "オープンエーアイとオープンエーアイとビリビリ"), ("APIaryとCAPITAL", "APIaryとCAPITAL"), - ("API's and APIs", "API's and APIs"), + ("API's and APIs", "エーピーアイ's and エーピーアイs"), ("今日は生ビールを飲む。", "今日は生ビールを飲む。"), ]) def test_latin_words_and_initialisms_are_complete_tokens(text, expected): @@ -241,7 +237,7 @@ def test_native_mora_counter_uses_pronunciation_without_retokenizing_kana(): assert count(pronunciation) == expected -def test_native_surface_spelling_is_applied_only_to_repaired_readings(): +def test_native_surface_spelling_is_applied_to_all_entries(): pytest.importorskip("pyopenjtalk") count, _ = native_mora_counter() surface = native_surface_normalizer() @@ -250,8 +246,11 @@ def test_native_surface_spelling_is_applied_only_to_repaired_readings(): _row("already-valid", "テスト", "0/3")] cleaned, report = normalize_rows(rows, count, surface) assert cleaned[0][0] == "coca−cola" and cleaned[0][12] == "コカコーラ" - assert cleaned[1:] == rows[1:] - assert report["repaired_surfaces_normalized"] == 1 + assert cleaned[1][0] == surface(rows[1][0]) + assert cleaned[1][1:] == rows[1][1:] + assert cleaned[2][0] == surface(rows[2][0]) + assert cleaned[2][1:] == rows[2][1:] + assert report["surfaces_normalized"] == 3 def _fake_jtalk(root: Path): @@ -281,7 +280,11 @@ def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_ jtalk = _fake_jtalk(root) status = dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) assert status["ready"] - assert jtalk.compiled == [str(pack_root / "userdict.csv"), str(personal)] + assert len(jtalk.compiled) == 2 + assert all(Path(path).is_relative_to(root / ".cache") for path in jtalk.compiled) + assert [Path(path).read_bytes() for path in jtalk.registered[-1]] == [ + b"compiled " + (pack_root / "userdict.csv").read_bytes(), b"compiled " + personal.read_bytes(), + ] assert all(Path(path).is_relative_to(root / ".cache") for path in jtalk.registered[-1]) assert legacy.read_text(encoding="utf-8") == FOLLOWER assert not (pack_root / "user.dict").exists() @@ -303,7 +306,6 @@ def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_ def test_runtime_missing_invalid_and_compile_failure_keep_base_frontend(tmp_path): jtalk = _fake_jtalk(tmp_path) assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "not_installed" - dictionary._sources.cache_clear() pack_root = _prepare(tmp_path) def fail_compile(*args): raise RuntimeError("compiler failed") @@ -312,23 +314,20 @@ def fail_compile(*args): assert jtalk.registered[-1] == [] assert not list((tmp_path / ".cache/japanese_dictionary").glob("*.dict")) (pack_root / "userdict.csv").write_bytes(b"corrupt") - dictionary._sources.cache_clear() assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "invalid" def test_audio_cache_distinguishes_dictionary_selection_and_english_is_independent(tmp_path, monkeypatch): from tts import first_sentence_audio_cache as audio_cache, pipeline - monkeypatch.setattr(dictionary, "PROJECT_ROOT", tmp_path) - enabled = [True] - monkeypatch.setattr(dictionary, "_enabled", lambda: enabled[0]) + status = {"identity": "actual-base"} + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: status) monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") cache = audio_cache.FirstSentenceAudioCache(tmp_path / "audio") without = cache.path_for("Follower。", {}) - _prepare(tmp_path) - dictionary._sources.cache_clear() + status["identity"] = "actual-installed" installed = cache.path_for("Follower。", {}) assert installed != without - enabled[0] = False + status["identity"] = "actual-disabled" assert cache.path_for("Follower。", {}) not in {without, installed} monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "en") assert "japanese_dictionary" not in cache.key_payload("Follower.", {}) @@ -348,9 +347,12 @@ def test_settings_expose_dictionary_failures_and_the_disable_switch(monkeypatch, "version": "test", "message": "" if installed else "CSV checksum failed", } if pack_id == dictionary_pack.PACK_ID else original(pack_id, **kwargs)) monkeypatch.setattr(settings, "ENABLE_JAPANESE_USER_DICTIONARY", enabled) + monkeypatch.setattr(settings, "TTS_OUTPUT_LANGUAGE", "日文") monkeypatch.setattr(dictionary, "_runtime_status", { "state": runtime_state, "ready": False, "version": "test", "detail": "Dictionary compilation failed", }) + from tts import pipeline + monkeypatch.setattr(pipeline, "japanese_dictionary_status", dictionary.dictionary_runtime_status) group = next(item for item in _voice_configuration(settings) if item["id"] == "tts_japanese_dictionary") assert group["status"] == expected assert group["status_ok"] is (not enabled) diff --git a/tests/test_japanese_dictionary_boundaries.py b/tests/test_japanese_dictionary_boundaries.py new file mode 100644 index 00000000..47a7f47e --- /dev/null +++ b/tests/test_japanese_dictionary_boundaries.py @@ -0,0 +1,251 @@ +"""Optional dictionary failures and real pronunciation/cache boundaries.""" +from pathlib import Path +from types import SimpleNamespace + +import numpy as np +import pytest + +from config import japanese_dictionary as pack_data +from tts import japanese_dictionary as runtime +from tools.tts_text_processor import correct_pronunciation_for_tts +from test_japanese_dictionary import _fake_jtalk, _prepare, _row, LEARNER + + +@pytest.fixture(autouse=True) +def isolate_native_status(monkeypatch): + monkeypatch.setattr(runtime, "_runtime_status", { + "state": "not_loaded", "ready": False, "identity": None, "version": "", "detail": "Not loaded", + }) + + +@pytest.mark.parametrize("text,expected", [ + ("APIs GPUs URLs", "エーピーアイs ジーピーユーs ユーアールエルs"), + ("OpenAI's Claude’s GitHub's", "オープンエーアイ's クロード’s ギットハブ's"), + ("MLPsを使う APIary CAPITAL cats", "エムエルピーsを使う APIary CAPITAL cats"), + ("170cm 50kg 60kW 60MW 100ML", "170cm 50kg 60kW 60MW 100ML"), + ("3GPUs 3API 11AI", "3ジーピーユーs 3エーピーアイ 11エーアイ"), +]) +def test_inflected_known_tokens_and_measurements(text, expected): + assert correct_pronunciation_for_tts(text) == expected + + +def test_build_yields_short_latin_fragments_to_the_base_dictionary(): + from tools.audit_japanese_dictionary import normalize_rows + rows = [_row("m", "メモスリー"), _row("w", "ウォーブリュー"), + _row("cm", "シーエム"), _row("don't", "ドント")] + cleaned, stats = normalize_rows(rows, lambda _: None, lambda s: s.replace("'", "’")) + assert len(cleaned) == 1 and cleaned[0][0] == "don’t" + assert cleaned[0][1:] == rows[-1][1:] + assert stats["short_latin_entries_excluded"] == 3 + + +def test_invalid_pack_hash_is_checked_first_and_failure_cached_by_stat(tmp_path, monkeypatch): + root = _prepare(tmp_path) + csv = root / "userdict.csv" + original = csv.read_bytes() + csv.write_bytes(original + b"bad line\n") + calls = [] + validate = pack_data._validate_dictionary_pack + monkeypatch.setattr(pack_data, "_validate_dictionary_pack", lambda p: (calls.append(p), validate(p))[1]) + def unexpected_parse(_): + raise AssertionError("hash mismatch must reject before CSV parsing") + with monkeypatch.context() as patch: + patch.setattr(pack_data, "dictionary_rows", unexpected_parse) + for _ in range(3): + with pytest.raises(pack_data.JapaneseDictionaryError, match="hash"): + pack_data.load_dictionary_pack(root) + assert len(calls) == 1 + csv.write_bytes(original) + assert pack_data.load_dictionary_pack(root).entry_count > 0 + assert len(calls) == 2 + + +@pytest.mark.parametrize("enabled", [True, False]) +def test_unsupported_native_api_keeps_the_optional_boundary_nonfatal(tmp_path, enabled): + status = runtime.configure_japanese_dictionary(SimpleNamespace(), project_root=tmp_path, enabled=enabled) + assert status["state"] == "unsupported" and status["identity"] is None + + +def test_configuration_and_reset_failures_do_not_escape(tmp_path, monkeypatch): + def fail(*_): + raise AttributeError("native unavailable") + monkeypatch.setattr(runtime, "_enabled", fail) + assert runtime.configure_japanese_dictionary(SimpleNamespace(), project_root=tmp_path)["identity"] is None + jtalk = _fake_jtalk(tmp_path) + jtalk.unset_user_dict = fail + assert runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["identity"] is None + + +@pytest.mark.parametrize("personal", [b"", b"\xef\xbb\xbf" + LEARNER.encode(), LEARNER.replace("1/5", "*").encode()]) +def test_bad_personal_csv_preserves_the_official_pack_and_names_source(tmp_path, personal): + _prepare(tmp_path) + jtalk = _fake_jtalk(tmp_path) + good = runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True) + (tmp_path / "assets/text/ja/user.csv").write_bytes(personal) + bad = runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True) + assert bad["ready"] and bad["identity"] == good["identity"] + assert len(jtalk.registered[-1]) == 1 + assert "assets/text/ja/user.csv" in bad["detail"] + if personal.startswith(b"\xef\xbb\xbf"): + assert "BOM" in bad["detail"] + + +def test_native_registration_failure_preserves_previously_loaded_sources(tmp_path): + _prepare(tmp_path) + jtalk = _fake_jtalk(tmp_path) + (tmp_path / "assets/text/ja/user.csv").write_text(LEARNER, encoding="utf-8") + register = jtalk.update_global_jtalk_with_user_dict + def reject_personal(paths): + if len(paths) == 2: + raise TypeError("personal registration failed") + register(paths) + jtalk.update_global_jtalk_with_user_dict = reject_personal + status = runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True) + assert status["ready"] and len(jtalk.registered[-1]) == 1 + assert "user.csv" in status["detail"] + + +def test_compilation_reuses_a_peer_published_file_and_prunes_obsolete_cache(tmp_path, monkeypatch): + _prepare(tmp_path) + jtalk = _fake_jtalk(tmp_path) + cache = tmp_path / ".cache/japanese_dictionary" + cache.mkdir(parents=True) + old = cache / ("a" * 64 + ".dict") + old.write_bytes(b"obsolete") + def peer_wins(source, target): + Path(target).write_bytes(Path(source).read_bytes()) + raise PermissionError("peer already mmap'ed target") + monkeypatch.setattr(runtime.os, "replace", peer_wins) + assert runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["ready"] + assert not old.exists() + assert len(list(cache.glob("*.dict"))) == 1 + + +def test_native_units_and_contractions_against_base_frontend(tmp_path): + jtalk = pytest.importorskip("pyopenjtalk") + import hashlib + import json + import sys + from tools.prepare_japanese_dictionary import prepare_dictionary + root = Path(__file__).resolve().parents[1] + sys.path.insert(0, str(root / "GPT_SoVITS")) + from text import japanese + rows = [_row("m", "メモスリー"), _row("w", "ウォーブリュー"), _row("h", "ヘッチ"), + _row("kg", "ケイジー"), _row("don't", "ドント", "0/3"), _row("i'm", "アイム", "0/3")] + data = ("\n".join(",".join(row) for row in rows) + "\n").encode() + source = tmp_path / "upstream.csv" + source.write_bytes(data) + recipe = json.loads((root / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) + recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(rows)) + recipe_path = tmp_path / "recipe.json" + recipe_path.write_text(json.dumps(recipe), encoding="utf-8") + prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path) + texts = ("身長は170cmです", "50kgです", "3kmです", "10mmです", "100mlです", "60kWです", "60MWです", "APIsを使う", "OpenAI's API") + try: + jtalk.unset_user_dict() + before = [japanese.g2p(correct_pronunciation_for_tts(text)) for text in texts] + assert runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["ready"] + assert [japanese.g2p(correct_pronunciation_for_tts(text)) for text in texts] == before + for word, reading in (("don't", "ドント"), ("I'm", "アイム")): + assert "".join(f["read"] for f in jtalk.run_frontend(word.lower())) == reading + assert "UNK" not in japanese.g2p(word) + finally: + jtalk.unset_user_dict() + + +def test_audio_cache_uses_worker_selection_and_skips_unknown_and_japanese_legacy(tmp_path, monkeypatch): + from tts import pipeline, first_sentence_audio_cache as cache_module + from tts.backend import TTSRuntimeAdapter + from tts.backends.gpt_sovits import GPTSoVITSBackend + backend = GPTSoVITSBackend() + backend.deployment = "subprocess" + backend._proc = SimpleNamespace(poll=lambda: None) + backend._ready_info = {"japanese_dictionary": {"identity": "actual-base", "state": "invalid"}} + monkeypatch.setattr(runtime, "_runtime_status", {"identity": "host-installed"}) + monkeypatch.setattr(pipeline, "_tts_runtime", TTSRuntimeAdapter(backend)) + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") + monkeypatch.setattr(cache_module, "FIRST_SENTENCE_AUDIO_CACHE_ENABLED", True) + cache = cache_module.FirstSentenceAudioCache(tmp_path) + assert cache.key_payload("API", {})["japanese_dictionary"] == "actual-base" + assert cache.store("API", {}, 24000, np.ones(24)) + backend._ready_info["japanese_dictionary"]["identity"] = "actual-pack" + assert cache.lookup("API", {}) is None + assert cache._legacy_key_payloads(cache.key_payload("API", {})) == () + assert cache.store("API", {}, 24000, np.ones(24)) + assert cache.lookup("API", {}) is not None + backend._ready_info["japanese_dictionary"]["identity"] = None + assert not cache.store("API", {}, 24000, np.ones(24)) + assert cache.lookup("API", {}) is None + + +def test_cache_store_snapshots_native_identity_once(tmp_path, monkeypatch): + from tts import pipeline, first_sentence_audio_cache as cache_module + calls = [] + def status(): + calls.append(1) + return {"identity": "native"} + monkeypatch.setattr(pipeline, "japanese_dictionary_status", status) + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") + monkeypatch.setattr(cache_module, "FIRST_SENTENCE_AUDIO_CACHE_ENABLED", True) + cache = cache_module.FirstSentenceAudioCache(tmp_path) + assert cache.store("API", {}, 24000, np.ones(24)) + assert len(calls) == 1 + + +def test_offline_prebuild_refreshes_the_identity_after_native_startup(tmp_path, monkeypatch): + from tts import pipeline, first_sentence_audio_cache as cache_module + from tools import prebuild_first_sentence_audio_cache as prebuild + status = {"identity": None} + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: status) + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "ja") + monkeypatch.setattr(cache_module, "FIRST_SENTENCE_AUDIO_CACHE_ENABLED", True) + cache = cache_module.FirstSentenceAudioCache(tmp_path) + monkeypatch.setattr(prebuild, "get_first_sentence_audio_cache", lambda: cache) + planned = prebuild._prepare_item("こんにちは。") + status["identity"] = "native-loaded" + calls = [] + class Inferencer: + def infer_stream(self, **kwargs): + calls.append(kwargs) + yield 24000, np.ones(24), kwargs["text"] + result, _, path = prebuild._synthesize_to_cache(Inferencer(), planned, False) + assert result.startswith("stored") and path != planned.cache_path and path.is_file() + assert prebuild._synthesize_to_cache(Inferencer(), planned, False)[0] == "hit" + assert len(calls) == 1 + + +@pytest.mark.parametrize("language", ["日文", "英文"]) +def test_absent_pack_is_optional_and_inactive(tmp_path, monkeypatch, language): + from config import asset_packages, settings + from server.handlers.system_handler import _voice_configuration + from tts import pipeline + original = asset_packages.external_asset_pack_status + monkeypatch.setattr(asset_packages, "external_asset_pack_status", lambda pack_id, **kwargs: { + "installed": False, "state": "not_installed", "version": "", "message": "", + } if pack_id == pack_data.PACK_ID else original(pack_id, **kwargs)) + monkeypatch.setattr(settings, "TTS_BACKEND", "gpt_sovits") + monkeypatch.setattr(settings, "TTS_OUTPUT_LANGUAGE", language) + monkeypatch.setattr(settings, "ENABLE_JAPANESE_USER_DICTIONARY", True) + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: {"state": "not_loaded", "ready": False, "detail": "Not loaded"}) + group = next(g for g in _voice_configuration(settings) if g["id"] == "tts_japanese_dictionary") + assert not group["active"] and not group["configured"] and group["status_ok"] + assert "base Japanese" in group["status_detail"] + + +def test_live_english_output_does_not_activate_an_installed_pack(monkeypatch): + from config import asset_packages, settings + from server.handlers.system_handler import _voice_configuration + from tts import pipeline + original = asset_packages.external_asset_pack_status + monkeypatch.setattr(asset_packages, "external_asset_pack_status", lambda pack_id, **kwargs: { + "installed": True, "state": "installed", "version": "test", "message": "", + } if pack_id == pack_data.PACK_ID else original(pack_id, **kwargs)) + monkeypatch.setattr(settings, "TTS_BACKEND", "gpt_sovits") + monkeypatch.setattr(settings, "TTS_OUTPUT_LANGUAGE", "日文") + monkeypatch.setattr(settings, "ENABLE_JAPANESE_USER_DICTIONARY", True) + monkeypatch.setattr(pipeline, "current_tts_language_code", lambda: "en") + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: { + "state": "ready", "ready": True, "detail": "Loaded", + }) + group = next(g for g in _voice_configuration(settings) if g["id"] == "tts_japanese_dictionary") + assert not group["active"] and group["configured"] and group["status_ok"] diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py index b8e2a8f3..a44d3f4f 100644 --- a/tools/audit_japanese_dictionary.py +++ b/tools/audit_japanese_dictionary.py @@ -63,12 +63,18 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None """Apply representation repairs; preserve lexical alternatives and accent nuclei.""" cleaned, seen = [], set() stats = {"unicode_normalized_rows": 0, "literal_initialism_readings_repaired": 0, - "reading_separators_normalized": 0, "repaired_surfaces_normalized": 0, + "reading_separators_normalized": 0, "surfaces_normalized": 0, + "short_latin_entries_excluded": 0, "mora_counts_repaired": 0, "exact_duplicates_removed": 0} unresolved = [] for number, original in enumerate(rows, 1): row = list(original) - separator_repaired = False + # The Japanese base dictionary owns letters and short Latin tokens, + # including measurement suffixes. Imported English fragments must not + # displace cm/kg/km or supply unrelated readings for isolated m/w/h. + if re.fullmatch(r"[A-Za-z]{1,2}", unicodedata.normalize("NFKC", row[0])): + stats["short_latin_entries_excluded"] += 1 + continue row[10:13] = [unicodedata.normalize("NFC", value) for value in row[10:13]] stats["unicode_normalized_rows"] += row != original joined = [_without_reading_separators(value) for value in row[10:13]] @@ -77,7 +83,6 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None accent = int(row[13].split("/")[0]) if actual is None or 0 < actual and accent <= actual: row[10:13] = joined - separator_repaired = True stats["reading_separators_normalized"] += 1 literal = unicodedata.normalize("NFKC", row[12]) if (re.fullmatch(r"[A-Z]{2,}", literal) @@ -90,7 +95,6 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None if _KANA.fullmatch(row[12]): actual = count_mora(row[12]) accent, declared = map(int, row[13].split("/")) - valid_reading = actual is None or 0 < actual and accent <= actual if actual is not None and actual != declared: if 0 < actual and accent <= actual: row[13] = f"{accent}/{actual}" @@ -99,14 +103,13 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None unresolved.append({"line": number, "surface": row[0], "pronunciation": row[12], "reason": "accent_needs_review", "declared": row[13], "native_mora": actual}) else: - valid_reading = False unresolved.append({"line": number, "surface": row[0], "pronunciation": row[12], "reason": "non_kana_reading_needs_review"}) - # Activate a repaired reading using the native input spelling (e.g. ’ - # and −). Leave unreviewed, unchanged entries exactly as they were. - if normalize_surface is not None and valid_reading and separator_repaired: + # Surface spelling is a representation rule for every entry, including + # unchanged contractions/compounds and readings awaiting lexical review. + if normalize_surface is not None: surface = normalize_surface(row[0]) - stats["repaired_surfaces_normalized"] += surface != row[0] + stats["surfaces_normalized"] += surface != row[0] row[0] = surface entry = tuple(row) if entry in seen: diff --git a/tools/data/japanese_dictionary_source.json b/tools/data/japanese_dictionary_source.json index b477ae31..1a13654a 100644 --- a/tools/data/japanese_dictionary_source.json +++ b/tools/data/japanese_dictionary_source.json @@ -1,5 +1,5 @@ { - "version": "2026.10.16.3", + "version": "2026.10.18.1", "language": "ja", "source": { "repository": "https://github.com/RVC-Boss/GPT-SoVITS", @@ -30,7 +30,8 @@ "spell untranslated whole-token initialisms", "recount morae from one explicit native pronunciation while preserving valid accent nuclei", "remove only exact duplicate entries", - "canonicalize native MeCab surface spelling only for repaired entries; leave unchanged, unreviewed surfaces intact" + "yield all one- or two-letter Latin surfaces to the base Japanese dictionary", + "canonicalize every surface using native MeCab input spelling, including unchanged contractions and compounds" ], "unresolved_policy": "Flag non-kana remnants and out-of-range accent candidates for review; retain them without guessing a replacement. Preserve different legitimate readings of the same word." }, diff --git a/tools/prebuild_first_sentence_audio_cache.py b/tools/prebuild_first_sentence_audio_cache.py index 39ea8e82..92051bd9 100644 --- a/tools/prebuild_first_sentence_audio_cache.py +++ b/tools/prebuild_first_sentence_audio_cache.py @@ -420,6 +420,9 @@ def _load_inferencer(): def _synthesize_to_cache(inferencer, item: CacheItem, force: bool) -> tuple[str, float, Path]: + # Planning can precede native frontend startup. Recompute the real loaded + # pronunciation identity before checking or reporting the cache entry. + item = _prepare_item(item.raw_text) if item.cached and not force: return "hit", 0.0, item.cache_path diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py index e10979d7..a9e05273 100644 --- a/tools/prepare_japanese_dictionary.py +++ b/tools/prepare_japanese_dictionary.py @@ -50,29 +50,19 @@ def normalize(value: str) -> str: # A repair targets the reviewed bad pronunciation, not every reading of # the same surface. Preserve legitimate alternatives and their costs. corrected = [] - repaired_surfaces = 0 for row in rows: word = normalize(row[0]) expected = before.get(word) matches = expected is not None and unicodedata.normalize("NFC", row[12]) == unicodedata.normalize("NFC", expected) if word in corrections and matches: row = list(corrections[word]) - if normalize_surface is not None: - surface = normalize_surface(row[0]) - repaired_surfaces += surface != row[0] - row[0] = surface corrected.append(row) existing = {normalize(row[0]) for row in rows} for word, correction in corrections.items(): if word not in existing: row = list(correction) - if normalize_surface is not None: - surface = normalize_surface(row[0]) - repaired_surfaces += surface != row[0] - row[0] = surface corrected.append(row) corrected, normalization = normalize_rows(corrected, count_mora, normalize_surface) - normalization["repaired_surfaces_normalized"] += repaired_surfaces buffer = io.StringIO(newline="") csv.writer(buffer, lineterminator="\n").writerows(corrected) derivative = buffer.getvalue().encode("utf-8") diff --git a/tools/tts_text_processor.py b/tools/tts_text_processor.py index cc26953b..e1c5ad6c 100644 --- a/tools/tts_text_processor.py +++ b/tools/tts_text_processor.py @@ -108,6 +108,17 @@ def _convert(match: re.Match) -> str: known = _SPECIAL_CASES_FOLDED.get(token.casefold()) if known: return known + # Unknown short number suffixes belong to the native unit lexicon, + # including uppercase units. Known names and longer initialisms (e.g. + # 3GPUs) keep their existing pronunciation contract. + if len(token) <= 2 and match.start() and text[match.start() - 1].isdecimal(): + return token + suffix = re.search(r"(?:['’][sS]|s)$", token) + if suffix: + stem = token[:suffix.start()] + known = _SPECIAL_CASES_FOLDED.get(stem.casefold()) + if known or _INITIALISM_PATTERN.fullmatch(stem): + return (known or "".join(_LETTER_TO_KATAKANA.get(ch.upper(), ch) for ch in stem)) + suffix.group() if not _INITIALISM_PATTERN.fullmatch(token): return token return "".join( diff --git a/tools/validate_japanese_dictionary.py b/tools/validate_japanese_dictionary.py new file mode 100644 index 00000000..03d33603 --- /dev/null +++ b/tools/validate_japanese_dictionary.py @@ -0,0 +1,117 @@ +"""Offline native release gate for a prepared Japanese pronunciation pack.""" +from __future__ import annotations + +import argparse +from collections import defaultdict +import json +from pathlib import Path +import sys + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + +from config.japanese_dictionary import PACK_TREE, dictionary_rows, load_dictionary_pack # noqa: E402 +from tools.tts_text_processor import correct_pronunciation_for_tts # noqa: E402 +from tts.japanese_dictionary import configure_japanese_dictionary # noqa: E402 + +# Numeric Latin suffixes, inflected initialisms/names, native Japanese and +# punctuation exercise both sides of the optional pack's authority boundary. +BASE_SAMPLES = ( + "身長は170cmです", "体重は50kgです", "距離は3kmです", "長さは10mmです", + "量は100mlです", "出力は60kWです", "出力は60MWです", "5mを歩く", + "7hです", "100msです", "周波数は50Hzです", "室温は20℃です", "100MLです", + "APIsを使う", "GPUsを使う", "URLsを使う", "OpenAI's API", "Claude’s API", "GitHub's API", + "今日は生ビールを飲みます。", "生きることは大切です。", "生徒が学校へ行く。", + "昨日は雨が降った。", "明日は晴れるでしょう。", "東京駅で電車に乗る。", + "日本橋を渡ります。", "日本語を勉強しています。", "彼は上手に歌います。", + "部屋の上手に立つ。", "人気のある店です。", "人の気配がない。", + "今日の市場はにぎやかです。", "市場価格を確認する。", "一日中働きました。", + "四月一日に出発します。", "大人になった。", "小豆を煮る。", "明後日にまた会いましょう。", + "紅茶を飲みたい。", "行ったことがあります。", "銀行へ行く。", "この行を読みます。", + "重い荷物を運ぶ。", "荷物を重ねます。", "橋の端で箸を使う。", "雨が降り、飴を買った。", + "花が咲き、鼻がむずむずする。", "新しい本を読む。", "研究結果を発表します。", + "音声合成の品質を確認する。", "こんにちは、元気ですか。", "猫-犬。", +) +CONTRACTIONS = ("don't", "won't", "I'm", "I've", "we're", "they're", "isn't", "can't", "wouldn't") +UNIT_SAMPLES = tuple("100" + unit + "です" for unit in ( + "g", "mg", "kg", "t", "m", "cm", "mm", "km", "ml", "mL", "L", "kW", "MW", "W", "V", "mV", + "A", "mA", "Hz", "kHz", "MHz", "GHz", "s", "ms", "ns", "kB", "MB", "GB", "TB", "dB", "rpm", "℃", +)) + + +def validate(project_root: Path) -> dict: + import pyopenjtalk + sys.path.insert(0, str(ROOT / "GPT_SoVITS")) + from text import japanese + project_root = project_root.resolve() + pack = load_dictionary_pack(project_root / "assets" / PACK_TREE) + rows = list(dictionary_rows((pack.root / "userdict.csv").read_bytes())) + jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) + surface = jtalk.normalize_for_mecab + expected = defaultdict(set) + for row in rows: + expected[row[0]].add(row[11]) + failures, contexts, frontend_words = [], 0, 0 + try: + pyopenjtalk.unset_user_dict() + baseline = {text: japanese.g2p(correct_pronunciation_for_tts(text)) for text in BASE_SAMPLES} + unit_baseline = {text: japanese.g2p(correct_pronunciation_for_tts(text)) for text in UNIT_SAMPLES} + status = configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=True) + if not status["ready"]: + raise RuntimeError(status["detail"]) + for text, before in baseline.items(): + after = japanese.g2p(correct_pronunciation_for_tts(text)) + if after != before: + failures.append({"kind": "base_regression", "text": text, "before": before, "after": after}) + unit_prosody_changes = [] + without_prosody = lambda phones: [phone for phone in phones if phone not in {"[", "]", "#"}] + for text, before in unit_baseline.items(): + after = japanese.g2p(correct_pronunciation_for_tts(text)) + if without_prosody(after) != without_prosody(before): + failures.append({"kind": "unit_phonemes", "text": text, "before": before, "after": after}) + elif after != before: + # A different accent boundary alone does not establish a wrong + # reading. Record it for listening review without inventing an + # accent correction or a per-unit exclusion. + unit_prosody_changes.append({"text": text, "before": before, "after": after}) + corrected = list(dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) + words = {row[0]: row[0] for row in corrected} + words.update({surface(word): word for word in CONTRACTIONS}) + for key, word in words.items(): + key = surface(key.lower()) + for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): + whole = [f for f in pyopenjtalk.run_frontend(prefix + word.lower() + suffix) if f["string"] == key] + contexts += 1 + if len(whole) != 1 or whole[0]["read"] not in expected[key]: + failures.append({"kind": "whole_word", "word": word, "prefix": prefix, "suffix": suffix, "observed": whole}) + # Use the actual production prosody path, not only native row lookup. + phones = japanese.g2p(word) + native = [japanese.post_replace_ph(p) for p in japanese.pyopenjtalk_g2p_prosody(word.lower())[1:-1]] + frontend_words += 1 + if not phones or "UNK" in phones or phones != native: + failures.append({"kind": "frontend", "word": word, "phones": phones}) + return {"version": pack.version, "entry_count": pack.entry_count, "sha256": pack.sha256, + "base_samples": len(BASE_SAMPLES), "native_contexts": contexts, + "additional_unit_phoneme_checks": len(UNIT_SAMPLES), + "unit_prosody_changes_for_review": unit_prosody_changes, + "production_frontend_words": frontend_words, "failures": failures, + "limitations": "Native phoneme/prosody and token-selection checks; no waveform or listening acceptance."} + finally: + pyopenjtalk.unset_user_dict() + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--project-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + report = validate(args.project_root) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps({key: value for key, value in report.items() if key != "failures"})) + print(f"Failures: {len(report['failures'])}") + return int(bool(report["failures"])) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tts/backends/gpt_sovits.py b/tts/backends/gpt_sovits.py index 3d190fef..0f239255 100644 --- a/tts/backends/gpt_sovits.py +++ b/tts/backends/gpt_sovits.py @@ -94,6 +94,16 @@ def _configure_emotion_references(self) -> None: "detail": "Emotion reference pack ready; all references prewarmed."} logger.info("[TTS-EMOTION] prewarmed %d distinct references in %.3fs", len(refs), elapsed) + @property + def japanese_dictionary_status(self) -> dict: + if self.deployment == "subprocess": + status = self._ready_info.get("japanese_dictionary") if self._is_running() else None + return dict(status) if isinstance(status, dict) else {} + if self._inferencer is None: + return {} + from tts.japanese_dictionary import dictionary_runtime_status + return dictionary_runtime_status() + @property def is_rocm(self) -> bool: if self.deployment == "subprocess": @@ -301,6 +311,8 @@ def _messages_for_request_locked( yield self._decode_chunk(message) continue if kind == "done": + if isinstance(message.get("japanese_dictionary"), dict): + self._ready_info["japanese_dictionary"] = dict(message["japanese_dictionary"]) return if kind == "error": raise TTSBackendError( diff --git a/tts/first_sentence_audio_cache.py b/tts/first_sentence_audio_cache.py index e2b4212e..5448b316 100644 --- a/tts/first_sentence_audio_cache.py +++ b/tts/first_sentence_audio_cache.py @@ -89,13 +89,10 @@ def key_payload(self, text: str, params: dict[str, Any]) -> dict[str, Any]: "params": {field: params.get(field) for field in _KEY_FIELDS}, } if output_language == "ja": - from tts.japanese_dictionary import dictionary_cache_identity - payload["japanese_dictionary"] = dictionary_cache_identity() + from tts.pipeline import japanese_dictionary_status + payload["japanese_dictionary"] = japanese_dictionary_status().get("identity") return payload - def _fingerprint(self, text: str, params: dict[str, Any]) -> str: - return self._fingerprint_payload(self.key_payload(text, params)) - @staticmethod def _fingerprint_payload(payload: dict[str, Any]) -> str: raw = json.dumps(payload, ensure_ascii=False, sort_keys=True, default=str) @@ -110,8 +107,7 @@ def path_for(self, text: str, params: dict[str, Any]) -> Path: def _legacy_key_payloads( self, - text: str, - params: dict[str, Any], + current: dict[str, Any], ) -> tuple[dict[str, Any], ...]: """Recreate the bounded pre-asset-layout cache identity. @@ -123,7 +119,10 @@ def _legacy_key_payloads( metadata on the latency-critical lookup path. """ - current = self.key_payload(text, params) + # Older Japanese entries predate native pronunciation identities and + # cannot prove compatibility. Only the English predecessor is live. + if "japanese_dictionary" in current: + return () language = str(current.get("tts_output_language") or "").strip().lower() legacy_language = {"ja": "日文", "en": "英文"}.get(language) gpt_name = _portable_basename(current.get("gpt_model")) @@ -196,6 +195,8 @@ def lookup(self, text: str, params: dict[str, Any]) -> tuple[int, np.ndarray] | if not FIRST_SENTENCE_AUDIO_CACHE_ENABLED: return None current_payload = self.key_payload(text, params) + if "japanese_dictionary" in current_payload and not current_payload["japanese_dictionary"]: + return None current_path = self._path_for_payload(current_payload) if current_path.exists(): current = self._read_entry( @@ -205,7 +206,7 @@ def lookup(self, text: str, params: dict[str, Any]) -> tuple[int, np.ndarray] | if current is not None: return current[0], current[1] - for legacy_payload in self._legacy_key_payloads(text, params): + for legacy_payload in self._legacy_key_payloads(current_payload): legacy_path = self._path_for_payload(legacy_payload) if legacy_path == current_path or not legacy_path.exists(): continue @@ -268,9 +269,11 @@ def store( ) return False - path = self.path_for(text, params) key_payload = self.key_payload(text, params) - key = self._fingerprint(text, params) + if "japanese_dictionary" in key_payload and not key_payload["japanese_dictionary"]: + return False + path = self._path_for_payload(key_payload) + key = self._fingerprint_payload(key_payload) metadata = { "schema": _CACHE_SCHEMA, "key": key, diff --git a/tts/gpt_sovits_sidecar.py b/tts/gpt_sovits_sidecar.py index 030d3174..0a36da1d 100644 --- a/tts/gpt_sovits_sidecar.py +++ b/tts/gpt_sovits_sidecar.py @@ -137,6 +137,7 @@ def main() -> None: ) raise SystemExit(1) from exc + from tts.japanese_dictionary import dictionary_runtime_status _emit( { "type": "ready", @@ -144,6 +145,7 @@ def main() -> None: "torch": torch_version, "hip": hip_version, "cuda_available": cuda_available, + "japanese_dictionary": dictionary_runtime_status(), } ) @@ -180,7 +182,8 @@ def main() -> None: _emit_chunk(request_id, sample_rate, audio, str(chunk_text or "")) else: raise ValueError(f"unsupported operation: {operation!r}") - _emit({"type": "done", "request_id": request_id}) + _emit({"type": "done", "request_id": request_id, + "japanese_dictionary": dictionary_runtime_status()}) except Exception as exc: _emit({"type": "error", "request_id": request_id, "msg": str(exc)}) diff --git a/tts/japanese_dictionary.py b/tts/japanese_dictionary.py index 5b312b69..471afcd9 100644 --- a/tts/japanese_dictionary.py +++ b/tts/japanese_dictionary.py @@ -1,112 +1,141 @@ -"""Optional Japanese pronunciation data and its local OpenJTalk compilation. - -The official CSV is an external asset. Compiled dictionaries are disposable -cache files; neither installation nor compilation modifies personal sources. -""" +"""Optional Japanese data loading; source CSVs are never modified.""" from __future__ import annotations import hashlib import logging import os -from functools import lru_cache from pathlib import Path import tempfile from config.asset_paths import PROJECT_ROOT -from config.japanese_dictionary import ( - PACK_TREE, JapaneseDictionaryError, dictionary_rows, load_dictionary_pack, -) +from config.japanese_dictionary import PACK_TREE, dictionary_rows, load_dictionary_pack logger = logging.getLogger(__name__) -_runtime_status = {"state": "not_loaded", "ready": False, "version": "", +_runtime_status = {"state": "not_loaded", "ready": False, "version": "", "identity": None, "detail": "Japanese frontend has not loaded."} -@lru_cache(maxsize=8) -def _sources(project_root: Path) -> tuple[tuple[Path, ...], str]: - # Resource installation and the enable switch require a backend restart. - official = project_root / "assets" / PACK_TREE - legacy = project_root / "GPT_SoVITS/text/ja_userdic/userdict.csv" - personal = project_root / "assets/text/ja/user.csv" - paths: list[Path] = [] - version = "" - if official.exists(): - pack = load_dictionary_pack(official) - paths.append(official / "userdict.csv") - version = pack.version - elif legacy.is_file(): - # Existing installations still use this source. New packs replace its - # role without copying, editing, or deleting the installation's data. - paths.append(legacy) - version = "legacy-local" - if personal.is_file(): - paths.append(personal) - return tuple(paths), version - - def _enabled() -> bool: from config.settings import ENABLE_JAPANESE_USER_DICTIONARY return ENABLE_JAPANESE_USER_DICTIONARY -@lru_cache(maxsize=16) -def _digest(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() - - -def dictionary_cache_identity() -> str: - """Speech cache identity; does not import pyopenjtalk in the host process.""" - if not _enabled(): - return "disabled" - try: - paths, _ = _sources(PROJECT_ROOT) - return ":".join(_digest(path) for path in paths) or "none" - except (JapaneseDictionaryError, OSError): - return "invalid" - - def dictionary_runtime_status() -> dict: return dict(_runtime_status) def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, enabled: bool | None = None) -> dict: - """Install all selected dictionaries once when the Japanese frontend loads.""" + """Load independent sources without making optional failures fatal.""" global _runtime_status - enabled = _enabled() if enabled is None else enabled - state = "not_installed" - version = "" - detail = "Optional Japanese dictionary is not installed." + state, version, identity = "invalid", "", None + detail = "Optional Japanese dictionary unavailable." + reset = None try: - if not enabled: - jtalk.unset_user_dict() - state, detail = "disabled", "Japanese user dictionaries are disabled." - else: - paths, version = _sources(Path(project_root).resolve()) - compiled = [] - jtalk.unset_user_dict() - for source in paths: - for _ in dictionary_rows(source.read_bytes()): - pass - base = Path(os.fsdecode(jtalk.OPEN_JTALK_DICT_DIR)) - system = base / "sys.dic" - system_key = (str(base), system.stat().st_size, system.stat().st_mtime_ns) - key = hashlib.sha256(repr((jtalk.__version__, system_key, _digest(source))).encode()).hexdigest() - cache = Path(project_root) / ".cache/japanese_dictionary" + enabled = _enabled() if enabled is None else enabled + reset = getattr(jtalk, "unset_user_dict", None) + if not callable(reset): + state = "unsupported" + raise RuntimeError("Japanese user dictionaries require the pinned pyopenjtalk-plus API (unset_user_dict)") + reset() + project_root = Path(project_root).resolve() + base = Path(os.fsdecode(jtalk.OPEN_JTALK_DICT_DIR)) + system = (base / "sys.dic").stat() + native_key = (jtalk.__version__, str(base), system.st_size, system.st_mtime_ns) + compiled, digests, issues = [], [], [] + cache = project_root / ".cache/japanese_dictionary" + official = project_root / "assets" / PACK_TREE / "userdict.csv" + legacy = project_root / "GPT_SoVITS/text/ja_userdic/userdict.csv" + personal = project_root / "assets/text/ja/user.csv" + sources = [] + if enabled: + if official.parent.exists(): + sources.append(official) + elif legacy.is_file(): + sources.append(legacy) + if personal.is_file(): + sources.append(personal) + for source in sources: + label = source.relative_to(project_root).as_posix() + try: + pack = load_dictionary_pack(source.parent) if source == official else None + data = source.read_bytes() + digest = hashlib.sha256(data).hexdigest() + if pack is not None: + if digest != pack.sha256: + raise ValueError("Dictionary changed during loading; restart after installation finishes") + else: + for _ in dictionary_rows(data): + pass + key = hashlib.sha256(repr((native_key, digest)).encode()).hexdigest() target = cache / f"{key}.dict" if not target.is_file(): cache.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory(prefix="compile-", dir=cache) as temporary: + snapshot = Path(temporary) / "source.csv" + snapshot.write_bytes(data) output = Path(temporary) / "user.dict" - jtalk.mecab_dict_index(str(source), str(output)) - os.replace(output, target) - compiled.append(str(target)) - if compiled: - jtalk.update_global_jtalk_with_user_dict(compiled) - state, detail = "ready", f"Japanese dictionary {version or 'personal'} loaded ({len(compiled)} source(s))." - logger.info("[JapaneseDictionary] %s", detail) - except (JapaneseDictionaryError, OSError, RuntimeError, ValueError) as exc: - jtalk.unset_user_dict() - state, detail = "invalid", str(exc) + jtalk.mecab_dict_index(str(snapshot), str(output)) + try: + os.replace(output, target) + except OSError: + # A peer may already have published and mmap'ed the + # same content key on Windows. Reuse its file. + if not target.is_file(): + raise + except Exception as exc: + issues.append(f"{label}: {exc}") + logger.warning("[JapaneseDictionary] Skipped %s: %s", label, exc) + continue + try: + jtalk.update_global_jtalk_with_user_dict([*compiled, str(target)]) + except Exception as exc: + # Recovery failure escapes to the outer optional boundary: + # never claim an identity for an unknown native selection. + if compiled: + jtalk.update_global_jtalk_with_user_dict(compiled) + else: + reset() + issues.append(f"{label}: {exc}") + logger.warning("[JapaneseDictionary] Skipped %s: %s", label, exc) + continue + compiled.append(str(target)) + digests.append(digest) + if pack is not None: + version = pack.version + elif source == legacy: + version = "legacy-local" + identity = hashlib.sha256(repr((native_key, tuple(digests))).encode()).hexdigest() + if compiled: + state = "ready" + detail = f"Japanese dictionary {version or 'personal'} loaded ({len(compiled)} source(s))." + # Windows can retain files mmap'ed by another live process. Their + # cleanup is retried on a later successful startup. + for old in cache.glob("*.dict"): + if str(old) not in compiled and len(old.stem) == 64 and all(c in "0123456789abcdef" for c in old.stem): + try: + old.unlink() + except OSError as exc: + logger.debug("[JapaneseDictionary] Cache still in use: %s (%s)", old.name, exc) + elif not enabled: + state, detail = "disabled", "Japanese user dictionaries are disabled; using the base dictionary." + elif issues: + state, detail = "invalid", "Using the base Japanese dictionary." + else: + state, detail = "not_installed", "Optional Japanese dictionary is not installed; using the base dictionary." + if issues: + detail += " Skipped " + "; ".join(issues) + except Exception as exc: + # Configuration, native API and reset errors belong to this optional + # boundary. An unsupported runtime must still import the base frontend. + identity = None + state = "unsupported" if state == "unsupported" else "invalid" + detail = str(exc) + if callable(reset): + try: + reset() + except Exception as cleanup: + logger.warning("[JapaneseDictionary] Native reset failed: %s", cleanup) logger.warning("[JapaneseDictionary] Optional dictionary unavailable: %s", exc) - _runtime_status = {"state": state, "ready": state == "ready", "version": version, "detail": detail} + _runtime_status = {"state": state, "ready": state == "ready", "version": version, + "identity": identity, "detail": detail} return dictionary_runtime_status() diff --git a/tts/pipeline.py b/tts/pipeline.py index 11ec3468..f81a1f09 100644 --- a/tts/pipeline.py +++ b/tts/pipeline.py @@ -86,6 +86,20 @@ def selectable_tts_concurrency(value) -> int: _active_stream_queues: set[asyncio.Queue] = set() +def japanese_dictionary_status() -> dict: + backend = getattr(_tts_runtime, "backend", None) + if _tts_runtime is None: + # Offline cache prebuilding uses a direct local inferencer, whose + # frontend establishes the same process-local native identity. + from tts.japanese_dictionary import dictionary_runtime_status + return dictionary_runtime_status() + status = getattr(backend, "japanese_dictionary_status", None) + return dict(status) if isinstance(status, dict) and status else { + "state": "not_loaded", "ready": False, "identity": None, "version": "", + "detail": "Japanese frontend has not loaded.", + } + + def emotion_references_enabled() -> bool: backend = getattr(_tts_runtime, "backend", None) return bool(getattr(backend, "emotion_reference_status", {}).get("ready")) and current_tts_language_code() == "ja" From c9ab9a933078df464e656694b5142901e7a7e3d4 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 03:14:35 +1300 Subject: [PATCH 04/10] fix(tts): quarantine suspect dictionary rows and preserve base readings --- LICENSES/JAPANESE-DICTIONARY.md | 38 ++- docs/japanese_pronunciation_dictionary.md | 151 ++++----- tests/test_japanese_dictionary.py | 16 +- tests/test_japanese_dictionary_data_policy.py | 121 +++++++ tools/audit_japanese_dictionary.py | 118 ++++++- .../japanese_dictionary_common_words.json | 318 ++++++++++++++++++ .../data/japanese_dictionary_corrections.csv | 1 + tools/data/japanese_dictionary_source.json | 51 ++- tools/prepare_japanese_dictionary.py | 20 +- tools/validate_japanese_dictionary.py | 89 ++++- 10 files changed, 804 insertions(+), 119 deletions(-) create mode 100644 tests/test_japanese_dictionary_data_policy.py create mode 100644 tools/data/japanese_dictionary_common_words.json diff --git a/LICENSES/JAPANESE-DICTIONARY.md b/LICENSES/JAPANESE-DICTIONARY.md index a1646f4e..bbd18863 100644 --- a/LICENSES/JAPANESE-DICTIONARY.md +++ b/LICENSES/JAPANESE-DICTIONARY.md @@ -48,19 +48,31 @@ then removes only identical full rows. Different lexical readings remain. The generated `provenance.json` additionally identifies the derivative's version, hash, entry count, normalization rules and repair counters. -Version 2026.10.18.1 retains the previous 11 singular-reading repairs and -complete `proposer` entry, and reviews the remaining 317 flagged entries: -94 lexical/script/annotation repairs, 65 phonetic representation repairs and -158 entries retaining their reading/cost/accent fields pending stronger evidence. The recipe identifies each -reviewed before/after pronunciation, relevant CMU/native evidence and measured -word-cost adjustments. Other readings of a corrected surface remain available. -All surfaces receive native MeCab spelling, including 48 of those retained -entries; their other fields are unchanged. One- and two-letter Latin surfaces -yield to the Japanese base dictionary, preserving letter/unit readings. The -prepared pack has 127,811 entries. `proposer` still -uses a provisional flat accent; -native listening review is still required. This does not claim exhaustive -dictionary accuracy or contextual disambiguation of all Japanese homographs. +Version 2026.10.20.1 retains the previous correction history and adds a +source-backed `let's → レッツ` repair: the existing `lets` entry has the same +CMU phones. Its accent annotation is reused, not newly validated by listening. +The historical 317-row review comprises 94 lexical/script/annotation repairs, +65 representation repairs and 158 unconfirmed readings. The release filter +subsequently yields known whole words to the pinned base dictionary and +quarantines every variant of CMU final-phone risk surfaces. The CMU file and +base dictionary hashes, rules and complete exclusion records are in provenance. +The direct flags exactly reproduce the external 1,557-row TSV; extending +quarantine to its other same-surface variants excludes another 112 rows. +This screen admits false positives and can miss other errors; it is not a claim +that each excluded row is wrong. A uniform 10,300 floor for plain Latin word +costs supersedes earlier per-entry cost calibration, preventing observed cheap +fragment splits of known base words without runtime word exceptions. + +From 127,811 normalized entries, 2,422 are excluded (769 base-owned rows, +1,669 CMU-risk rows, 16 overlap), leaving 125,389. The floor raises 116,922 +retained costs. Three-context validation covers all 746 base-owned surfaces; +289 common English words extend the former 52-control sample. The gate does +not establish exhaustive lexical accuracy. `proposer` still uses a provisional +flat accent; waveform/listening acceptance remains outstanding. + +The upstream default branch was rechecked on 2026-10-20 at +`b4570b876f5c086819ba84e3215472733c6a7b70`: the CSV still has the same Git blob +and contents as the pinned source above. The CSV and notices are distributed separately as the optional `japanese-pronunciation` asset pack. Large generated pack files remain outside diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index 4741fc80..9c9038e3 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -15,8 +15,8 @@ loader before installation. Older applications reject the unknown pack ID. The first compatible application release has not yet been published. ```powershell -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.18.1.zip -python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.18.1.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.1.zip +python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.20.1.zip python tools/external_assets.py status ``` @@ -91,8 +91,8 @@ is explicit. ```powershell python tools/prepare_japanese_dictionary.py --source GPT_SoVITS/text/ja_userdic/userdict.csv python tools/validate_japanese_dictionary.py --output output/japanese_dictionary_validation.json -python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.18.1.zip -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.18.1.zip +python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.20.1.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.1.zip python tools/audit_japanese_dictionary.py --source assets/text/ja/english/userdict.csv --output output/japanese_dictionary_audit.json ``` @@ -103,73 +103,51 @@ model paths or rebuild any existing model/voice archive. ## Quality boundary / 质量范围 -Version 2026.10.18.1 retains the review of the 317 entries left by version 2026.10.16.2: -94 receive reviewed lexical/script/annotation repairs, 65 receive phonetic -separator or format-control repairs, and 158 retain their readings, costs and -accent fields pending stronger evidence. Of these, 48 receive surface spelling -normalization only. The local review report records every -disposition. An all-kana alternative alone is not proof of lexical correctness. -Examples include `balloonist` (`二` → `ニ`), `ninth` (`ん` → `ン`), untranslated -letter names, stray inflection letters and literal LLM explanations. - -The correction recipe targets the reviewed **before pronunciation**; it does -not replace every pronunciation sharing that word. Legitimate alternatives -such as singular/plural `corps` and initialism/plural `CDS/CDs` remain available; -no context classifier is added to force a single choice. - -Native input converts apostrophes and hyphens to `’` and `−`. All entries receive -this native surface spelling, including previously dormant contractions and -compounds; this changes representation rather than guessing readings. The frontend preserves -Latin compound punctuation until native tokenization. Standalone punctuation -and Japanese punctuation keep their normal handling. Native MeCab measurements -in three synthetic contexts exposed cheap unintended word splits; 109 reviewed -entry costs were reduced just enough to beat those measured splits. The -measured costs are stored in the CSV/recipe, with no runtime word-specific -pronunciation or cost rules. Unconfirmed entries retain their original rows. - -One- and two-letter Latin surfaces are excluded at build time: these fragments -must yield to the base Japanese dictionary's letter and measurement entries. -This excludes 328 pre-deduplication rows (300 distinct previous pack rows), -preventing imported `m`/`w`/`h` fragments from displacing cm/kg/km/mm/ml/kW. -No per-unit or per-word runtime list is added. Unknown short number suffixes -also bypass generic initialism expansion; known names and longer initialisms -such as `3GPUs` retain their existing handling. Native spelling is normalized for -8,998 pre-deduplication surfaces. - -The pack contains 127,811 entries. Re-auditing it produces no further structural -repairs and leaves the 158 review entries intact. No accent is clamped or -invented for `guangjo`, whose source accent exceeds the native mora count. - -The earlier version's 11 singular-reading repairs are retained: -`follower`, `learner`, `abstraction`, `acronym`, `adapter`, `airline`, -`applicator`, `bank`, `component`, `developer`, and `handler`. Their plural -entries remain intact. It also adds the complete `proposer` → `プロポーザー` -entry; its flat accent is provisional pending listening review. -The `component` row also has its entry cost corrected from 608 to 0: native -full-pack validation otherwise selects `comp + on + ent`, despite the correct -whole-word reading being present. The corresponding `components` cost changes -from 659 to 0 to keep its existing plural reading selected. Both sides are -checked by the native test. These repairs stay in dictionary data. - -Build-time normalization uses general rules: NFC in reading fields, -removing phonetic-neutral separators only from otherwise pure kana when the -existing accent remains valid, spelling untranslated whole-token initialisms, -counting morae from one explicit -native pronunciation while preserving valid accent nuclei, and removing only -identical full rows. It does not strip English plural suffixes automatically -or merge different readings of one word. Passing kana through ordinary g2p -would retokenize it and can incorrectly split small kana; the audit feeds the -pronunciation directly to the native label builder instead. - -Preparation records repair counters before deduplication; the release's 65 -representation repairs are explicitly tracked alongside their measured costs. -The audit also lists CMUdict singular/inflection collisions as review candidates; -such collisions alone do not prove errors (`path` and `paths` can both be -`パス`). It does not guess replacements for these entries. - -运行时没有新增逐词判断。原词条修复记录在 correction CSV;通用数据清理在构建时 -执行,来源、修复理由、规则及计数均随包保存。不同读音保留,无法确定的重音或 -机器翻译残留只生成待核对清单。后续可沿用同一审计流程逐批修复原数据。 +Version 2026.10.20.1 retains the historical review of 317 entries: 94 +lexical/script/annotation repairs, 65 separator/format repairs, and 158 +unconfirmed readings. Those decisions are evidence, not mandatory release +inclusions. The build subsequently applies the conservative extension policy +below; exclusions and cost changes also apply to historically reviewed rows. +The correction CSV now has 173 rows. The additional `let's` repair reuses +`lets → レッツ` and its accent annotation: both have CMU `L EH1 T S`. + +Preparation applies these rules offline, after representation repairs: + +- Yield every surface recognized as one known whole word by an independent + base MeCab instance to that base. Unknown letter-spelled tokens do not qualify. + Pin the native runtime and `sys.dic`, `matrix.bin`, `char.bin` hashes. +- Quarantine every variant of a surface if any reading's final Katakana + consonant conflicts with every CMU pronunciation variant: ズ requires S/Z, + ド requires D/T, and ング requires NG. These match the external audit; its + unreachable ッド branch is omitted. Removing only one bad variant could promote + another shifted reading, as with `directly → director`. +- Raise plain Latin word costs to a common floor of 10,300, preserving higher + costs and punctuation-bearing compound costs. Removing same-name rows alone + lets cheap fragments split known words (`production → pro + duct + ion`). + This uniform floor supersedes the earlier per-entry cost calibration. At + 10,200, native `hilt + on` still beat `hilton` by one cost unit in one control + context; 10,300 passes all 746 base-word controls in three contexts while + preserving the retained reviewed-word/compound gate. + +From 127,811 normalized rows, the policy excludes 2,422 rows: 769 base-owned +rows and 1,669 rows associated with CMU-risk surfaces (16 overlap). It raises +116,922 retained plain-word costs. The resulting pack contains 125,389 rows. +The provenance records each exclusion's normalized input line, surface, +reading and reason. CMU data and base dictionary hashes are pinned. + +The direct flags exactly reproduce all 1,557 external TSV records (ズ 807, +ド 523, ング 227); none of those original word/reading pairs remain. The +additional 112 same-surface variants are quarantined to avoid promoting +another shifted row. These are conservative exclusions, not 1,669 proven errors. For +example, `with → ウィズ` and `condo → コンド` can be legitimate loanword +adaptations despite a flagged final consonant. The rule can miss interior +shifts and vowel-ending errors. It neither guesses replacements nor promises +exhaustive dictionary accuracy. The older singular/inflection collision list +remains review-only (`path` and `paths` can both be `パス`). + +运行时没有新增逐词判断。构建时优先使用基础词典,隔离词尾音可疑的整个词形, +并统一限制普通拉丁词条的低代价拆词行为。隔离清单包含误报,不等于全部确认有错; +词内错位及部分元音结尾错误仍需审计。可确认的 `let's` 错行使用已有 `lets` 的读音修复。 Whole-token abbreviation preparation handles `MLP` next to Japanese, fullwidth Latin text and case variants of existing names, and avoids replacing `API` @@ -182,17 +160,26 @@ word/name extension, not a general context-sensitive Japanese homograph model or a voice/timbre replacement. Native pronunciation/prosody comparisons do not replace end-to-end synthesis and human listening acceptance. -The native release gate compares 52 samples, including Latin units and -inflected names/initialisms, against the actual base `japanese.g2p` prosody path. -It also checks 172 reviewed rows and nine common contractions in three contexts -(543 native contexts) and 181 production frontend/native-prosody comparisons. -An additional 32-unit set has no phoneme changes. It records an `rpm` prosody -boundary difference for listening review: the reading is the same, and that -observation alone does not justify changing an existing accent. -It runs offline with a prepared full pack; core unit tests use synthetic data -and do not require downloading dictionary or model assets. Pack validation -checks the hash before parsing CSV and memoizes successes/failures by member -file stats. Audio cache key generation performs no dictionary-file validation. +The native release gate compares the original 52 controls and 32 additional +unit samples against the production `japanese.g2p` path. It also compares all +746 base-owned imported words in three contexts (2,238 controls), exercises +289 frozen common English words, including all 48 distinct words in the +external 49-row frequent-document subset, and validates the 11 reported regressions +with six reviewed readings and five unchanged base fallbacks. The broader +vocabulary is selected from frequent tracked Markdown words and flagged +surfaces; inclusion alone is not a pronunciation correctness oracle. Changes +for other unknown words are recorded for review. + +Retained corrected rows and contractions pass 537 native contexts and 179 +production frontend/prosody comparisons. There are no failures or unit-prosody +changes in this candidate. These results apply to these controls and the +pinned native environment; they do not establish zero regressions across all +English vocabulary or replace waveform/listening acceptance. + +The gate runs offline with a prepared full pack. Core tests use synthetic data +and require no dictionary/model downloads. Pack validation checks hash before +parsing CSV and memoizes success/failure by member stats. Audio cache keys +perform no dictionary-file validation. ## Publication / 发布 diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py index 8e5fa35a..9ad62e41 100644 --- a/tests/test_japanese_dictionary.py +++ b/tests/test_japanese_dictionary.py @@ -49,7 +49,8 @@ def _prepare(root: Path, *, native: bool = False) -> Path: recipe_path = root / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") return prepare_dictionary(source, project_root=root, recipe_path=recipe_path, - count_mora=None if native else lambda _: None) + count_mora=None if native else lambda _: None, + base_word=None if native else lambda _: False) @pytest.mark.parametrize("text,expected", [ @@ -83,14 +84,18 @@ def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): assert (tmp_path / "upstream.csv").read_text(encoding="utf-8") == FOLLOWER + LEARNER pack = dictionary_pack.load_dictionary_pack(pack_root) corrections = list(dictionary_pack.dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) - assert pack.entry_count == len(corrections) + # Conservative final-phone screening also quarantines legitimate loanword + # adaptations; condo -> コンド is a documented false positive (CMU OW). + accepted = [row for row in corrections if row[0] != "condo"] + assert pack.entry_count == len(accepted) readings = {row[0]: row[11] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes())} - assert readings == {row[0]: row[11] for row in corrections} + assert readings == {row[0]: row[11] for row in accepted} assert readings["follower"] == "フォロワー" assert readings["learner"] == "ラーナー" provenance = json.loads((pack_root / "provenance.json").read_text(encoding="utf-8")) assert provenance["normalization"]["rules"] - assert provenance["normalization"]["results"]["output_entries"] == pack.entry_count + assert provenance["normalization"]["results"]["output_entries"] == provenance["extension_filter"]["results"]["input_entries"] + assert provenance["extension_filter"]["results"]["output_entries"] == pack.entry_count assert all(path.is_file() for path in pack.files()) # A maintainer cannot accidentally publish a customized installation as the # pinned upstream base, even if it has the expected CSV shape. @@ -212,7 +217,8 @@ def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surfac recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=2) recipe_path = tmp_path / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") - pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, count_mora=lambda _: None) + pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, + count_mora=lambda _: None, base_word=lambda _: False) readings = [row[12] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0] == "corps"] assert set(readings) == {"コア", "コアズ"} assert source.read_bytes() == data diff --git a/tests/test_japanese_dictionary_data_policy.py b/tests/test_japanese_dictionary_data_policy.py new file mode 100644 index 00000000..293f899e --- /dev/null +++ b/tests/test_japanese_dictionary_data_policy.py @@ -0,0 +1,121 @@ +"""The optional CSV extends unknown vocabulary without owning base words.""" +from pathlib import Path +import hashlib +import json + +import pytest + +from tools.audit_japanese_dictionary import ( + LATIN_WORD_COST_FLOOR, cmu_pronunciations, filter_extension_rows, final_phone_risk, + native_base_word_lookup, word_key, +) +from test_japanese_dictionary import _row + + +@pytest.fixture(autouse=True) +def isolate_native_status(monkeypatch): + from tts import japanese_dictionary as runtime + monkeypatch.setattr(runtime, "_runtime_status", { + "state": "not_loaded", "ready": False, "identity": None, "version": "", "detail": "Not loaded", + }) + + +@pytest.mark.parametrize("reading,phones,risky", [ + ("ヘルプト", {("HH", "EH1", "L", "P")}, False), + ("モデルド", {("M", "AA1", "D", "AH0", "L")}, True), + ("ウェルカムド", {("W", "EH1", "L", "K", "AH0", "M")}, True), + ("ダイレクトネス", {("D", "ER0", "EH1", "K", "T", "L", "IY0")}, False), + ("リセッティング", {("R", "IY0", "S", "EH1", "T")}, True), + ("レットアップス", {("L", "EH1", "T", "S")}, False), + ("アップス", {("AE1", "P", "S")}, False), + ("デッドラインズ", {("D", "EH1", "D", "L", "AY2", "N")}, True), + ("パス", {("P", "AE1", "TH")}, False), + ("ウィズ", {("W", "IH1", "TH"), ("W", "IH1", "DH")}, True), + ("コンド", {("K", "AA1", "N", "D", "OW0")}, True), + ("テストズ", {("T", "EH1", "S", "T"), ("T", "EH1", "S", "T", "Z")}, False), + ("テスト", set(), False), +]) +def test_final_phone_screening_includes_variants_and_documents_false_positives(reading, phones, risky): + assert final_phone_risk(reading, phones) is risky + + +def test_filter_yields_base_words_quarantines_variants_and_bounds_fragment_costs(): + rows = [_row("Help", "ヘルプト"), _row("directly", "ディレクターズ"), + _row("directly", "ディレクター"), _row("follower", "フォロワー"), + _row("don't", "ドント"), _row("pricy", "プライシー")] + rows[-1][3] = "12000" + before = [list(row) for row in rows] + phones = cmu_pronunciations(b"DIRECTLY D ER0 EH1 K T L IY0\nDIRECTLY(1) D IH0 R EH1 K T L IY0\n") + kept, report = filter_extension_rows(rows, phones, lambda word: word == "help") + assert {row[0] for row in kept} == {"follower", "don't", "pricy"} + assert [row[3] for row in kept] == [str(LATIN_WORD_COST_FLOOR), rows[-2][3], "12000"] + assert report["reason_counts"] == {"base_dictionary_word": 1, "cmu_final_phone_risk": 2} + assert rows == before, "filtering must not rewrite its evidence input" + assert filter_extension_rows(kept, phones, lambda _: False)[0] == kept + assert word_key("Let’s") == "let's" + + +def test_native_base_lookup_does_not_treat_letter_spelling_as_a_word(): + pytest.importorskip("pyopenjtalk") + known = native_base_word_lookup() + assert all(known(word) for word in ("Help", "learn", "Two", "with", "Production")) + assert not any(known(word) for word in ("model", "welcome", "directly", "deadline")) + + +def test_native_cost_floor_preserves_known_words_and_retains_unknown_word_support(tmp_path: Path): + native = pytest.importorskip("pyopenjtalk") + base = native.OpenJTalk(native.OPEN_JTALK_DICT_DIR) + rows = [_row("hilt", "ヒルト", "1/3"), _row("pro", "プロ", "1/2"), + _row("duct", "ダクト", "1/3"), _row("ion", "イオン", "1/3"), + _row("follower", "フォロワー", "0/4")] + kept, _ = filter_extension_rows(rows, {}, native_base_word_lookup()) + source, binary = tmp_path / "source.csv", tmp_path / "user.dict" + source.write_text("".join(",".join(row) + "\n" for row in kept), encoding="utf-8") + native.mecab_dict_index(str(source), str(binary)) + extension = native.OpenJTalk(native.OPEN_JTALK_DICT_DIR, userdic=str(binary).encode()) + for text in ("production", "hiltonを確認する。", "これはhiltonです。"): + assert extension.run_frontend(text) == base.run_frontend(text) + for text in ("follower", "followerを確認する。", "これはfollowerです。"): + assert any(f["read"] == "フォロワー" for f in extension.run_frontend(text)) + + +def test_reported_regressions_through_preparation_and_production_frontend(tmp_path, monkeypatch): + native = pytest.importorskip("pyopenjtalk") + from tools.prepare_japanese_dictionary import prepare_dictionary, RECIPE_PATH + from tools.tts_text_processor import correct_pronunciation_for_tts + from tts.japanese_dictionary import configure_japanese_dictionary + + root = RECIPE_PATH.parents[2] + monkeypatch.syspath_prepend(str(root / "GPT_SoVITS")) + from text.cleaner import clean_text + + def phones(text): + return clean_text(correct_pronunciation_for_tts(text), "ja", "v2")[0] + + bad = {"Help": "ヘルプト", "learn": "レアン", "Two": "ツーズ", "with": "ウィズドロールズ", + "Production": "プロダクションズ", "model": "モデルド", "let's": "レットアップス", + "welcome": "ウェルカムド", "directly": "ダイレクトネス", "reset": "リセッティング", + "deadline": "デッドラインズ"} + rows = [_row(word, reading, "0/10") for word, reading in bad.items()] + # The real data has a flagged third variant; its non-flagged shifted + # alternatives must not become the next selected reading after filtering. + rows += [_row("directly", "ディレクター"), _row("directly", "ディレクターズ"), + _row("reset", "リセット")] + data = "".join(",".join(row) + "\n" for row in rows).encode() + source, recipe_path = tmp_path / "upstream.csv", tmp_path / "recipe.json" + source.write_bytes(data) + recipe = json.loads(RECIPE_PATH.read_text(encoding="utf-8")) + recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(rows)) + recipe_path.write_text(json.dumps(recipe), encoding="utf-8") + prepare_dictionary(source, recipe_path=recipe_path, project_root=tmp_path) + try: + native.unset_user_dict() + baseline = {word: phones(word) for word in bad} + assert configure_japanese_dictionary(native, project_root=tmp_path, enabled=True)["ready"] + for word in bad.keys() - {"let's"}: + assert phones(word) == baseline[word] + assert [f["read"] for f in native.run_frontend("let’s")] == ["レッツ"] + assert phones("let’s") == phones("レッツ") + assert source.read_bytes() == data + finally: + native.unset_user_dict() diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py index a44d3f4f..3cb68450 100644 --- a/tools/audit_japanese_dictionary.py +++ b/tools/audit_japanese_dictionary.py @@ -22,6 +22,103 @@ _KANA = re.compile(r"[\u30a1-\u30fa\u30fc]+") _MORA_PHONES = frozenset({"a", "i", "u", "e", "o", "A", "I", "U", "E", "O", "N", "cl"}) +# A screening rule, not an English-to-Japanese translator. Loanword adaptation +# can violate these correspondences. Exclude a flagged imported row rather than +# invent its replacement; keep every CMU pronunciation alternative eligible. +_FINAL_CONSONANTS = { + "ズ": {"Z", "S"}, "ド": {"D", "T"}, "ング": {"NG"}, +} +LATIN_WORD_COST_FLOOR = 10300 + + +def word_key(value: str) -> str: + return unicodedata.normalize("NFKC", value).translate(str.maketrans("’−", "'-")).casefold() + + +def cmu_pronunciations(data: bytes) -> dict[str, set[tuple[str, ...]]]: + phones = defaultdict(set) + for line in data.decode("utf-8").splitlines(): + fields = line.split() + if fields and not fields[0].startswith(";;;"): + phones[fields[0].split("(", 1)[0].casefold()].add(tuple(fields[1:])) + return dict(phones) + + +def final_phone_risk(reading: str, pronunciations: set[tuple[str, ...]]) -> bool: + phones = {tuple(re.sub(r"[012]$", "", p) for p in variant) for variant in pronunciations if variant} + if not phones: + return False + for ending, expected in _FINAL_CONSONANTS.items(): + if reading.endswith(ending): + return not any(p[-1] in expected for p in phones) + return False + + +def native_base_word_lookup() -> Callable[[str], bool]: + import pyopenjtalk + + # Independent base instance: installed official/personal dictionaries must + # never decide which upstream entries the release builder accepts. + jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) + + @lru_cache(maxsize=None) + def known(word: str) -> bool: + word = word_key(word) + _, morphs = jtalk.run_mecab_detailed(word) + tokens = [m for m in morphs if not m["is_ignored"]] + return (len(tokens) == 1 and not tokens[0]["is_unknown"] + and tokens[0]["surface"] == jtalk.normalize_for_mecab(word)) + + return known + + +def native_base_hashes() -> dict[str, str]: + import os + import pyopenjtalk + + root = Path(os.fsdecode(pyopenjtalk.OPEN_JTALK_DICT_DIR)) + return {name: hashlib.sha256((root / name).read_bytes()).hexdigest() + for name in ("sys.dic", "matrix.bin", "char.bin")} + + +def filter_extension_rows(rows: list[list[str]], phones: dict[str, set[tuple[str, ...]]], + base_word: Callable[[str], bool], *, + cost_floor: int = LATIN_WORD_COST_FLOOR) -> tuple[list[list[str]], dict]: + """Yield known words to the base and quarantine suspect readings offline.""" + kept, excluded = [], [] + counts = Counter() + direct_flags = [row for row in rows if final_phone_risk(row[11], phones.get(word_key(row[0]), set()))] + suspect = {word_key(row[0]) for row in direct_flags} + repriced = 0 + for number, row in enumerate(rows, 1): + word = word_key(row[0]) + reasons = [] + if base_word(word): + reasons.append("base_dictionary_word") + # A contaminated surface can have several shifted alternatives. Do not + # merely promote the next unverified variant (e.g. directly -> director). + if word in suspect: + reasons.append("cmu_final_phone_risk") + if reasons: + counts.update(reasons) + excluded.append({"normalized_line": number, "surface": word, "reading": row[12], + "reasons": reasons}) + else: + # Imported short words must not cheaply split a known base word. + # Use one floor for plain Latin words; punctuation-bearing compounds + # need their existing cost to compete with native boundary splits. + if re.fullmatch(r"[a-z]+", word) and int(row[3]) < cost_floor: + row = list(row) + row[3] = str(cost_floor) + repriced += 1 + kept.append(row) + return kept, {"input_entries": len(rows), "output_entries": len(kept), "excluded_entries": len(excluded), + "reason_counts": dict(counts), "latin_word_cost_floor": cost_floor, + "direct_final_phone_flags": len(direct_flags), + "direct_final_phone_suffix_counts": dict(Counter( + next(ending for ending in _FINAL_CONSONANTS if row[11].endswith(ending)) + for row in direct_flags)), + "latin_word_costs_raised": repriced, "excluded": excluded} def _without_reading_separators(value: str) -> str: @@ -122,17 +219,14 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], int | None], - normalize_surface: Callable[[str], str] | None = None) -> dict: + normalize_surface: Callable[[str], str] | None = None, + base_word: Callable[[str], bool] | None = None) -> dict: words = defaultdict(list) for number, row in enumerate(rows, 1): - key = unicodedata.normalize("NFKC", row[0]).translate(str.maketrans("’−", "'-")).casefold() + key = word_key(row[0]) words[key].append((number, row)) - phones = defaultdict(set) - cmu_data = Path(cmudict).read_bytes() - for line in cmu_data.decode("utf-8").splitlines(): - fields = line.split() - if fields: - phones[fields[0].split("(", 1)[0].casefold()].add(tuple(fields[1:])) + cmu_data = Path(cmudict).read_bytes().replace(b"\r\n", b"\n") + phones = cmu_pronunciations(cmu_data) multiple = [{"surface": word, "readings": sorted({row[12] for _, row in entries}), "lines": [line for line, _ in entries]} for word, entries in words.items() if len({row[12] for _, row in entries}) > 1] @@ -153,11 +247,13 @@ def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], "disposition": "review_only; Japanese loanword forms may legitimately coincide"}) _, normalization = normalize_rows(rows, count_mora, normalize_surface) duplicates = Counter(tuple(row) for row in rows) + _, extension = filter_extension_rows(rows, phones, base_word or (lambda _: False)) return {"entries": len(rows), "distinct_surfaces": len(words), "cmudict_sha256": hashlib.sha256(cmu_data).hexdigest(), "exact_duplicate_rows": sum(count - 1 for count in duplicates.values()), "multiple_reading_surfaces": len(multiple), "multiple_readings": multiple, "inflection_candidate_count": len(candidates), "inflection_candidates": candidates, + "extension_filter": extension, "normalization": normalization, "limitations": "Candidates do not prove lexical errors. Legitimate heteronyms and loanword inflections are retained. No LLM, network, waveform synthesis or semantic classifier is used."} @@ -165,12 +261,14 @@ def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--source", type=Path, required=True) - parser.add_argument("--cmudict", type=Path, default=PROJECT_ROOT / "GPT_SoVITS/text/cmudict-fast.rep") + parser.add_argument("--cmudict", type=Path, default=PROJECT_ROOT / "GPT_SoVITS/text/cmudict.rep") parser.add_argument("--output", type=Path, required=True) args = parser.parse_args() count, version = native_mora_counter() source_data = args.source.read_bytes() - report = audit_rows(list(dictionary_rows(source_data)), args.cmudict, count, native_surface_normalizer()) + report = audit_rows(list(dictionary_rows(source_data)), args.cmudict, count, native_surface_normalizer(), + native_base_word_lookup()) + report["base_dictionary_sha256"] = native_base_hashes() report["source_sha256"] = hashlib.sha256(source_data).hexdigest() report["native_runtime"] = version args.output.parent.mkdir(parents=True, exist_ok=True) diff --git a/tools/data/japanese_dictionary_common_words.json b/tools/data/japanese_dictionary_common_words.json new file mode 100644 index 00000000..54c965a6 --- /dev/null +++ b/tools/data/japanese_dictionary_common_words.json @@ -0,0 +1,318 @@ +{ + "description": "Frozen regression vocabulary: frequent CMU English tokens in tracked Markdown, independent final-phone risk surfaces occurring at least three times there, the 11 externally reported regressions, and all 48 distinct words from the external 49-row frequent-doc subset. Counts select coverage, not correct pronunciations.", + "selected_on": "2026-10-20", + "words": [ + "ablation", + "acceptance", + "accepted", + "acknowledgement", + "action", + "actual", + "after", + "agent", + "all", + "alpha", + "also", + "amadeus", + "ambient", + "and", + "api", + "app", + "application", + "applied", + "arbitrary", + "are", + "arrived", + "assets", + "audio", + "audition", + "auto", + "awake", + "backend", + "before", + "begin", + "blackwell", + "borderline", + "both", + "boundary", + "bounded", + "branch", + "browser", + "build", + "but", + "button", + "cache", + "can", + "cancelled", + "cannot", + "change", + "changes", + "character", + "chat", + "check", + "checks", + "classification", + "code", + "com", + "commented", + "companion", + "complete", + "configuration", + "context", + "continued", + "contract", + "contributing", + "contribution", + "conversation", + "conversion", + "core", + "could", + "cpu", + "current", + "deadline", + "default", + "desktop", + "device", + "directed", + "directly", + "display", + "docker", + "docs", + "does", + "drive", + "driver", + "duplex", + "each", + "edu", + "electron", + "embedded", + "enough", + "entry", + "environment", + "every", + "evidence", + "exact", + "execution", + "existing", + "explicit", + "extension", + "external", + "extra", + "file", + "files", + "final", + "first", + "flash", + "flowchart", + "for", + "frame", + "from", + "full", + "gain", + "game", + "github", + "graph", + "has", + "help", + "Help", + "history", + "host", + "hot", + "identity", + "including", + "inference", + "injection", + "input", + "install", + "installation", + "installing", + "interior", + "into", + "isolated", + "its", + "japanese", + "judge", + "just", + "keep", + "key", + "learn", + "let’s", + "lifecycle", + "linux", + "live", + "local", + "locked", + "low", + "lowering", + "main", + "manifest", + "match", + "may", + "mib", + "mode", + "model", + "modeling", + "mora", + "must", + "name", + "native", + "net", + "never", + "new", + "none", + "not", + "now", + "occurred", + "official", + "old", + "one", + "only", + "onto", + "optional", + "original", + "other", + "output", + "pack", + "package", + "passed", + "path", + "per", + "permanently", + "playback", + "playing", + "policy", + "preference", + "probe", + "process", + "production", + "Production", + "profile", + "profiles", + "progressive", + "prompt", + "prototype", + "provider", + "public", + "python", + "quieter", + "rag", + "real", + "reference", + "refusal", + "release", + "reliably", + "remain", + "remains", + "render", + "request", + "reset", + "residual", + "restart", + "restriction", + "retain", + "revision", + "role", + "run", + "same", + "scanning", + "see", + "selected", + "selection", + "semantic", + "send", + "separate", + "server", + "session", + "settings", + "shared", + "ship", + "shutdown", + "similar", + "smaller", + "smoke", + "smooth", + "snapshot", + "source", + "spacing", + "speckle", + "speech", + "start", + "startup", + "state", + "status", + "still", + "strategy", + "subtitle", + "sync", + "test", + "tests", + "text", + "that", + "the", + "their", + "then", + "these", + "this", + "through", + "time", + "token", + "tools", + "torch", + "trait", + "transient", + "trigger", + "true", + "two", + "Two", + "unchanged", + "use", + "used", + "user", + "uses", + "variation", + "verified", + "verify", + "version", + "voice", + "wake", + "wallpaper", + "was", + "waveform", + "welcome", + "were", + "when", + "while", + "width", + "will", + "window", + "windows", + "with", + "without", + "work", + "writing", + "zip" + ], + "reviewed_readings": { + "Help": "ヘルプ", + "learn": "ラーン", + "Two": "ツー", + "with": "ウィズ", + "Production": "プロダクション", + "let’s": "レッツ" + }, + "base_fallback_words": [ + "model", + "welcome", + "directly", + "reset", + "deadline" + ], + "external_audit": { + "input_pack_version": "2026.10.18.1", + "input_csv_sha256": "adb58cb77524441d68d2f1db0c952e999536034341cfd0b542fc4c3b55d39c63", + "flagged_rows": 1557, + "tsv_sha256": "b8446675edbd1ff0796b76ad6167e8e20266b198a9829d8f60bc0e37e0f73e2a", + "frequent_doc_rows": 49, + "frequent_doc_distinct_words": 48 + } +} diff --git a/tools/data/japanese_dictionary_corrections.csv b/tools/data/japanese_dictionary_corrections.csv index 9f59c18b..968e4593 100644 --- a/tools/data/japanese_dictionary_corrections.csv +++ b/tools/data/japanese_dictionary_corrections.csv @@ -170,3 +170,4 @@ two-thirds,1348,1348,273,名詞,固有名詞,一般,*,*,*,トゥーサーズ,トゥーサーズ,トゥーサーズ,1/5,* untruths,1348,1348,556,名詞,固有名詞,一般,*,*,*,アントルーズ,アントルーズ,アントルーズ,5/6,* whiz-zip-bang,1348,1348,46,名詞,固有名詞,一般,*,*,*,ウィズジップバン,ウィズジップバン,ウィズジップバン,7/7,* +let's,1348,1348,398,名詞,固有名詞,一般,*,*,*,レッツ,レッツ,レッツ,1/3,* diff --git a/tools/data/japanese_dictionary_source.json b/tools/data/japanese_dictionary_source.json index 1a13654a..7b29eccd 100644 --- a/tools/data/japanese_dictionary_source.json +++ b/tools/data/japanese_dictionary_source.json @@ -1,5 +1,5 @@ { - "version": "2026.10.18.1", + "version": "2026.10.20.1", "language": "ja", "source": { "repository": "https://github.com/RVC-Boss/GPT-SoVITS", @@ -9881,6 +9881,25 @@ }, "cost_before": 813, "cost_after": 46 + }, + { + "word": "let's", + "before": "レットアップス", + "after": "レッツ", + "reason": "CMU LET'S and LETS have identical L EH1 T S phones. Reuse the existing lets row's レッツ reading and 1/3 accent annotation instead of the shifted let-ups reading; keep the original let's word cost.", + "evidence": { + "upstream_source_lines": [ + 69568, + 69591 + ], + "cmu_phones": [ + "L", + "EH1", + "T", + "S" + ], + "accent_basis": "Reuse the existing same-phoneme lets entry; no listening acceptance claimed." + } } ], "review": { @@ -9908,5 +9927,35 @@ } } } + }, + "extension_filter": { + "cmudict_path": "GPT_SoVITS/text/cmudict.rep", + "cmudict_sha256": "0e601d017d6e6f958443d41cd8922b4cd7598b3ba2056253a33f3e5a35f38494", + "base_dictionary_sha256": { + "sys.dic": "0328ca62355100aba6df13af56912ad88cf10246e2a286005b89cb8ce2cef652", + "matrix.bin": "62fd16b4f64c851d5dc352ef0d5740c5fc83ddc7c203b2b0b1fc5271969a14ce", + "char.bin": "888ee94c5a8a7a26d24ab3f1b7155441351954fd51ea06b4a2f78bd742492b2f" + }, + "rules": [ + "Exclude every imported row for a surface selected as one known whole word by an independent base MeCab instance; unknown letter-spelled tokens do not count as known words.", + "Quarantine all variants of a surface if any reading ends in ズ without CMU S/Z, ド without D/T, or ング without NG. Accept every CMU pronunciation variant. These are the external audit rules; omit its unreachable ッド branch. Do not promote another unverified shifted reading.", + "Raise plain Latin word costs to one common floor, keeping higher existing costs and punctuation-bearing compound costs; prevent cheap imported fragments from splitting known base words." + ], + "limitations": "Final-phone screening is conservative quarantine, not proof of an error or a proposed replacement. Legitimate loanword adaptations can be excluded. Interior shifts and vowel-ending errors can escape this rule. The common cost floor is qualified against all base-owned imported words in three native contexts and the retained reviewed-word gate, not all possible Japanese contexts. No runtime word exceptions are added.", + "latin_word_cost_floor": 10300, + "cmudict_hash_normalization": "UTF-8 bytes with CRLF normalized to LF", + "external_audit": { + "input_pack_version": "2026.10.18.1", + "input_csv_sha256": "adb58cb77524441d68d2f1db0c952e999536034341cfd0b542fc4c3b55d39c63", + "flagged_rows": 1557, + "suffix_counts": { + "ズ": 807, + "ド": 523, + "ング": 227 + }, + "frequent_doc_rows": 49, + "frequent_doc_distinct_words": 48, + "flag_list_sha256": "b8446675edbd1ff0796b76ad6167e8e20266b198a9829d8f60bc0e37e0f73e2a" + } } } diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py index a9e05273..fe2f5110 100644 --- a/tools/prepare_japanese_dictionary.py +++ b/tools/prepare_japanese_dictionary.py @@ -18,7 +18,10 @@ from config.japanese_dictionary import ( # noqa: E402 PACK_TREE, JapaneseDictionaryError, dictionary_rows, load_dictionary_pack, ) -from tools.audit_japanese_dictionary import native_mora_counter, native_surface_normalizer, normalize_rows # noqa: E402 +from tools.audit_japanese_dictionary import ( # noqa: E402 + cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, + native_mora_counter, native_surface_normalizer, normalize_rows, +) RECIPE_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_source.json" CORRECTIONS_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_corrections.csv" @@ -27,7 +30,8 @@ def prepare_dictionary(source: Path, *, project_root: Path = PROJECT_ROOT, recipe_path: Path = RECIPE_PATH, corrections_path: Path = CORRECTIONS_PATH, overwrite: bool = False, count_mora: Callable[[str], int | None] | None = None, - normalize_surface: Callable[[str], str] | None = None) -> Path: + normalize_surface: Callable[[str], str] | None = None, + base_word: Callable[[str], bool] | None = None) -> Path: recipe = json.loads(Path(recipe_path).read_text(encoding="utf-8")) data = Path(source).read_bytes().replace(b"\r\n", b"\n") if hashlib.sha256(data).hexdigest() != recipe["source"]["sha256"]: @@ -40,13 +44,22 @@ def normalize(value: str) -> str: correction_data = Path(corrections_path).read_bytes().replace(b"\r\n", b"\n") corrections = {normalize(row[0]): row for row in dictionary_rows(correction_data)} before = {normalize(item["word"]): item["before"] for item in recipe["corrections"]} + policy = recipe["extension_filter"] + cmu_data = (PROJECT_ROOT / policy["cmudict_path"]).read_bytes().replace(b"\r\n", b"\n") + if hashlib.sha256(cmu_data).hexdigest() != policy["cmudict_sha256"]: + raise JapaneseDictionaryError("CMU screening data differs from the pinned recipe") if count_mora is None: count_mora, runtime_version = native_mora_counter() if runtime_version != recipe["normalization"]["native_runtime"]: raise JapaneseDictionaryError("Prepare with the recipe's pinned pyopenjtalk-plus runtime") normalize_surface = native_surface_normalizer() if normalize_surface is None else normalize_surface + if native_base_hashes() != policy["base_dictionary_sha256"]: + raise JapaneseDictionaryError("Base dictionary differs from the pinned recipe") + base_word = native_base_word_lookup() if base_word is None else base_word else: runtime_version = "injected" + if base_word is None: + raise JapaneseDictionaryError("An injected mora counter also requires an explicit base-word lookup") # A repair targets the reviewed bad pronunciation, not every reading of # the same surface. Preserve legitimate alternatives and their costs. corrected = [] @@ -63,6 +76,8 @@ def normalize(value: str) -> str: row = list(correction) corrected.append(row) corrected, normalization = normalize_rows(corrected, count_mora, normalize_surface) + corrected, filtering = filter_extension_rows(corrected, cmu_pronunciations(cmu_data), base_word, + cost_floor=policy["latin_word_cost_floor"]) buffer = io.StringIO(newline="") csv.writer(buffer, lineterminator="\n").writerows(corrected) derivative = buffer.getvalue().encode("utf-8") @@ -70,6 +85,7 @@ def normalize(value: str) -> str: "corrections_sha256": hashlib.sha256(correction_data).hexdigest(), "normalization": {**recipe["normalization"], "results": {key: value for key, value in normalization.items() if key != "unresolved"}}, + "extension_filter": {**policy, "results": filtering}, "native_runtime": runtime_version} files = { "userdict.csv": derivative, diff --git a/tools/validate_japanese_dictionary.py b/tools/validate_japanese_dictionary.py index 03d33603..fc4b298e 100644 --- a/tools/validate_japanese_dictionary.py +++ b/tools/validate_japanese_dictionary.py @@ -3,6 +3,7 @@ import argparse from collections import defaultdict +import hashlib import json from pathlib import Path import sys @@ -11,6 +12,9 @@ sys.path.insert(0, str(ROOT)) from config.japanese_dictionary import PACK_TREE, dictionary_rows, load_dictionary_pack # noqa: E402 +from tools.audit_japanese_dictionary import ( # noqa: E402 + cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, word_key, +) from tools.tts_text_processor import correct_pronunciation_for_tts # noqa: E402 from tts.japanese_dictionary import configure_japanese_dictionary # noqa: E402 @@ -37,36 +41,75 @@ "g", "mg", "kg", "t", "m", "cm", "mm", "km", "ml", "mL", "L", "kW", "MW", "W", "V", "mV", "A", "mA", "Hz", "kHz", "MHz", "GHz", "s", "ms", "ns", "kB", "MB", "GB", "TB", "dB", "rpm", "℃", )) +REPORTED_CONTEXTS = { + "Helpを押して。": "help", "このbugからlearnしましょう。": "learn", "Twoつ目の方法。": "two", + "Python with Docker。": "with", "Productionに出す。": "production", "モデルのresetをする。": "reset", + **{f"この{word}を確認して。": word for word in ("model", "welcome", "directly", "reset", "deadline")}, +} def validate(project_root: Path) -> dict: import pyopenjtalk sys.path.insert(0, str(ROOT / "GPT_SoVITS")) from text import japanese + from text.cleaner import clean_text + + def production_phones(text: str) -> list[str]: + return clean_text(correct_pronunciation_for_tts(text), "ja", "v2")[0] project_root = project_root.resolve() pack = load_dictionary_pack(project_root / "assets" / PACK_TREE) rows = list(dictionary_rows((pack.root / "userdict.csv").read_bytes())) + metadata = json.loads((pack.root / "provenance.json").read_text(encoding="utf-8")) + policy = metadata["extension_filter"] + if native_base_hashes() != policy["base_dictionary_sha256"]: + raise RuntimeError("Release gate requires the recipe's base dictionary") + base_word = native_base_word_lookup() + cmu_data = (ROOT / policy["cmudict_path"]).read_bytes().replace(b"\r\n", b"\n") + if hashlib.sha256(cmu_data).hexdigest() != policy["cmudict_sha256"]: + raise RuntimeError("Release gate requires the recipe's CMU screening data") + cmu = cmu_pronunciations(cmu_data) + _, screening = filter_extension_rows(rows, cmu, base_word, cost_floor=policy["latin_word_cost_floor"]) + vocabulary_data = json.loads((ROOT / "tools/data/japanese_dictionary_common_words.json").read_text(encoding="utf-8")) + common = vocabulary_data["words"] + excluded = policy["results"]["excluded"] + base_owned = sorted({item["surface"] for item in excluded if "base_dictionary_word" in item["reasons"]}) + corrected = list(dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) surface = jtalk.normalize_for_mecab + + def target_readings(text: str, word: str) -> list[str]: + return [f["read"] for f in pyopenjtalk.run_frontend(correct_pronunciation_for_tts(text).lower()) + if f["string"] == surface(word)] expected = defaultdict(set) for row in rows: expected[row[0]].add(row[11]) failures, contexts, frontend_words = [], 0, 0 + if screening["excluded_entries"]: + failures.append({"kind": "unscreened_entries", "entries": screening["excluded"]}) + if screening["latin_word_costs_raised"]: + failures.append({"kind": "unbounded_word_costs", "count": screening["latin_word_costs_raised"]}) try: pyopenjtalk.unset_user_dict() - baseline = {text: japanese.g2p(correct_pronunciation_for_tts(text)) for text in BASE_SAMPLES} - unit_baseline = {text: japanese.g2p(correct_pronunciation_for_tts(text)) for text in UNIT_SAMPLES} + baseline = {text: production_phones(text) for text in BASE_SAMPLES} + unit_baseline = {text: production_phones(text) for text in UNIT_SAMPLES} + # Compare every base-owned imported surface, not just a hand-picked + # sample. Include Japanese contexts so cheap prefix splits cannot pass. + base_controls = {prefix + word + suffix for word in base_owned + for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。"))} + english_controls = base_controls | set(common) | set(REPORTED_CONTEXTS) + english_baseline = {text: production_phones(text) for text in sorted(english_controls)} + context_baseline = {text: target_readings(text, word) for text, word in REPORTED_CONTEXTS.items()} status = configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=True) if not status["ready"]: raise RuntimeError(status["detail"]) for text, before in baseline.items(): - after = japanese.g2p(correct_pronunciation_for_tts(text)) + after = production_phones(text) if after != before: failures.append({"kind": "base_regression", "text": text, "before": before, "after": after}) unit_prosody_changes = [] without_prosody = lambda phones: [phone for phone in phones if phone not in {"[", "]", "#"}] for text, before in unit_baseline.items(): - after = japanese.g2p(correct_pronunciation_for_tts(text)) + after = production_phones(text) if without_prosody(after) != without_prosody(before): failures.append({"kind": "unit_phonemes", "text": text, "before": before, "after": after}) elif after != before: @@ -74,11 +117,40 @@ def validate(project_root: Path) -> dict: # reading. Record it for listening review without inventing an # accent correction or a per-unit exclusion. unit_prosody_changes.append({"text": text, "before": before, "after": after}) - corrected = list(dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) + vocabulary = [] + quarantined = {item["surface"] for item in excluded} - {word_key(row[0]) for row in rows} + for text, before in english_baseline.items(): + after = production_phones(text) + must_preserve = text in base_controls or text in vocabulary_data["base_fallback_words"] + if must_preserve and after != before: + failures.append({"kind": "english_base_regression", "text": text, "before": before, "after": after}) + if text in common: + observed = pyopenjtalk.run_frontend(correct_pronunciation_for_tts(text).lower()) + reading = vocabulary_data["reviewed_readings"].get(text) + if reading and [f["read"] for f in observed] != [reading]: + failures.append({"kind": "reviewed_english_reading", "text": text, "expected": reading, + "observed": observed}) + vocabulary.append({"word": text, "base_owned": base_word(text), + "quarantined": word_key(text) in quarantined, + "changed": after != before, "readings": [f["read"] for f in observed]}) + sentence_results = [] + for text, word in REPORTED_CONTEXTS.items(): + before, after = context_baseline[text], target_readings(text, word) + if len(before) != 1 or after != before: + failures.append({"kind": "reported_sentence_reading", "text": text, "word": word, + "before": before, "after": after}) + # Other English words may legitimately improve (Python in the + # with sentence). Keep the full production-path observation visible. + sentence_results.append({"text": text, "word": word, "before": before, "after": after, + "sentence_phones_changed": production_phones(text) != english_baseline[text]}) words = {row[0]: row[0] for row in corrected} words.update({surface(word): word for word in CONTRACTIONS}) for key, word in words.items(): key = surface(key.lower()) + if key not in expected: + # Corrections are historical evidence, not mandatory overrides + # of the base or of conservative release-data exclusions. + continue for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): whole = [f for f in pyopenjtalk.run_frontend(prefix + word.lower() + suffix) if f["string"] == key] contexts += 1 @@ -93,6 +165,10 @@ def validate(project_root: Path) -> dict: return {"version": pack.version, "entry_count": pack.entry_count, "sha256": pack.sha256, "base_samples": len(BASE_SAMPLES), "native_contexts": contexts, "additional_unit_phoneme_checks": len(UNIT_SAMPLES), + "base_owned_words": len(base_owned), "english_base_contexts": len(base_controls), + "common_english_words": len(common), "common_english_results": vocabulary, + "external_sentence_contexts": len(REPORTED_CONTEXTS), + "external_sentence_results": sentence_results, "unit_prosody_changes_for_review": unit_prosody_changes, "production_frontend_words": frontend_words, "failures": failures, "limitations": "Native phoneme/prosody and token-selection checks; no waveform or listening acceptance."} @@ -108,7 +184,8 @@ def main() -> int: report = validate(args.project_root) args.output.parent.mkdir(parents=True, exist_ok=True) args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") - print(json.dumps({key: value for key, value in report.items() if key != "failures"})) + print(json.dumps({key: value for key, value in report.items() + if key not in {"failures", "common_english_results", "external_sentence_results"}})) print(f"Failures: {len(report['failures'])}") return int(bool(report["failures"])) From b85250ff3355359f9caecc11b9907976f5e27c49 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 04:40:32 +1300 Subject: [PATCH 05/10] fix(tts): match optional Japanese dictionary entries as whole words --- GPT_SoVITS/text/japanese.py | 6 +- LICENSES/JAPANESE-DICTIONARY.md | 39 +- config/japanese_dictionary.py | 6 + docs/japanese_pronunciation_dictionary.md | 98 +- tests/test_japanese_dictionary.py | 24 +- tests/test_japanese_dictionary_boundaries.py | 3 +- tests/test_japanese_dictionary_data_policy.py | 57 +- tools/audit_japanese_dictionary.py | 55 +- .../japanese_dictionary_common_words.json | 7 +- .../data/japanese_dictionary_corrections.csv | 221 ++-- tools/data/japanese_dictionary_source.json | 1016 +++++++++++------ tools/prepare_japanese_dictionary.py | 7 +- tools/validate_japanese_dictionary.py | 49 +- tts/japanese_dictionary.py | 35 +- 14 files changed, 1033 insertions(+), 590 deletions(-) diff --git a/GPT_SoVITS/text/japanese.py b/GPT_SoVITS/text/japanese.py index b42db694..4c6d9751 100644 --- a/GPT_SoVITS/text/japanese.py +++ b/GPT_SoVITS/text/japanese.py @@ -2,7 +2,7 @@ import re import pyopenjtalk -from tts.japanese_dictionary import configure_japanese_dictionary +from tts.japanese_dictionary import configure_japanese_dictionary, with_latin_word_boundaries configure_japanese_dictionary(pyopenjtalk) @@ -93,7 +93,7 @@ def preprocess_jap(text, with_prosody=False): if with_prosody: text += pyopenjtalk_g2p_prosody(sentence)[1:-1] else: - p = pyopenjtalk.g2p(sentence) + p = pyopenjtalk.g2p(with_latin_word_boundaries(sentence)) text += p.split(" ") if i < len(marks): @@ -133,7 +133,7 @@ def pyopenjtalk_g2p_prosody(text, drop_unvoiced_vowels=True): modeling for neural TTS`: https://doi.org/10.1587/transinf.2020EDP7104 """ - labels = pyopenjtalk.make_label(pyopenjtalk.run_frontend(text)) + labels = pyopenjtalk.make_label(pyopenjtalk.run_frontend(with_latin_word_boundaries(text))) N = len(labels) phones = [] diff --git a/LICENSES/JAPANESE-DICTIONARY.md b/LICENSES/JAPANESE-DICTIONARY.md index bbd18863..79252c26 100644 --- a/LICENSES/JAPANESE-DICTIONARY.md +++ b/LICENSES/JAPANESE-DICTIONARY.md @@ -48,27 +48,32 @@ then removes only identical full rows. Different lexical readings remain. The generated `provenance.json` additionally identifies the derivative's version, hash, entry count, normalization rules and repair counters. -Version 2026.10.20.1 retains the previous correction history and adds a +Version 2026.10.20.2 retains the previous correction history and adds a source-backed `let's → レッツ` repair: the existing `lets` entry has the same -CMU phones. Its accent annotation is reused, not newly validated by listening. +CMU phones. Its accent annotation is reused, not validated by listening. The historical 317-row review comprises 94 lexical/script/annotation repairs, 65 representation repairs and 158 unconfirmed readings. The release filter -subsequently yields known whole words to the pinned base dictionary and -quarantines every variant of CMU final-phone risk surfaces. The CMU file and -base dictionary hashes, rules and complete exclusion records are in provenance. -The direct flags exactly reproduce the external 1,557-row TSV; extending -quarantine to its other same-surface variants excludes another 112 rows. -This screen admits false positives and can miss other errors; it is not a claim -that each excluded row is wrong. A uniform 10,300 floor for plain Latin word -costs supersedes earlier per-entry cost calibration, preventing observed cheap -fragment splits of known base words without runtime word exceptions. +yields known whole words and redundant letter readings to the pinned base +and quarantines every variant of CMU final-phone risk surfaces. CMU/base +hashes, rules and complete exclusion records are in provenance. The direct +flags reproduce the external 1,557-row TSV; quarantine of their other variants +excludes another 112 rows. This screen admits false positives and can miss +other errors; it does not establish that each excluded row is wrong. -From 127,811 normalized entries, 2,422 are excluded (769 base-owned rows, -1,669 CMU-risk rows, 16 overlap), leaving 125,389. The floor raises 116,922 -retained costs. Three-context validation covers all 746 base-owned surfaces; -289 common English words extend the former 52-control sample. The gate does -not establish exhaustive lexical accuracy. `proposer` still uses a provisional -flat accent; waveform/listening acceptance remains outstanding. +The prepared CSV and frontend share full-token boundaries on retained Latin +entries. Only complete registered official tokens are marked, preventing +prefix and suffix intrusion while leaving unlisted/base/legacy/personal-only +inputs unchanged. All original source costs are preserved; historical cost +calibration and the common floor are retired. This CSV requires the matching +frontend integration, not only an OpenJTalk user-dictionary registration. + +From 127,808 normalized entries, 2,502 are excluded (849 base-owned rows, +1,669 CMU-risk rows, 16 overlap), leaving 125,306. Three-context validation +covers all 814 base-owned surfaces, and 290 common English words extend the +former 52-control sample. Native selection across 122,978 retained distinct +surfaces in three contexts loses no previously selected whole-word match. +These checks do not establish exhaustive lexical accuracy. `proposer` still +uses a provisional flat accent; waveform/listening acceptance is outstanding. The upstream default branch was rechecked on 2026-10-20 at `b4570b876f5c086819ba84e3215472733c6a7b70`: the CSV still has the same Git blob diff --git a/config/japanese_dictionary.py b/config/japanese_dictionary.py index cd455541..115e93b6 100644 --- a/config/japanese_dictionary.py +++ b/config/japanese_dictionary.py @@ -9,15 +9,21 @@ from functools import lru_cache from pathlib import Path from typing import Iterator +import unicodedata PACK_ID = "japanese-pronunciation" PACK_TREE = "text/ja/english" +LATIN_WORD_BOUNDARY = "\u3000" PACK_FILES = ( "userdict.csv", "provenance.json", "NOTICE.md", "GPT-SoVITS-MIT.txt", "CMUdict-BSD-2-Clause.txt", "Amadeus-AGPL-3.0.txt", ) +def latin_word_key(value: str) -> str: + return unicodedata.normalize("NFKC", value).translate(str.maketrans("’−", "'-")).casefold().strip(" ") + + class JapaneseDictionaryError(ValueError): pass diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index 9c9038e3..a4b01a74 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -15,8 +15,8 @@ loader before installation. Older applications reject the unknown pack ID. The first compatible application release has not yet been published. ```powershell -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.1.zip -python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.20.1.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.2.zip +python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.20.2.zip python tools/external_assets.py status ``` @@ -91,8 +91,8 @@ is explicit. ```powershell python tools/prepare_japanese_dictionary.py --source GPT_SoVITS/text/ja_userdic/userdict.csv python tools/validate_japanese_dictionary.py --output output/japanese_dictionary_validation.json -python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.20.1.zip -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.1.zip +python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.20.2.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.2.zip python tools/audit_japanese_dictionary.py --source assets/text/ja/english/userdict.csv --output output/japanese_dictionary_audit.json ``` @@ -103,51 +103,58 @@ model paths or rebuild any existing model/voice archive. ## Quality boundary / 质量范围 -Version 2026.10.20.1 retains the historical review of 317 entries: 94 +Version 2026.10.20.2 retains the historical review of 317 entries: 94 lexical/script/annotation repairs, 65 separator/format repairs, and 158 unconfirmed readings. Those decisions are evidence, not mandatory release -inclusions. The build subsequently applies the conservative extension policy -below; exclusions and cost changes also apply to historically reviewed rows. -The correction CSV now has 173 rows. The additional `let's` repair reuses -`lets → レッツ` and its accent annotation: both have CMU `L EH1 T S`. +inclusions. The correction CSV has 172 rows after retiring the cost-only +`components` entry and adding `let's → レッツ`. The latter reuses the existing +`lets` reading and accent annotation: both have CMU `L EH1 T S`. Preparation applies these rules offline, after representation repairs: - Yield every surface recognized as one known whole word by an independent - base MeCab instance to that base. Unknown letter-spelled tokens do not qualify. - Pin the native runtime and `sys.dic`, `matrix.bin`, `char.bin` hashes. + base MeCab instance to that base. Also remove redundant letter/initialism + readings identical to the base result, such as `rpm`. Other unknown words + remain eligible for improved readings. Pin the runtime and base dictionary. - Quarantine every variant of a surface if any reading's final Katakana consonant conflicts with every CMU pronunciation variant: ズ requires S/Z, - ド requires D/T, and ング requires NG. These match the external audit; its - unreachable ッド branch is omitted. Removing only one bad variant could promote - another shifted reading, as with `directly → director`. -- Raise plain Latin word costs to a common floor of 10,300, preserving higher - costs and punctuation-bearing compound costs. Removing same-name rows alone - lets cheap fragments split known words (`production → pro + duct + ion`). - This uniform floor supersedes the earlier per-entry cost calibration. At - 10,200, native `hilt + on` still beat `hilton` by one cost unit in one control - context; 10,300 passes all 746 base-word controls in three contexts while - preserving the retained reviewed-word/compound gate. - -From 127,811 normalized rows, the policy excludes 2,422 rows: 769 base-owned -rows and 1,669 rows associated with CMU-risk surfaces (16 overlap). It raises -116,922 retained plain-word costs. The resulting pack contains 125,389 rows. -The provenance records each exclusion's normalized input line, surface, -reading and reason. CMU data and base dictionary hashes are pinned. + ド requires D/T, and ング requires NG. Removing only one bad variant could + promote another shifted reading (`directly → director`). +- Surround retained Latin surfaces with native fullwidth spaces. The frontend + applies both boundaries only to complete tokens present in the successfully + registered official CSV, after sentence/punctuation splitting. Thus `mode` + cannot intrude into `model`, nor `con` into `icon`. Unlisted words and units + keep their input unchanged; base, legacy and personal-only behavior is kept. + Both prosody and plain-phoneme paths use this contract. Raw pyopenjtalk callers + of this prepared CSV must use the same helper. This requires the matching + frontend code, in addition to the installer/catalog entry. + +All original source costs are preserved. Whole-token boundaries supersede +both the historical 109-entry calibration and the rejected common cost floor; +changing costs traded correct short-word readings against longer-word splits. +Historical cost evidence is explicitly marked retired. There are no runtime +per-word reading exceptions; the runtime vocabulary comes from the exact +successfully loaded official bytes and is cleared on disable/load failure. +The audio-cache identity includes this frontend contract. + +From 127,808 normalized rows, the policy excludes 2,502 rows: 849 base-owned +rows and 1,669 rows associated with CMU-risk surfaces (16 overlap). The resulting +pack has 125,306 rows; 125,303 Latin rows receive word boundaries. Provenance +records every exclusion's normalized input line, surface, reading and reason. +CMU data and native base hashes are pinned. The direct flags exactly reproduce all 1,557 external TSV records (ズ 807, -ド 523, ング 227); none of those original word/reading pairs remain. The -additional 112 same-surface variants are quarantined to avoid promoting -another shifted row. These are conservative exclusions, not 1,669 proven errors. For -example, `with → ウィズ` and `condo → コンド` can be legitimate loanword -adaptations despite a flagged final consonant. The rule can miss interior -shifts and vowel-ending errors. It neither guesses replacements nor promises -exhaustive dictionary accuracy. The older singular/inflection collision list -remains review-only (`path` and `paths` can both be `パス`). - -运行时没有新增逐词判断。构建时优先使用基础词典,隔离词尾音可疑的整个词形, -并统一限制普通拉丁词条的低代价拆词行为。隔离清单包含误报,不等于全部确认有错; -词内错位及部分元音结尾错误仍需审计。可确认的 `let's` 错行使用已有 `lets` 的读音修复。 +ド 523, ング 227); none of those original word/reading pairs remain. Another +112 same-surface variants are quarantined to avoid promoting shifted rows. +These are conservative exclusions, not 1,669 proven errors: `with → ウィズ` +and `condo → コンド` can be legitimate loanword adaptations. The rule can miss +interior shifts and vowel-ending errors. The older singular/inflection collision +list remains review-only (`path` and `paths` can both be `パス`). + +构建时优先使用基础词典,隔离词尾音可疑的整个词形;正式包只在完整词边界命中, +避免短词串入其他单词。原有词条代价全部保留,代价补丁已撤掉。未收录的词、 +单位及仅使用个人或旧词典的路径保持原输入。隔离清单含误报,不等于全部确认有错; +可确认的 `let's` 错行使用已有 `lets` 的读音修复。 Whole-token abbreviation preparation handles `MLP` next to Japanese, fullwidth Latin text and case variants of existing names, and avoids replacing `API` @@ -162,20 +169,25 @@ replace end-to-end synthesis and human listening acceptance. The native release gate compares the original 52 controls and 32 additional unit samples against the production `japanese.g2p` path. It also compares all -746 base-owned imported words in three contexts (2,238 controls), exercises -289 frozen common English words, including all 48 distinct words in the +814 base-owned imported words in three contexts (2,442 controls), exercises +290 frozen common English words, including all 48 distinct words in the external 49-row frequent-document subset, and validates the 11 reported regressions -with six reviewed readings and five unchanged base fallbacks. The broader +with reviewed readings and five unchanged base fallbacks. The broader vocabulary is selected from frequent tracked Markdown words and flagged surfaces; inclusion alone is not a pronunciation correctness oracle. Changes for other unknown words are recorded for review. -Retained corrected rows and contractions pass 537 native contexts and 179 +Retained corrected rows and contractions pass 516 native contexts and 172 production frontend/prosody comparisons. There are no failures or unit-prosody changes in this candidate. These results apply to these controls and the pinned native environment; they do not establish zero regressions across all English vocabulary or replace waveform/listening acceptance. +Across all 122,978 retained distinct surfaces in three contexts, the new native +selection preserves every complete-word match from the previous pack and +selects a complete word for previously split inputs. This checks selection, +not the correctness of every imported reading. + The gate runs offline with a prepared full pack. Core tests use synthetic data and require no dictionary/model downloads. Pack validation checks hash before parsing CSV and memoizes success/failure by member stats. Audio cache keys diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py index 9ad62e41..5b57555f 100644 --- a/tests/test_japanese_dictionary.py +++ b/tests/test_japanese_dictionary.py @@ -25,6 +25,7 @@ @pytest.fixture(autouse=True) def clear_runtime_sources(monkeypatch): + monkeypatch.setattr(dictionary, "_official_latin_words", frozenset()) monkeypatch.setattr(dictionary, "_runtime_status", { "state": "not_loaded", "ready": False, "version": "", "identity": None, "detail": "Not loaded in this test.", }) @@ -39,7 +40,8 @@ def _prepare(root: Path, *, native: bool = False) -> Path: # Preserve the real competing short words: a corrected full-word row # must actually win native segmentation, not merely exist in the CSV. for surface, reading, cost in [("comp", "コンプ", "344"), - ("on", "オン", "227"), ("ent", "エント", "287")]: + ("on", "オン", "227"), ("ent", "エント", "287"), + ("components", "コンポーネンツ", "659")]: row = _row(surface, reading, "1/3" if surface != "on" else "1/2") row[3] = cost data += (",".join(row) + "\n").encode() @@ -50,7 +52,7 @@ def _prepare(root: Path, *, native: bool = False) -> Path: recipe_path.write_text(json.dumps(recipe), encoding="utf-8") return prepare_dictionary(source, project_root=root, recipe_path=recipe_path, count_mora=None if native else lambda _: None, - base_word=None if native else lambda _: False) + base_word=None if native else lambda *_: False) @pytest.mark.parametrize("text,expected", [ @@ -88,7 +90,8 @@ def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): # adaptations; condo -> コンド is a documented false positive (CMU OW). accepted = [row for row in corrections if row[0] != "condo"] assert pack.entry_count == len(accepted) - readings = {row[0]: row[11] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes())} + readings = {row[0].strip(dictionary_pack.LATIN_WORD_BOUNDARY): row[11] + for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes())} assert readings == {row[0]: row[11] for row in accepted} assert readings["follower"] == "フォロワー" assert readings["learner"] == "ラーナー" @@ -206,6 +209,7 @@ def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surfac repair = next(entry for entry in recipe["corrections"] if entry["word"] == "corps") bad = _row("corps", repair["before"], "18/75") legitimate_plural = _row("corps", "コアズ", "1/3") + bad[3], legitimate_plural[3] = "977", "1234" source = tmp_path / "upstream.csv" # csv.writer handles the commas and quotes inside the original LLM annotation. import csv @@ -218,9 +222,13 @@ def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surfac recipe_path = tmp_path / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, - count_mora=lambda _: None, base_word=lambda _: False) - readings = [row[12] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0] == "corps"] + count_mora=lambda _: None, base_word=lambda *_: False) + readings = [row[12] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) + if row[0].strip(dictionary_pack.LATIN_WORD_BOUNDARY) == "corps"] assert set(readings) == {"コア", "コアズ"} + costs = {row[12]: row[3] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) + if row[0].strip(dictionary_pack.LATIN_WORD_BOUNDARY) == "corps"} + assert costs == {"コア": "977", "コアズ": "1234"} assert source.read_bytes() == data @@ -286,6 +294,8 @@ def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_ jtalk = _fake_jtalk(root) status = dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=True) assert status["ready"] + boundary = dictionary_pack.LATIN_WORD_BOUNDARY + assert dictionary.with_latin_word_boundaries("Follower model 100rpm") == boundary + "Follower" + boundary + " model 100rpm" assert len(jtalk.compiled) == 2 assert all(Path(path).is_relative_to(root / ".cache") for path in jtalk.compiled) assert [Path(path).read_bytes() for path in jtalk.registered[-1]] == [ @@ -307,6 +317,7 @@ def test_runtime_uses_pack_before_legacy_and_compiles_only_disposable_cache(tmp_ assert personal.read_text(encoding="utf-8") == LEARNER dictionary.configure_japanese_dictionary(jtalk, project_root=root, enabled=False) assert dictionary.dictionary_runtime_status()["state"] == "disabled" and jtalk.registered[-1] == [] + assert dictionary.with_latin_word_boundaries("Follower") == "Follower" def test_runtime_missing_invalid_and_compile_failure_keep_base_frontend(tmp_path): @@ -317,6 +328,7 @@ def fail_compile(*args): raise RuntimeError("compiler failed") jtalk.mecab_dict_index = fail_compile assert dictionary.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["state"] == "invalid" + assert dictionary.with_latin_word_boundaries("Follower") == "Follower" assert jtalk.registered[-1] == [] assert not list((tmp_path / ".cache/japanese_dictionary").glob("*.dict")) (pack_root / "userdict.csv").write_bytes(b"corrupt") @@ -379,7 +391,7 @@ def test_native_runtime_reads_corrected_words_and_keeps_japanese_intact(tmp_path "Bank": "バンク", "Component": "コンポーネント", "Components": "コンポーネンツ", "Handler": "ハンドラー", "Dentist": "デンティスト", "Balloonist": "バルーニスト", "Ninth": "ナインス", "Intruder": "イントルーダー", "Lawrence": "ローレンス"}.items(): - features = jtalk.run_frontend(word.lower()) + features = jtalk.run_frontend(dictionary.with_latin_word_boundaries(word.lower())) assert len(features) == 1 and features[0]["read"] == expected prepared = correct_pronunciation_for_tts("LeaderとMLPを確認する。") assert "エムエルピー" in "".join(feature["read"] for feature in jtalk.run_frontend(prepared.lower())) diff --git a/tests/test_japanese_dictionary_boundaries.py b/tests/test_japanese_dictionary_boundaries.py index 47a7f47e..492e5ec7 100644 --- a/tests/test_japanese_dictionary_boundaries.py +++ b/tests/test_japanese_dictionary_boundaries.py @@ -13,6 +13,7 @@ @pytest.fixture(autouse=True) def isolate_native_status(monkeypatch): + monkeypatch.setattr(runtime, "_official_latin_words", frozenset()) monkeypatch.setattr(runtime, "_runtime_status", { "state": "not_loaded", "ready": False, "identity": None, "version": "", "detail": "Not loaded", }) @@ -147,7 +148,7 @@ def test_native_units_and_contractions_against_base_frontend(tmp_path): assert runtime.configure_japanese_dictionary(jtalk, project_root=tmp_path, enabled=True)["ready"] assert [japanese.g2p(correct_pronunciation_for_tts(text)) for text in texts] == before for word, reading in (("don't", "ドント"), ("I'm", "アイム")): - assert "".join(f["read"] for f in jtalk.run_frontend(word.lower())) == reading + assert "".join(f["read"] for f in jtalk.run_frontend(runtime.with_latin_word_boundaries(word.lower()))) == reading assert "UNK" not in japanese.g2p(word) finally: jtalk.unset_user_dict() diff --git a/tests/test_japanese_dictionary_data_policy.py b/tests/test_japanese_dictionary_data_policy.py index 293f899e..0a9519a6 100644 --- a/tests/test_japanese_dictionary_data_policy.py +++ b/tests/test_japanese_dictionary_data_policy.py @@ -6,10 +6,12 @@ import pytest from tools.audit_japanese_dictionary import ( - LATIN_WORD_COST_FLOOR, cmu_pronunciations, filter_extension_rows, final_phone_risk, + cmu_pronunciations, filter_extension_rows, final_phone_risk, native_base_word_lookup, word_key, ) from test_japanese_dictionary import _row +from config.japanese_dictionary import LATIN_WORD_BOUNDARY +from tts.japanese_dictionary import with_latin_word_boundaries @pytest.fixture(autouse=True) @@ -18,6 +20,7 @@ def isolate_native_status(monkeypatch): monkeypatch.setattr(runtime, "_runtime_status", { "state": "not_loaded", "ready": False, "identity": None, "version": "", "detail": "Not loaded", }) + monkeypatch.setattr(runtime, "_official_latin_words", frozenset()) @pytest.mark.parametrize("reading,phones,risky", [ @@ -39,19 +42,23 @@ def test_final_phone_screening_includes_variants_and_documents_false_positives(r assert final_phone_risk(reading, phones) is risky -def test_filter_yields_base_words_quarantines_variants_and_bounds_fragment_costs(): +def test_filter_yields_base_words_quarantines_variants_and_bounds_whole_words(): rows = [_row("Help", "ヘルプト"), _row("directly", "ディレクターズ"), - _row("directly", "ディレクター"), _row("follower", "フォロワー"), - _row("don't", "ドント"), _row("pricy", "プライシー")] - rows[-1][3] = "12000" + _row("directly", "ディレクター"), _row("direct", "ダイレクト"), _row("follower", "フォロワー"), + _row("don't", "ドント"), _row("pricy", "プライシー"), _row("hel", "ヘル")] + rows[-2][3] = "12000" before = [list(row) for row in rows] phones = cmu_pronunciations(b"DIRECTLY D ER0 EH1 K T L IY0\nDIRECTLY(1) D IH0 R EH1 K T L IY0\n") - kept, report = filter_extension_rows(rows, phones, lambda word: word == "help") - assert {row[0] for row in kept} == {"follower", "don't", "pricy"} - assert [row[3] for row in kept] == [str(LATIN_WORD_COST_FLOOR), rows[-2][3], "12000"] + kept, report = filter_extension_rows(rows, phones, lambda word, _: word == "help") + assert {row[0] for row in kept} == {LATIN_WORD_BOUNDARY + w + LATIN_WORD_BOUNDARY + for w in ("direct", "follower", "don't", "pricy", "hel")} + costs = {word_key(row[0]): row[3] for row in kept} + assert costs["hel"] == rows[-1][3] + assert costs["direct"] == rows[3][3] and costs["follower"] == rows[4][3] + assert costs["don't"] == rows[5][3] and costs["pricy"] == "12000" assert report["reason_counts"] == {"base_dictionary_word": 1, "cmu_final_phone_risk": 2} assert rows == before, "filtering must not rewrite its evidence input" - assert filter_extension_rows(kept, phones, lambda _: False)[0] == kept + assert filter_extension_rows(kept, phones, lambda *_: False)[0] == kept assert word_key("Let’s") == "let's" @@ -60,23 +67,39 @@ def test_native_base_lookup_does_not_treat_letter_spelling_as_a_word(): known = native_base_word_lookup() assert all(known(word) for word in ("Help", "learn", "Two", "with", "Production")) assert not any(known(word) for word in ("model", "welcome", "directly", "deadline")) + assert known("rpm", "アールピーエム") + assert not known("model", "モデル") -def test_native_cost_floor_preserves_known_words_and_retains_unknown_word_support(tmp_path: Path): +def test_boundaries_require_a_complete_registered_token_and_preserve_numeric_units(): + words = frozenset({"mode", "con", "ion", "don't"}) + text = "model icon production 100rpm 100L 100人" + assert with_latin_word_boundaries(text, words) == text + boundary = LATIN_WORD_BOUNDARY + assert with_latin_word_boundaries("MODEとdon't", words) == boundary + "MODE" + boundary + "と" + boundary + "don't" + boundary + + +def test_native_boundaries_prevent_prefix_and_suffix_intrusion_without_repricing(tmp_path: Path): native = pytest.importorskip("pyopenjtalk") base = native.OpenJTalk(native.OPEN_JTALK_DICT_DIR) - rows = [_row("hilt", "ヒルト", "1/3"), _row("pro", "プロ", "1/2"), + rows = [_row("hilton", "ヒルトン"), _row("production", "プロダクション"), + _row("hilt", "ヒルト", "1/3"), _row("pro", "プロ", "1/2"), _row("duct", "ダクト", "1/3"), _row("ion", "イオン", "1/3"), - _row("follower", "フォロワー", "0/4")] + _row("follower", "フォロワー", "0/4"), _row("con", "コン", "1/2"), + _row("mode", "モード", "1/3")] kept, _ = filter_extension_rows(rows, {}, native_base_word_lookup()) + words = frozenset(word_key(row[0]) for row in kept) source, binary = tmp_path / "source.csv", tmp_path / "user.dict" source.write_text("".join(",".join(row) + "\n" for row in kept), encoding="utf-8") native.mecab_dict_index(str(source), str(binary)) extension = native.OpenJTalk(native.OPEN_JTALK_DICT_DIR, userdic=str(binary).encode()) - for text in ("production", "hiltonを確認する。", "これはhiltonです。"): - assert extension.run_frontend(text) == base.run_frontend(text) - for text in ("follower", "followerを確認する。", "これはfollowerです。"): - assert any(f["read"] == "フォロワー" for f in extension.run_frontend(text)) + for word in ("production", "hilton", "icon", "model", "100人", "100rpm", "100ML"): + for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): + text = prefix + word + suffix + assert extension.run_frontend(with_latin_word_boundaries(text, words)) == base.run_frontend(text) + for word, reading in (("follower", "フォロワー"), ("hilt", "ヒルト"), ("con", "コン"), ("mode", "モード")): + for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): + assert any(f["read"] == reading for f in extension.run_frontend(with_latin_word_boundaries(prefix + word + suffix, words))) def test_reported_regressions_through_preparation_and_production_frontend(tmp_path, monkeypatch): @@ -114,7 +137,7 @@ def phones(text): assert configure_japanese_dictionary(native, project_root=tmp_path, enabled=True)["ready"] for word in bad.keys() - {"let's"}: assert phones(word) == baseline[word] - assert [f["read"] for f in native.run_frontend("let’s")] == ["レッツ"] + assert [f["read"] for f in native.run_frontend(with_latin_word_boundaries("let’s"))] == ["レッツ"] assert phones("let’s") == phones("レッツ") assert source.read_bytes() == data finally: diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py index 3cb68450..13df1c56 100644 --- a/tools/audit_japanese_dictionary.py +++ b/tools/audit_japanese_dictionary.py @@ -18,7 +18,7 @@ sys.path.insert(0, str(PROJECT_ROOT)) from tools.tts_text_processor import convert_english_abbreviations_to_katakana # noqa: E402 -from config.japanese_dictionary import dictionary_rows # noqa: E402 +from config.japanese_dictionary import LATIN_WORD_BOUNDARY, dictionary_rows, latin_word_key # noqa: E402 _KANA = re.compile(r"[\u30a1-\u30fa\u30fc]+") _MORA_PHONES = frozenset({"a", "i", "u", "e", "o", "A", "I", "U", "E", "O", "N", "cl"}) @@ -28,11 +28,10 @@ _FINAL_CONSONANTS = { "ズ": {"Z", "S"}, "ド": {"D", "T"}, "ング": {"NG"}, } -LATIN_WORD_COST_FLOOR = 10300 def word_key(value: str) -> str: - return unicodedata.normalize("NFKC", value).translate(str.maketrans("’−", "'-")).casefold() + return latin_word_key(value) def cmu_pronunciations(data: bytes) -> dict[str, set[tuple[str, ...]]]: @@ -54,7 +53,7 @@ def final_phone_risk(reading: str, pronunciations: set[tuple[str, ...]]) -> bool return False -def native_base_word_lookup() -> Callable[[str], bool]: +def native_base_word_lookup() -> Callable[..., bool]: import pyopenjtalk # Independent base instance: installed official/personal dictionaries must @@ -62,12 +61,22 @@ def native_base_word_lookup() -> Callable[[str], bool]: jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) @lru_cache(maxsize=None) - def known(word: str) -> bool: - word = word_key(word) + def describe(word: str) -> tuple[bool, str | None]: _, morphs = jtalk.run_mecab_detailed(word) tokens = [m for m in morphs if not m["is_ignored"]] - return (len(tokens) == 1 and not tokens[0]["is_unknown"] - and tokens[0]["surface"] == jtalk.normalize_for_mecab(word)) + if len(tokens) != 1 or tokens[0]["surface"] != jtalk.normalize_for_mecab(word): + return False, None + if not tokens[0]["is_unknown"]: + return True, tokens[0]["features"][8] + features = jtalk.run_frontend(word) + reading = features[0]["read"] if len(features) == 1 else None + return False, reading + + def known(word: str, reading: str | None = None) -> bool: + lexical, base_reading = describe(word_key(word)) + # A redundant initialism/letter reading needs no imported override + # either (rpm). Keep unknown words whose imported reading adds value. + return lexical or reading is not None and reading == base_reading return known @@ -82,18 +91,18 @@ def native_base_hashes() -> dict[str, str]: def filter_extension_rows(rows: list[list[str]], phones: dict[str, set[tuple[str, ...]]], - base_word: Callable[[str], bool], *, - cost_floor: int = LATIN_WORD_COST_FLOOR) -> tuple[list[list[str]], dict]: + base_word: Callable[..., bool]) -> tuple[list[list[str]], dict]: """Yield known words to the base and quarantine suspect readings offline.""" kept, excluded = [], [] counts = Counter() direct_flags = [row for row in rows if final_phone_risk(row[11], phones.get(word_key(row[0]), set()))] suspect = {word_key(row[0]) for row in direct_flags} - repriced = 0 + base_words = {word_key(row[0]) for row in rows if base_word(word_key(row[0]), row[11])} + bounded = 0 for number, row in enumerate(rows, 1): word = word_key(row[0]) reasons = [] - if base_word(word): + if word in base_words: reasons.append("base_dictionary_word") # A contaminated surface can have several shifted alternatives. Do not # merely promote the next unverified variant (e.g. directly -> director). @@ -104,21 +113,21 @@ def filter_extension_rows(rows: list[list[str]], phones: dict[str, set[tuple[str excluded.append({"normalized_line": number, "surface": word, "reading": row[12], "reasons": reasons}) else: - # Imported short words must not cheaply split a known base word. - # Use one floor for plain Latin words; punctuation-bearing compounds - # need their existing cost to compete with native boundary splits. - if re.fullmatch(r"[a-z]+", word) and int(row[3]) < cost_floor: + # Match a complete frontend token, never a fragment of another word. + # Preserve source costs; changing costs merely trades competing splits. + if re.search(r"[a-z]", word) and not (row[0].startswith(LATIN_WORD_BOUNDARY) + and row[0].endswith(LATIN_WORD_BOUNDARY)): row = list(row) - row[3] = str(cost_floor) - repriced += 1 + row[0] = LATIN_WORD_BOUNDARY + row[0] + LATIN_WORD_BOUNDARY + bounded += 1 kept.append(row) return kept, {"input_entries": len(rows), "output_entries": len(kept), "excluded_entries": len(excluded), - "reason_counts": dict(counts), "latin_word_cost_floor": cost_floor, + "reason_counts": dict(counts), "direct_final_phone_flags": len(direct_flags), "direct_final_phone_suffix_counts": dict(Counter( next(ending for ending in _FINAL_CONSONANTS if row[11].endswith(ending)) for row in direct_flags)), - "latin_word_costs_raised": repriced, "excluded": excluded} + "latin_word_boundaries_added": bounded, "excluded": excluded} def _without_reading_separators(value: str) -> str: @@ -169,7 +178,7 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None # The Japanese base dictionary owns letters and short Latin tokens, # including measurement suffixes. Imported English fragments must not # displace cm/kg/km or supply unrelated readings for isolated m/w/h. - if re.fullmatch(r"[A-Za-z]{1,2}", unicodedata.normalize("NFKC", row[0])): + if re.fullmatch(r"[a-z]{1,2}", word_key(row[0])): stats["short_latin_entries_excluded"] += 1 continue row[10:13] = [unicodedata.normalize("NFC", value) for value in row[10:13]] @@ -220,7 +229,7 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], int | None], normalize_surface: Callable[[str], str] | None = None, - base_word: Callable[[str], bool] | None = None) -> dict: + base_word: Callable[..., bool] | None = None) -> dict: words = defaultdict(list) for number, row in enumerate(rows, 1): key = word_key(row[0]) @@ -247,7 +256,7 @@ def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], "disposition": "review_only; Japanese loanword forms may legitimately coincide"}) _, normalization = normalize_rows(rows, count_mora, normalize_surface) duplicates = Counter(tuple(row) for row in rows) - _, extension = filter_extension_rows(rows, phones, base_word or (lambda _: False)) + _, extension = filter_extension_rows(rows, phones, base_word or (lambda *_: False)) return {"entries": len(rows), "distinct_surfaces": len(words), "cmudict_sha256": hashlib.sha256(cmu_data).hexdigest(), "exact_duplicate_rows": sum(count - 1 for count in duplicates.values()), diff --git a/tools/data/japanese_dictionary_common_words.json b/tools/data/japanese_dictionary_common_words.json index 54c965a6..76cc7be8 100644 --- a/tools/data/japanese_dictionary_common_words.json +++ b/tools/data/japanese_dictionary_common_words.json @@ -73,6 +73,7 @@ "default", "desktop", "device", + "direct", "directed", "directly", "display", @@ -113,8 +114,8 @@ "github", "graph", "has", - "help", "Help", + "help", "history", "host", "hot", @@ -298,7 +299,9 @@ "Two": "ツー", "with": "ウィズ", "Production": "プロダクション", - "let’s": "レッツ" + "let’s": "レッツ", + "mode": "モード", + "direct": "ダイレクト" }, "base_fallback_words": [ "model", diff --git a/tools/data/japanese_dictionary_corrections.csv b/tools/data/japanese_dictionary_corrections.csv index 968e4593..99fa0ca9 100644 --- a/tools/data/japanese_dictionary_corrections.csv +++ b/tools/data/japanese_dictionary_corrections.csv @@ -7,167 +7,166 @@ airline,1348,1348,504,名詞,固有名詞,一般,*,*,*,エアライン,エアライン,エアライン,3/5,* applicator,1348,1348,659,名詞,固有名詞,一般,*,*,*,アプリケーター,アプリケーター,アプリケーター,4/7,* bank,1348,1348,344,名詞,固有名詞,一般,*,*,*,バンク,バンク,バンク,1/3,* -component,1348,1348,0,名詞,固有名詞,一般,*,*,*,コンポーネント,コンポーネント,コンポーネント,3/7,* -components,1348,1348,0,名詞,固有名詞,一般,*,*,*,コンポーネンツ,コンポーネンツ,コンポーネンツ,0/7,* +component,1348,1348,608,名詞,固有名詞,一般,*,*,*,コンポーネント,コンポーネント,コンポーネント,3/7,* developer,1348,1348,608,名詞,固有名詞,一般,*,*,*,デベロッパー,デベロッパー,デベロッパー,3/6,* handler,1348,1348,504,名詞,固有名詞,一般,*,*,*,ハンドラー,ハンドラー,ハンドラー,1/5,* adhd,1348,1348,344,名詞,固有名詞,一般,*,*,*,エーディーエイチディー,エーディーエイチディー,エーディーエイチディー,8/9,* -allergist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,アレルジストス,アレルジストス,アレルジストス,7/7,* +allergist's,1348,1348,711,名詞,固有名詞,一般,*,*,*,アレルジストス,アレルジストス,アレルジストス,7/7,* amd,1348,1348,287,名詞,固有名詞,一般,*,*,*,エーエムディー,エーエムディー,エーエムディー,5/6,* bahrainian,1348,1348,659,名詞,固有名詞,一般,*,*,*,バーレイニアン,バーレイニアン,バーレイニアン,3/7,* -ballet's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,バレエズ,バレエズ,バレエズ,1/4,* +ballet's,1348,1348,556,名詞,固有名詞,一般,*,*,*,バレエズ,バレエズ,バレエズ,1/4,* balloonist,1348,1348,659,名詞,固有名詞,一般,*,*,*,バルーニスト,バルーニスト,バルーニスト,4/6,* barbecued,1348,1348,608,名詞,固有名詞,一般,*,*,*,バーベキュード,バーベキュード,バーベキュード,4/6,* -business's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ビジネシズ,ビジネシズ,ビジネシズ,5/5,* +business's,1348,1348,659,名詞,固有名詞,一般,*,*,*,ビジネシズ,ビジネシズ,ビジネシズ,5/5,* busloads,1348,1348,556,名詞,固有名詞,一般,*,*,*,バスローズ,バスローズ,バスローズ,0/5,* busts,1348,1348,398,名詞,固有名詞,一般,*,*,*,バストス,バストス,バストス,0/4,* cajoling,1348,1348,556,名詞,固有名詞,一般,*,*,*,カジョーリング,カジョーリング,カジョーリング,2/6,* cds,1348,1348,287,名詞,固有名詞,一般,*,*,*,シーディーズ,シーディーズ,シーディーズ,1/5,* chamonix,1348,1348,556,名詞,固有名詞,一般,*,*,*,シャモニー,シャモニー,シャモニー,0/4,* -christ's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,クライストス,クライストス,クライストス,6/6,* -circuit's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,サーキッツ,サーキッツ,サーキッツ,3/5,* -condo,1348,1348,278,名詞,固有名詞,一般,*,*,*,コンド,コンド,コンド,1/3,* +christ's,1348,1348,556,名詞,固有名詞,一般,*,*,*,クライストス,クライストス,クライストス,6/6,* +circuit's,1348,1348,608,名詞,固有名詞,一般,*,*,*,サーキッツ,サーキッツ,サーキッツ,3/5,* +condo,1348,1348,398,名詞,固有名詞,一般,*,*,*,コンド,コンド,コンド,1/3,* corps,1348,1348,398,名詞,固有名詞,一般,*,*,*,コア,コア,コア,1/2,* -credit's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,クレジッツ,クレジッツ,クレジッツ,0/5,* -cricket's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,クリケッツ,クリケッツ,クリケッツ,3/5,* -criminalist's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,クリミナリスツ,クリミナリスツ,クリミナリスツ,5/7,* +credit's,1348,1348,556,名詞,固有名詞,一般,*,*,*,クレジッツ,クレジッツ,クレジッツ,0/5,* +cricket's,1348,1348,608,名詞,固有名詞,一般,*,*,*,クリケッツ,クリケッツ,クリケッツ,3/5,* +criminalist's,1348,1348,813,名詞,固有名詞,一般,*,*,*,クリミナリスツ,クリミナリスツ,クリミナリスツ,5/7,* debarment,1348,1348,608,名詞,固有名詞,一般,*,*,*,ディバーメント,ディバーメント,ディバーメント,5/6,* -debate's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ディベーツ,ディベーツ,ディベーツ,2/4,* +debate's,1348,1348,556,名詞,固有名詞,一般,*,*,*,ディベーツ,ディベーツ,ディベーツ,2/4,* dentist,1348,1348,504,名詞,固有名詞,一般,*,*,*,デンティスト,デンティスト,デンティスト,3/5,* documented,1348,1348,659,名詞,固有名詞,一般,*,*,*,ドキュメンテッド,ドキュメンテッド,ドキュメンテッド,5/7,* -dolomite's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ドロマイツ,ドロマイツ,ドロマイツ,3/5,* -economist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* -economists',1348,1348,-2221,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* -embassy's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,エンバシーズ,エンバシーズ,エンバシーズ,6/6,* -endocrinologist's,1348,1348,-845,名詞,固有名詞,一般,*,*,*,エンドクリノロジスツ,エンドクリノロジスツ,エンドクリノロジスツ,8/10,* -ernest's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,アーネスツ,アーネスツ,アーネスツ,0/5,* -everest's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,エヴェレストス,エヴェレストス,エヴェレストス,6/6,* +dolomite's,1348,1348,659,名詞,固有名詞,一般,*,*,*,ドロマイツ,ドロマイツ,ドロマイツ,3/5,* +economist's,1348,1348,711,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* +economists',1348,1348,711,名詞,固有名詞,一般,*,*,*,エコノミスツ,エコノミスツ,エコノミスツ,4/6,* +embassy's,1348,1348,608,名詞,固有名詞,一般,*,*,*,エンバシーズ,エンバシーズ,エンバシーズ,6/6,* +endocrinologist's,1348,1348,1016,名詞,固有名詞,一般,*,*,*,エンドクリノロジスツ,エンドクリノロジスツ,エンドクリノロジスツ,8/10,* +ernest's,1348,1348,556,名詞,固有名詞,一般,*,*,*,アーネスツ,アーネスツ,アーネスツ,0/5,* +everest's,1348,1348,608,名詞,固有名詞,一般,*,*,*,エヴェレストス,エヴェレストス,エヴェレストス,6/6,* experienced,1348,1348,711,名詞,固有名詞,一般,*,*,*,エクスペリエンスト,エクスペリエンスト,エクスペリエンスト,6/9,* -faraday's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ファラデーズ,ファラデーズ,ファラデーズ,5/5,* +faraday's,1348,1348,608,名詞,固有名詞,一般,*,*,*,ファラデーズ,ファラデーズ,ファラデーズ,5/5,* fluoresce,1348,1348,608,名詞,固有名詞,一般,*,*,*,フローレス,フローレス,フローレス,2/5,* -gadhafi's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,カダフィズ,カダフィズ,カダフィズ,0/4,* -grant's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,グランツ,グランツ,グランツ,2/4,* +gadhafi's,1348,1348,608,名詞,固有名詞,一般,*,*,*,カダフィズ,カダフィズ,カダフィズ,0/4,* +grant's,1348,1348,504,名詞,固有名詞,一般,*,*,*,グランツ,グランツ,グランツ,2/4,* hermetically,1348,1348,762,名詞,固有名詞,一般,*,*,*,ハーメチックリー,ハーメチックリー,ハーメチックリー,4/8,* hgh,1348,1348,287,名詞,固有名詞,一般,*,*,*,エイチジーエイチ,エイチジーエイチ,エイチジーエイチ,0/8,* -hirohito's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,ヒロヒトズ,ヒロヒトズ,ヒロヒトズ,3/5,* -hoechst's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ヘキスツ,ヘキスツ,ヘキスツ,0/4,* -hospice's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,ホスピシズ,ホスピシズ,ホスピシズ,5/5,* -illusionist's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,イリュージョニスツ,イリュージョニスツ,イリュージョニスツ,5/7,* +hirohito's,1348,1348,659,名詞,固有名詞,一般,*,*,*,ヒロヒトズ,ヒロヒトズ,ヒロヒトズ,3/5,* +hoechst's,1348,1348,608,名詞,固有名詞,一般,*,*,*,ヘキスツ,ヘキスツ,ヘキスツ,0/4,* +hospice's,1348,1348,608,名詞,固有名詞,一般,*,*,*,ホスピシズ,ホスピシズ,ホスピシズ,5/5,* +illusionist's,1348,1348,813,名詞,固有名詞,一般,*,*,*,イリュージョニスツ,イリュージョニスツ,イリュージョニスツ,5/7,* intruder,1348,1348,556,名詞,固有名詞,一般,*,*,*,イントルーダー,イントルーダー,イントルーダー,4/7,* intrusive,1348,1348,608,名詞,固有名詞,一般,*,*,*,イントルーシヴ,イントルーシヴ,イントルーシヴ,1/7,* intrusiveness,1348,1348,813,名詞,固有名詞,一般,*,*,*,イントルーシヴネス,イントルーシヴネス,イントルーシヴネス,0/9,* -iodide's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,アイオダイズ,アイオダイズ,アイオダイズ,4/6,* +iodide's,1348,1348,556,名詞,固有名詞,一般,*,*,*,アイオダイズ,アイオダイズ,アイオダイズ,4/6,* jfet,1348,1348,344,名詞,固有名詞,一般,*,*,*,ジェイフェット,ジェイフェット,ジェイフェット,0/5,* -journalist's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,ジャーナリスツ,ジャーナリスツ,ジャーナリスツ,4/6,* +journalist's,1348,1348,762,名詞,固有名詞,一般,*,*,*,ジャーナリスツ,ジャーナリスツ,ジャーナリスツ,4/6,* lawrence,1348,1348,556,名詞,固有名詞,一般,*,*,*,ローレンス,ローレンス,ローレンス,1/5,* -lieutenant's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,リューテナンツ,リューテナンツ,リューテナンツ,4/6,* -luxottica's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ルクソティカズ,ルクソティカズ,ルクソティカズ,0/6,* -marlboro's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,マールボロズ,マールボロズ,マールボロズ,4/6,* -marriott's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,マリオットス,マリオットス,マリオットス,6/6,* -matsushita's,1348,1348,-1100,名詞,固有名詞,一般,*,*,*,マツシタズ,マツシタズ,マツシタズ,5/5,* -medalist's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,メダリスツ,メダリスツ,メダリスツ,3/5,* +lieutenant's,1348,1348,762,名詞,固有名詞,一般,*,*,*,リューテナンツ,リューテナンツ,リューテナンツ,4/6,* +luxottica's,1348,1348,711,名詞,固有名詞,一般,*,*,*,ルクソティカズ,ルクソティカズ,ルクソティカズ,0/6,* +marlboro's,1348,1348,659,名詞,固有名詞,一般,*,*,*,マールボロズ,マールボロズ,マールボロズ,4/6,* +marriott's,1348,1348,659,名詞,固有名詞,一般,*,*,*,マリオットス,マリオットス,マリオットス,6/6,* +matsushita's,1348,1348,762,名詞,固有名詞,一般,*,*,*,マツシタズ,マツシタズ,マツシタズ,5/5,* +medalist's,1348,1348,659,名詞,固有名詞,一般,*,*,*,メダリスツ,メダリスツ,メダリスツ,3/5,* meteorologist,1348,1348,813,名詞,固有名詞,一般,*,*,*,メテオロジスト,メテオロジスト,メテオロジスト,0/7,* -meteorologist's,1348,1348,-946,名詞,固有名詞,一般,*,*,*,メテオロジスツ,メテオロジスツ,メテオロジスツ,5/7,* -mississippi's,1348,1348,-1048,名詞,固有名詞,一般,*,*,*,ミシシッピーズ,ミシシッピーズ,ミシシッピーズ,0/7,* +meteorologist's,1348,1348,914,名詞,固有名詞,一般,*,*,*,メテオロジスツ,メテオロジスツ,メテオロジスツ,5/7,* +mississippi's,1348,1348,813,名詞,固有名詞,一般,*,*,*,ミシシッピーズ,ミシシッピーズ,ミシシッピーズ,0/7,* mormonism,1348,1348,608,名詞,固有名詞,一般,*,*,*,モルモニズム,モルモニズム,モルモニズム,4/6,* narrates,1348,1348,556,名詞,固有名詞,一般,*,*,*,ナレイツ,ナレイツ,ナレイツ,2/4,* -nike's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,ナイキズ,ナイキズ,ナイキズ,1/4,* +nike's,1348,1348,451,名詞,固有名詞,一般,*,*,*,ナイキズ,ナイキズ,ナイキズ,1/4,* ninth,1348,1348,398,名詞,固有名詞,一般,*,*,*,ナインス,ナインス,ナインス,1/4,* -norway's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ノルウェーズ,ノルウェーズ,ノルウェーズ,5/5,* -office's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,オフィシズ,オフィシズ,オフィシズ,4/4,* +norway's,1348,1348,556,名詞,固有名詞,一般,*,*,*,ノルウェーズ,ノルウェーズ,ノルウェーズ,5/5,* +office's,1348,1348,556,名詞,固有名詞,一般,*,*,*,オフィシズ,オフィシズ,オフィシズ,4/4,* personalizes,1348,1348,762,名詞,固有名詞,一般,*,*,*,パーソナライズィズ,パーソナライズィズ,パーソナライズィズ,0/8,* pinnochio,1348,1348,608,名詞,固有名詞,一般,*,*,*,ピノキオ,ピノキオ,ピノキオ,0/4,* -pinnochio's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ピノキオズ,ピノキオズ,ピノキオズ,5/5,* +pinnochio's,1348,1348,711,名詞,固有名詞,一般,*,*,*,ピノキオズ,ピノキオズ,ピノキオズ,5/5,* politz,1348,1348,451,名詞,固有名詞,一般,*,*,*,ポリツ,ポリツ,ポリツ,0/3,* -poppy's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,ポッピーズ,ポッピーズ,ポッピーズ,5/5,* -post's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,ポスツ,ポスツ,ポスツ,1/3,* +poppy's,1348,1348,504,名詞,固有名詞,一般,*,*,*,ポッピーズ,ポッピーズ,ポッピーズ,5/5,* +post's,1348,1348,451,名詞,固有名詞,一般,*,*,*,ポスツ,ポスツ,ポスツ,1/3,* purchaser,1348,1348,608,名詞,固有名詞,一般,*,*,*,パーチェイサー,パーチェイサー,パーチェイサー,3/6,* -sake's,1348,1348,-1415,名詞,固有名詞,一般,*,*,*,セイクス,セイクス,セイクス,1/4,* -sarajevo's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,サラエボズ,サラエボズ,サラエボズ,5/5,* -schubert's,1348,1348,-1203,名詞,固有名詞,一般,*,*,*,シューベルトス,シューベルトス,シューベルトス,6/6,* -scientist's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,サイエンティスツ,サイエンティスツ,サイエンティスツ,5/7,* -scout's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,スカウツ,スカウツ,スカウツ,2/4,* -shevardnadze's,1348,1348,-997,名詞,固有名詞,一般,*,*,*,シェヴァルドナーゼズ,シェヴァルドナーゼズ,シェヴァルドナーゼズ,0/8,* +sake's,1348,1348,451,名詞,固有名詞,一般,*,*,*,セイクス,セイクス,セイクス,1/4,* +sarajevo's,1348,1348,659,名詞,固有名詞,一般,*,*,*,サラエボズ,サラエボズ,サラエボズ,5/5,* +schubert's,1348,1348,659,名詞,固有名詞,一般,*,*,*,シューベルトス,シューベルトス,シューベルトス,6/6,* +scientist's,1348,1348,711,名詞,固有名詞,一般,*,*,*,サイエンティスツ,サイエンティスツ,サイエンティスツ,5/7,* +scout's,1348,1348,504,名詞,固有名詞,一般,*,*,*,スカウツ,スカウツ,スカウツ,2/4,* +shevardnadze's,1348,1348,863,名詞,固有名詞,一般,*,*,*,シェヴァルドナーゼズ,シェヴァルドナーゼズ,シェヴァルドナーゼズ,0/8,* sizes,1348,1348,398,名詞,固有名詞,一般,*,*,*,サイズィズ,サイズィズ,サイズィズ,0/4,* -smith's,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,スミス,スミス,スミス,1/3,* -soviet's,1348,1348,-1308,名詞,固有名詞,一般,*,*,*,ソビエツ,ソビエツ,ソビエツ,0/4,* +smith's,1348,1348,504,名詞,固有名詞,一般,*,*,*,スミス,スミス,スミス,1/3,* +soviet's,1348,1348,556,名詞,固有名詞,一般,*,*,*,ソビエツ,ソビエツ,ソビエツ,0/4,* stowaway,1348,1348,556,名詞,固有名詞,一般,*,*,*,ストウアウェイ,ストウアウェイ,ストウアウェイ,3/6,* -superintendent's,1348,1348,-896,名詞,固有名詞,一般,*,*,*,スーパーリンテンデンツ,スーパーリンテンデンツ,スーパーリンテンデンツ,7/11,* +superintendent's,1348,1348,965,名詞,固有名詞,一般,*,*,*,スーパーリンテンデンツ,スーパーリンテンデンツ,スーパーリンテンデンツ,7/11,* suvs,1348,1348,344,名詞,固有名詞,一般,*,*,*,エスユーブイズ,エスユーブイズ,エスユーブイズ,2/7,* swords,1348,1348,451,名詞,固有名詞,一般,*,*,*,ソーズ,ソーズ,ソーズ,1/3,* -synergy's,1348,1348,-1255,名詞,固有名詞,一般,*,*,*,シナジーズ,シナジーズ,シナジーズ,5/5,* -t-shirt,1348,1348,91,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* -t-shirts,1348,1348,144,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* +synergy's,1348,1348,608,名詞,固有名詞,一般,*,*,*,シナジーズ,シナジーズ,シナジーズ,5/5,* +t-shirt,1348,1348,504,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* +t-shirts,1348,1348,556,名詞,固有名詞,一般,*,*,*,ティーシャツ,ティーシャツ,ティーシャツ,1/4,* thrusts,1348,1348,504,名詞,固有名詞,一般,*,*,*,スラストス,スラストス,スラストス,5/5,* tlc,1348,1348,287,名詞,固有名詞,一般,*,*,*,ティーエルシー,ティーエルシー,ティーエルシー,0/6,* unfocused,1348,1348,608,名詞,固有名詞,一般,*,*,*,アンフォーカスト,アンフォーカスト,アンフォーカスト,0/7,* untruthful,1348,1348,659,名詞,固有名詞,一般,*,*,*,アントゥルースフル,アントゥルースフル,アントゥルースフル,0/8,* -voldemort's,1348,1348,-1151,名詞,固有名詞,一般,*,*,*,ヴォルデモートス,ヴォルデモートス,ヴォルデモートス,7/7,* +voldemort's,1348,1348,711,名詞,固有名詞,一般,*,*,*,ヴォルデモートス,ヴォルデモートス,ヴォルデモートス,7/7,* wurtzel,1348,1348,504,名詞,固有名詞,一般,*,*,*,ワーツェル,ワーツェル,ワーツェル,0/4,* yomiuri,1348,1348,504,名詞,固有名詞,一般,*,*,*,ヨミウリ,ヨミウリ,ヨミウリ,0/4,* adl-tabataba,1348,1348,762,名詞,固有名詞,一般,*,*,*,アドルタバタバ,アドルタバタバ,アドルタバタバ,0/7,* adl-tabatabai,1348,1348,813,名詞,固有名詞,一般,*,*,*,アドルタバタバイ,アドルタバタバイ,アドルタバタバイ,0/8,* -aix-la-chapelle,1348,1348,141,名詞,固有名詞,一般,*,*,*,エクスラシャペル,エクスラシャペル,エクスラシャペル,7/7,* -anglo-catholicism,1348,1348,644,名詞,固有名詞,一般,*,*,*,アングロカトリシズム,アングロカトリシズム,アングロカトリシズム,9/10,* -bar-mitzvah,1348,1348,326,名詞,固有名詞,一般,*,*,*,バーミツバー,バーミツバー,バーミツバー,0/6,* -bas-reliefs,1348,1348,631,名詞,固有名詞,一般,*,*,*,バスリリーフス,バスリリーフス,バスリリーフス,5/7,* -broken-tooth,1348,1348,384,名詞,固有名詞,一般,*,*,*,ブローケントゥース,ブローケントゥース,ブローケントゥース,7/8,* -brothers-in-law,1348,1348,141,名詞,固有名詞,一般,*,*,*,ブラザーズインロウ,ブラザーズインロウ,ブラザーズインロウ,0/9,* -carpe-diem,1348,1348,588,名詞,固有名詞,一般,*,*,*,カーペディエム,カーペディエム,カーペディエム,5/6,* -champaign-urbana,1348,1348,594,名詞,固有名詞,一般,*,*,*,シャンペーンアーバナ,シャンペーンアーバナ,シャンペーンアーバナ,7/9,* +aix-la-chapelle,1348,1348,914,名詞,固有名詞,一般,*,*,*,エクスラシャペル,エクスラシャペル,エクスラシャペル,7/7,* +anglo-catholicism,1348,1348,1016,名詞,固有名詞,一般,*,*,*,アングロカトリシズム,アングロカトリシズム,アングロカトリシズム,9/10,* +bar-mitzvah,1348,1348,711,名詞,固有名詞,一般,*,*,*,バーミツバー,バーミツバー,バーミツバー,0/6,* +bas-reliefs,1348,1348,711,名詞,固有名詞,一般,*,*,*,バスリリーフス,バスリリーフス,バスリリーフス,5/7,* +broken-tooth,1348,1348,762,名詞,固有名詞,一般,*,*,*,ブローケントゥース,ブローケントゥース,ブローケントゥース,7/8,* +brothers-in-law,1348,1348,914,名詞,固有名詞,一般,*,*,*,ブラザーズインロウ,ブラザーズインロウ,ブラザーズインロウ,0/9,* +carpe-diem,1348,1348,659,名詞,固有名詞,一般,*,*,*,カーペディエム,カーペディエム,カーペディエム,5/6,* +champaign-urbana,1348,1348,965,名詞,固有名詞,一般,*,*,*,シャンペーンアーバナ,シャンペーンアーバナ,シャンペーンアーバナ,7/9,* churchhouse,1348,1348,711,名詞,固有名詞,一般,*,*,*,チャーチハウス,チャーチハウス,チャーチハウス,5/6,* -coca-cola,1348,1348,223,名詞,固有名詞,一般,*,*,*,コカコーラ,コカコーラ,コカコーラ,4/5,* -computer-generated,1348,1348,699,名詞,固有名詞,一般,*,*,*,コンピュータージェネレーテッド,コンピュータージェネレーテッド,コンピュータージェネレーテッド,10/13,* -daughters-in-law,1348,1348,193,名詞,固有名詞,一般,*,*,*,ドーターズインロー,ドーターズインロー,ドーターズインロー,0/9,* -diaz-calderon,1348,1348,-1024,名詞,固有名詞,一般,*,*,*,ディアスカルデロン,ディアスカルデロン,ディアスカルデロン,0/8,* -diego-garcia,1348,1348,384,名詞,固有名詞,一般,*,*,*,ディエゴガルシア,ディエゴガルシア,ディエゴガルシア,5/7,* -dumb-ass,1348,1348,166,名詞,固有名詞,一般,*,*,*,ダムアス,ダムアス,ダムアス,3/4,* -el-greco,1348,1348,160,名詞,固有名詞,一般,*,*,*,エルグレコ,エルグレコ,エルグレコ,4/5,* -ex-boyfriends,1348,1348,421,名詞,固有名詞,一般,*,*,*,エクスボーイフレンズ,エクスボーイフレンズ,エクスボーイフレンズ,9/10,* -ex-girlfriends,1348,1348,473,名詞,固有名詞,一般,*,*,*,エックスガールフレンズ,エックスガールフレンズ,エックスガールフレンズ,0/11,* -face-to-face,1348,1348,-14,名詞,固有名詞,一般,*,*,*,フェイストゥフェイス,フェイストゥフェイス,フェイストゥフェイス,7/7,* -foot-and-mouth,1348,1348,100,名詞,固有名詞,一般,*,*,*,フートアンドマウス,フートアンドマウス,フートアンドマウス,8/9,* -french-polynesia,1348,1348,594,名詞,固有名詞,一般,*,*,*,フランスポリネシア,フランスポリネシア,フランスポリネシア,8/9,* -goings-ons,1348,1348,273,名詞,固有名詞,一般,*,*,*,ゴーイングスオンズ,ゴーイングスオンズ,ゴーイングスオンズ,7/9,* -great-grandfather,1348,1348,644,名詞,固有名詞,一般,*,*,*,グレイトグランドファーザー,グレイトグランドファーザー,グレイトグランドファーザー,10/12,* -hit-and-run,1348,1348,-68,名詞,固有名詞,一般,*,*,*,ヒットアンドラン,ヒットアンドラン,ヒットアンドラン,8/8,* -homo-erectus,1348,1348,711,名詞,固有名詞,一般,*,*,*,ホモエレクトゥス,ホモエレクトゥス,ホモエレクトゥス,4/7,* +coca-cola,1348,1348,608,名詞,固有名詞,一般,*,*,*,コカコーラ,コカコーラ,コカコーラ,4/5,* +computer-generated,1348,1348,1066,名詞,固有名詞,一般,*,*,*,コンピュータージェネレーテッド,コンピュータージェネレーテッド,コンピュータージェネレーテッド,10/13,* +daughters-in-law,1348,1348,965,名詞,固有名詞,一般,*,*,*,ドーターズインロー,ドーターズインロー,ドーターズインロー,0/9,* +diaz-calderon,1348,1348,813,名詞,固有名詞,一般,*,*,*,ディアスカルデロン,ディアスカルデロン,ディアスカルデロン,0/8,* +diego-garcia,1348,1348,762,名詞,固有名詞,一般,*,*,*,ディエゴガルシア,ディエゴガルシア,ディエゴガルシア,5/7,* +dumb-ass,1348,1348,556,名詞,固有名詞,一般,*,*,*,ダムアス,ダムアス,ダムアス,3/4,* +el-greco,1348,1348,556,名詞,固有名詞,一般,*,*,*,エルグレコ,エルグレコ,エルグレコ,4/5,* +ex-boyfriends,1348,1348,813,名詞,固有名詞,一般,*,*,*,エクスボーイフレンズ,エクスボーイフレンズ,エクスボーイフレンズ,9/10,* +ex-girlfriends,1348,1348,863,名詞,固有名詞,一般,*,*,*,エックスガールフレンズ,エックスガールフレンズ,エックスガールフレンズ,0/11,* +face-to-face,1348,1348,762,名詞,固有名詞,一般,*,*,*,フェイストゥフェイス,フェイストゥフェイス,フェイストゥフェイス,7/7,* +foot-and-mouth,1348,1348,863,名詞,固有名詞,一般,*,*,*,フートアンドマウス,フートアンドマウス,フートアンドマウス,8/9,* +french-polynesia,1348,1348,965,名詞,固有名詞,一般,*,*,*,フランスポリネシア,フランスポリネシア,フランスポリネシア,8/9,* +goings-ons,1348,1348,659,名詞,固有名詞,一般,*,*,*,ゴーイングスオンズ,ゴーイングスオンズ,ゴーイングスオンズ,7/9,* +great-grandfather,1348,1348,1016,名詞,固有名詞,一般,*,*,*,グレイトグランドファーザー,グレイトグランドファーザー,グレイトグランドファーザー,10/12,* +hit-and-run,1348,1348,711,名詞,固有名詞,一般,*,*,*,ヒットアンドラン,ヒットアンドラン,ヒットアンドラン,8/8,* +homo-erectus,1348,1348,762,名詞,固有名詞,一般,*,*,*,ホモエレクトゥス,ホモエレクトゥス,ホモエレクトゥス,4/7,* ieee,1348,1348,344,名詞,固有名詞,一般,*,*,*,アイトリプルイー,アイトリプルイー,アイトリプルイー,1/8,* -jean-luc,1348,1348,166,名詞,固有名詞,一般,*,*,*,ジャンリュック,ジャンリュック,ジャンリュック,4/5,* -jean-michel,1348,1348,330,名詞,固有名詞,一般,*,*,*,ジャンミシェル,ジャンミシェル,ジャンミシェル,4/5,* -jean-michele,1348,1348,383,名詞,固有名詞,一般,*,*,*,ジャンミッシェル,ジャンミッシェル,ジャンミッシェル,4/6,* -juarez-espin,1348,1348,695,名詞,固有名詞,一般,*,*,*,フアレスエスピン,フアレスエスピン,フアレスエスピン,6/8,* -kebab-n-curry,1348,1348,25,名詞,固有名詞,一般,*,*,*,ケバブンカレー,ケバブンカレー,ケバブンカレー,0/7,* -life-affirming,1348,1348,487,名詞,固有名詞,一般,*,*,*,ライフアファーミング,ライフアファーミング,ライフアファーミング,0/9,* -might-have-beens,1348,1348,211,名詞,固有名詞,一般,*,*,*,マイトハブビーンズ,マイトハブビーンズ,マイトハブビーンズ,8/9,* -modus-operandi,1348,1348,489,名詞,固有名詞,一般,*,*,*,モーダスオペランディ,モーダスオペランディ,モーダスオペランディ,8/9,* -moholy-nagy,1348,1348,657,名詞,固有名詞,一般,*,*,*,モホリナギ,モホリナギ,モホリナギ,4/5,* -moseley-braun,1348,1348,437,名詞,固有名詞,一般,*,*,*,モーズリーブラウン,モーズリーブラウン,モーズリーブラウン,8/9,* -non-communists,1348,1348,-226,名詞,固有名詞,一般,*,*,*,ノンコミュニスツ,ノンコミュニスツ,ノンコミュニスツ,0/7,* -non-discrimination,1348,1348,-488,名詞,固有名詞,一般,*,*,*,ノンディスクリミネーション,ノンディスクリミネーション,ノンディスクリミネーション,9/11,* -non-governmental,1348,1348,-123,名詞,固有名詞,一般,*,*,*,ノンガバメントゥル,ノンガバメントゥル,ノンガバメントゥル,6/8,* -non-transferable,1348,1348,-123,名詞,固有名詞,一般,*,*,*,ノントランスファラブル,ノントランスファラブル,ノントランスファラブル,8/10,* -o'canas,1348,1348,-1361,名詞,固有名詞,一般,*,*,*,オカナス,オカナス,オカナス,3/4,* -off-duty,1348,1348,166,名詞,固有名詞,一般,*,*,*,オフデューティ,オフデューティ,オフデューティ,4/5,* -one-on-one,1348,1348,-290,名詞,固有名詞,一般,*,*,*,ワンオンワン,ワンオンワン,ワンオンワン,0/6,* -one-year,1348,1348,163,名詞,固有名詞,一般,*,*,*,ワンイヤー,ワンイヤー,ワンイヤー,3/5,* -pee-pee,1348,1348,109,名詞,固有名詞,一般,*,*,*,ピーピー,ピーピー,ピーピー,1/4,* +jean-luc,1348,1348,556,名詞,固有名詞,一般,*,*,*,ジャンリュック,ジャンリュック,ジャンリュック,4/5,* +jean-michel,1348,1348,711,名詞,固有名詞,一般,*,*,*,ジャンミシェル,ジャンミシェル,ジャンミシェル,4/5,* +jean-michele,1348,1348,762,名詞,固有名詞,一般,*,*,*,ジャンミッシェル,ジャンミッシェル,ジャンミッシェル,4/6,* +juarez-espin,1348,1348,762,名詞,固有名詞,一般,*,*,*,フアレスエスピン,フアレスエスピン,フアレスエスピン,6/8,* +kebab-n-curry,1348,1348,813,名詞,固有名詞,一般,*,*,*,ケバブンカレー,ケバブンカレー,ケバブンカレー,0/7,* +life-affirming,1348,1348,863,名詞,固有名詞,一般,*,*,*,ライフアファーミング,ライフアファーミング,ライフアファーミング,0/9,* +might-have-beens,1348,1348,965,名詞,固有名詞,一般,*,*,*,マイトハブビーンズ,マイトハブビーンズ,マイトハブビーンズ,8/9,* +modus-operandi,1348,1348,863,名詞,固有名詞,一般,*,*,*,モーダスオペランディ,モーダスオペランディ,モーダスオペランディ,8/9,* +moholy-nagy,1348,1348,711,名詞,固有名詞,一般,*,*,*,モホリナギ,モホリナギ,モホリナギ,4/5,* +moseley-braun,1348,1348,813,名詞,固有名詞,一般,*,*,*,モーズリーブラウン,モーズリーブラウン,モーズリーブラウン,8/9,* +non-communists,1348,1348,863,名詞,固有名詞,一般,*,*,*,ノンコミュニスツ,ノンコミュニスツ,ノンコミュニスツ,0/7,* +non-discrimination,1348,1348,1066,名詞,固有名詞,一般,*,*,*,ノンディスクリミネーション,ノンディスクリミネーション,ノンディスクリミネーション,9/11,* +non-governmental,1348,1348,965,名詞,固有名詞,一般,*,*,*,ノンガバメントゥル,ノンガバメントゥル,ノンガバメントゥル,6/8,* +non-transferable,1348,1348,965,名詞,固有名詞,一般,*,*,*,ノントランスファラブル,ノントランスファラブル,ノントランスファラブル,8/10,* +o'canas,1348,1348,504,名詞,固有名詞,一般,*,*,*,オカナス,オカナス,オカナス,3/4,* +off-duty,1348,1348,556,名詞,固有名詞,一般,*,*,*,オフデューティ,オフデューティ,オフデューティ,4/5,* +one-on-one,1348,1348,659,名詞,固有名詞,一般,*,*,*,ワンオンワン,ワンオンワン,ワンオンワン,0/6,* +one-year,1348,1348,556,名詞,固有名詞,一般,*,*,*,ワンイヤー,ワンイヤー,ワンイヤー,3/5,* +pee-pee,1348,1348,504,名詞,固有名詞,一般,*,*,*,ピーピー,ピーピー,ピーピー,1/4,* peeping,1348,1348,504,名詞,固有名詞,一般,*,*,*,ピーピング,ピーピング,ピーピング,0/5,* -peer-to-peer,1348,1348,-14,名詞,固有名詞,一般,*,*,*,ピアトゥピア,ピアトゥピア,ピアトゥピア,5/5,* -rock-and-roll,1348,1348,46,名詞,固有名詞,一般,*,*,*,ロックアンドロール,ロックアンドロール,ロックアンドロール,9/9,* +peer-to-peer,1348,1348,762,名詞,固有名詞,一般,*,*,*,ピアトゥピア,ピアトゥピア,ピアトゥピア,5/5,* +rock-and-roll,1348,1348,813,名詞,固有名詞,一般,*,*,*,ロックアンドロール,ロックアンドロール,ロックアンドロール,9/9,* rpm,1348,1348,287,名詞,固有名詞,一般,*,*,*,アールピーエム,アールピーエム,アールピーエム,7/7,* saint-saens,1348,1348,711,名詞,固有名詞,一般,*,*,*,サンサーンズ,サンサーンズ,サンサーンズ,4/6,* -science-fiction,1348,1348,-289,名詞,固有名詞,一般,*,*,*,サイエンスフィクション,サイエンスフィクション,サイエンスフィクション,7/9,* -serbo-croatian,1348,1348,800,名詞,固有名詞,一般,*,*,*,セルボクロアチアン,セルボクロアチアン,セルボクロアチアン,7/9,* -sheath-knife,1348,1348,384,名詞,固有名詞,一般,*,*,*,シースナイフ,シースナイフ,シースナイフ,5/6,* -sisters-in-law,1348,1348,89,名詞,固有名詞,一般,*,*,*,シスターズインロウ,シスターズインロウ,シスターズインロウ,7/9,* -sons-in-law,1348,1348,-205,名詞,固有名詞,一般,*,*,*,サンズインロー,サンズインロー,サンズインロー,0/7,* -soviet-union,1348,1348,-1081,名詞,固有名詞,一般,*,*,*,ソビエトユニオン,ソビエトユニオン,ソビエトユニオン,6/8,* +science-fiction,1348,1348,914,名詞,固有名詞,一般,*,*,*,サイエンスフィクション,サイエンスフィクション,サイエンスフィクション,7/9,* +serbo-croatian,1348,1348,863,名詞,固有名詞,一般,*,*,*,セルボクロアチアン,セルボクロアチアン,セルボクロアチアン,7/9,* +sheath-knife,1348,1348,762,名詞,固有名詞,一般,*,*,*,シースナイフ,シースナイフ,シースナイフ,5/6,* +sisters-in-law,1348,1348,863,名詞,固有名詞,一般,*,*,*,シスターズインロウ,シスターズインロウ,シスターズインロウ,7/9,* +sons-in-law,1348,1348,711,名詞,固有名詞,一般,*,*,*,サンズインロー,サンズインロー,サンズインロー,0/7,* +soviet-union,1348,1348,762,名詞,固有名詞,一般,*,*,*,ソビエトユニオン,ソビエトユニオン,ソビエトユニオン,6/8,* speaking,1348,1348,556,名詞,固有名詞,一般,*,*,*,スピーキング,スピーキング,スピーキング,0/6,* -twenty-three,1348,1348,384,名詞,固有名詞,一般,*,*,*,トゥエンティースリー,トゥエンティースリー,トゥエンティースリー,6/8,* -two-day,1348,1348,109,名詞,固有名詞,一般,*,*,*,ツーデイ,ツーデイ,ツーデイ,0/4,* -two-dimensional,1348,1348,533,名詞,固有名詞,一般,*,*,*,ツーディメンショナル,ツーディメンショナル,ツーディメンショナル,5/8,* -two-thirds,1348,1348,273,名詞,固有名詞,一般,*,*,*,トゥーサーズ,トゥーサーズ,トゥーサーズ,1/5,* +twenty-three,1348,1348,762,名詞,固有名詞,一般,*,*,*,トゥエンティースリー,トゥエンティースリー,トゥエンティースリー,6/8,* +two-day,1348,1348,504,名詞,固有名詞,一般,*,*,*,ツーデイ,ツーデイ,ツーデイ,0/4,* +two-dimensional,1348,1348,914,名詞,固有名詞,一般,*,*,*,ツーディメンショナル,ツーディメンショナル,ツーディメンショナル,5/8,* +two-thirds,1348,1348,659,名詞,固有名詞,一般,*,*,*,トゥーサーズ,トゥーサーズ,トゥーサーズ,1/5,* untruths,1348,1348,556,名詞,固有名詞,一般,*,*,*,アントルーズ,アントルーズ,アントルーズ,5/6,* -whiz-zip-bang,1348,1348,46,名詞,固有名詞,一般,*,*,*,ウィズジップバン,ウィズジップバン,ウィズジップバン,7/7,* +whiz-zip-bang,1348,1348,813,名詞,固有名詞,一般,*,*,*,ウィズジップバン,ウィズジップバン,ウィズジップバン,7/7,* let's,1348,1348,398,名詞,固有名詞,一般,*,*,*,レッツ,レッツ,レッツ,1/3,* diff --git a/tools/data/japanese_dictionary_source.json b/tools/data/japanese_dictionary_source.json index 7b29eccd..ba85509f 100644 --- a/tools/data/japanese_dictionary_source.json +++ b/tools/data/japanese_dictionary_source.json @@ -1,5 +1,5 @@ { - "version": "2026.10.20.1", + "version": "2026.10.20.2", "language": "ja", "source": { "repository": "https://github.com/RVC-Boss/GPT-SoVITS", @@ -94,17 +94,12 @@ "word": "component", "before": "コンポーネンツ", "after": "コンポーネント", - "cost_before": 608, - "cost_after": 0, - "reason": "Singular T and plural TS are distinct in CMUdict. Native full-pack validation also exposes comp + on + ent splitting at cost 608; cost 0 keeps the corrected whole word." - }, - { - "word": "components", - "before": "コンポーネンツ", - "after": "コンポーネンツ", - "cost_before": 659, - "cost_after": 0, - "reason": "Keep the existing plural reading selected after repairing the singular entry cost; native validation must preserve both whole-word readings." + "reason": "Singular T and plural TS are distinct in CMUdict. Preserve the source cost; whole-token boundaries prevent comp + on + ent splitting.", + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": 0, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "developer", @@ -171,7 +166,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -208,8 +203,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "amd", @@ -371,7 +369,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -408,8 +406,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "balloonist", @@ -485,7 +486,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -522,8 +523,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "busloads", @@ -705,7 +709,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -742,8 +746,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "circuit's", @@ -784,7 +791,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -821,8 +828,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "condo", @@ -850,7 +860,7 @@ "accent": "1/3" }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -887,8 +897,11 @@ ] } }, - "cost_before": 398, - "cost_after": 278 + "retired_cost_calibration": { + "source_cost": 398, + "previous_derivative_cost": 278, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "corps", @@ -966,7 +979,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1003,8 +1016,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "cricket's", @@ -1047,7 +1063,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1084,8 +1100,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "criminalist's", @@ -1138,7 +1157,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1175,8 +1194,11 @@ ] } }, - "cost_before": 813, - "cost_after": -1048 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": -1048, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "debarment", @@ -1243,7 +1265,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1280,8 +1302,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "dentist", @@ -1558,7 +1583,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1595,8 +1620,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "economist's", @@ -1712,7 +1740,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1749,8 +1777,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "economists'", @@ -1840,7 +1871,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1865,8 +1896,11 @@ ] } }, - "cost_before": 711, - "cost_after": -2221 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -2221, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "embassy's", @@ -1890,7 +1924,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -1927,8 +1961,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "endocrinologist's", @@ -1989,7 +2026,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2026,8 +2063,11 @@ ] } }, - "cost_before": 1016, - "cost_after": -845 + "retired_cost_calibration": { + "source_cost": 1016, + "previous_derivative_cost": -845, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "ernest's", @@ -2068,7 +2108,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2105,8 +2145,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "everest's", @@ -2142,7 +2185,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2179,8 +2222,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "experienced", @@ -2232,7 +2278,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2269,8 +2315,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "fluoresce", @@ -2341,7 +2390,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2378,8 +2427,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "grant's", @@ -2420,7 +2472,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2457,8 +2509,11 @@ ] } }, - "cost_before": 504, - "cost_after": -1361 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": -1361, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "hermetically", @@ -2633,7 +2688,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2670,8 +2725,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "hoechst's", @@ -2732,7 +2790,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2769,8 +2827,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "hospice's", @@ -2795,7 +2856,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2832,8 +2893,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "illusionist's", @@ -2882,7 +2946,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -2919,8 +2983,11 @@ ] } }, - "cost_before": 813, - "cost_after": -1048 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": -1048, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "intruder", @@ -3038,7 +3105,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3075,8 +3142,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "jfet", @@ -3158,7 +3228,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3195,8 +3265,11 @@ ] } }, - "cost_before": 762, - "cost_after": -1100 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -1100, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "lawrence", @@ -3272,7 +3345,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3309,8 +3382,11 @@ ] } }, - "cost_before": 762, - "cost_after": -1100 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -1100, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "luxottica's", @@ -3389,7 +3465,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3426,8 +3502,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "marlboro's", @@ -3474,7 +3553,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3511,8 +3590,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "marriott's", @@ -3536,7 +3618,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3573,8 +3655,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "matsushita's", @@ -3616,7 +3701,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3653,8 +3738,11 @@ ] } }, - "cost_before": 762, - "cost_after": -1100 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -1100, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "medalist's", @@ -3701,7 +3789,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3738,8 +3826,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "meteorologist", @@ -3823,7 +3914,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3860,8 +3951,11 @@ ] } }, - "cost_before": 914, - "cost_after": -946 + "retired_cost_calibration": { + "source_cost": 914, + "previous_derivative_cost": -946, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "mississippi's", @@ -3908,7 +4002,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -3945,8 +4039,11 @@ ] } }, - "cost_before": 813, - "cost_after": -1048 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": -1048, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "mormonism", @@ -4134,7 +4231,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4171,8 +4268,11 @@ ] } }, - "cost_before": 451, - "cost_after": -1415 + "retired_cost_calibration": { + "source_cost": 451, + "previous_derivative_cost": -1415, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "ninth", @@ -4216,7 +4316,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4253,8 +4353,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "office's", @@ -4277,7 +4380,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4314,8 +4417,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "personalizes", @@ -4392,7 +4498,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4429,8 +4535,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "politz", @@ -4475,7 +4584,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4512,8 +4621,11 @@ ] } }, - "cost_before": 504, - "cost_after": -1361 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": -1361, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "post's", @@ -4552,7 +4664,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4589,8 +4701,11 @@ ] } }, - "cost_before": 451, - "cost_after": -1415 + "retired_cost_calibration": { + "source_cost": 451, + "previous_derivative_cost": -1415, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "purchaser", @@ -4650,7 +4765,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4687,8 +4802,11 @@ ] } }, - "cost_before": 451, - "cost_after": -1415 + "retired_cost_calibration": { + "source_cost": 451, + "previous_derivative_cost": -1415, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "sarajevo's", @@ -4714,7 +4832,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4751,8 +4869,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "schubert's", @@ -4775,7 +4896,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4812,8 +4933,11 @@ ] } }, - "cost_before": 659, - "cost_after": -1203 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -1203, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "scientist's", @@ -4896,7 +5020,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -4933,8 +5057,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "scout's", @@ -4973,7 +5100,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5010,8 +5137,11 @@ ] } }, - "cost_before": 504, - "cost_after": -1361 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": -1361, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "shevardnadze's", @@ -5039,7 +5169,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5076,8 +5206,11 @@ ] } }, - "cost_before": 863, - "cost_after": -997 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": -997, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "sizes", @@ -5148,7 +5281,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5185,8 +5318,11 @@ ] } }, - "cost_before": 504, - "cost_after": -1361 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": -1361, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "soviet's", @@ -5229,7 +5365,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5266,8 +5402,11 @@ ] } }, - "cost_before": 556, - "cost_after": -1308 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": -1308, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "stowaway", @@ -5366,7 +5505,7 @@ ] }, "accent_policy": "reuse documented existing/native nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5403,8 +5542,11 @@ ] } }, - "cost_before": 965, - "cost_after": -896 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": -896, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "suvs", @@ -5474,7 +5616,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5511,8 +5653,11 @@ ] } }, - "cost_before": 608, - "cost_after": -1255 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": -1255, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "t-shirt", @@ -5534,7 +5679,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5571,8 +5716,11 @@ ] } }, - "cost_before": 504, - "cost_after": 91 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": 91, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "t-shirts", @@ -5595,7 +5743,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5632,8 +5780,11 @@ ] } }, - "cost_before": 556, - "cost_after": 144 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 144, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "thrusts", @@ -5757,7 +5908,7 @@ } ], "accent_policy": "preserve existing valid nucleus", - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5794,8 +5945,11 @@ ] } }, - "cost_before": 711, - "cost_after": -1151 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -1151, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "wurtzel", @@ -5947,7 +6101,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -5990,8 +6144,11 @@ ] } }, - "cost_before": 914, - "cost_after": 141 + "retired_cost_calibration": { + "source_cost": 914, + "previous_derivative_cost": 141, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "anglo-catholicism", @@ -6024,7 +6181,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6061,8 +6218,11 @@ ] } }, - "cost_before": 1016, - "cost_after": 644 + "retired_cost_calibration": { + "source_cost": 1016, + "previous_derivative_cost": 644, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "bar-mitzvah", @@ -6088,7 +6248,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6125,8 +6285,11 @@ ] } }, - "cost_before": 711, - "cost_after": 326 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": 326, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "bas-reliefs", @@ -6165,7 +6328,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6205,8 +6368,11 @@ ] } }, - "cost_before": 711, - "cost_after": 631 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": 631, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "broken-tooth", @@ -6232,7 +6398,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6269,8 +6435,11 @@ ] } }, - "cost_before": 762, - "cost_after": 384 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 384, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "brothers-in-law", @@ -6297,7 +6466,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6340,8 +6509,11 @@ ] } }, - "cost_before": 914, - "cost_after": 141 + "retired_cost_calibration": { + "source_cost": 914, + "previous_derivative_cost": 141, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "carpe-diem", @@ -6367,7 +6539,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6407,8 +6579,11 @@ ] } }, - "cost_before": 659, - "cost_after": 588 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": 588, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "champaign-urbana", @@ -6436,7 +6611,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6473,8 +6648,11 @@ ] } }, - "cost_before": 965, - "cost_after": 594 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": 594, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "churchhouse", @@ -6522,7 +6700,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6559,8 +6737,11 @@ ] } }, - "cost_before": 608, - "cost_after": 223 + "retired_cost_calibration": { + "source_cost": 608, + "previous_derivative_cost": 223, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "computer-generated", @@ -6593,7 +6774,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6630,8 +6811,11 @@ ] } }, - "cost_before": 1066, - "cost_after": 699 + "retired_cost_calibration": { + "source_cost": 1066, + "previous_derivative_cost": 699, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "daughters-in-law", @@ -6657,7 +6841,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6700,8 +6884,11 @@ ] } }, - "cost_before": 965, - "cost_after": 193 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": 193, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "diaz-calderon", @@ -6729,7 +6916,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6768,8 +6955,11 @@ ] } }, - "cost_before": 813, - "cost_after": -1024 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": -1024, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "diego-garcia", @@ -6797,7 +6987,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6834,8 +7024,11 @@ ] } }, - "cost_before": 762, - "cost_after": 384 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 384, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "dumb-ass", @@ -6858,7 +7051,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6895,8 +7088,11 @@ ] } }, - "cost_before": 556, - "cost_after": 166 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 166, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "el-greco", @@ -6920,7 +7116,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -6957,8 +7153,11 @@ ] } }, - "cost_before": 556, - "cost_after": 160 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 160, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "ex-boyfriends", @@ -6986,7 +7185,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7023,8 +7222,11 @@ ] } }, - "cost_before": 813, - "cost_after": 421 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": 421, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "ex-girlfriends", @@ -7053,7 +7255,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7090,8 +7292,11 @@ ] } }, - "cost_before": 863, - "cost_after": 473 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 473, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "face-to-face", @@ -7116,7 +7321,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7159,8 +7364,11 @@ ] } }, - "cost_before": 762, - "cost_after": -14 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -14, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "foot-and-mouth", @@ -7186,7 +7394,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7229,8 +7437,11 @@ ] } }, - "cost_before": 863, - "cost_after": 100 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 100, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "french-polynesia", @@ -7260,7 +7471,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7297,8 +7508,11 @@ ] } }, - "cost_before": 965, - "cost_after": 594 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": 594, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "goings-ons", @@ -7323,7 +7537,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7360,8 +7574,11 @@ ] } }, - "cost_before": 659, - "cost_after": 273 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": 273, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "great-grandfather", @@ -7391,7 +7608,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7428,8 +7645,11 @@ ] } }, - "cost_before": 1016, - "cost_after": 644 + "retired_cost_calibration": { + "source_cost": 1016, + "previous_derivative_cost": 644, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "hit-and-run", @@ -7454,7 +7674,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7497,8 +7717,11 @@ ] } }, - "cost_before": 711, - "cost_after": -68 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -68, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "homo-erectus", @@ -7526,7 +7749,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7566,8 +7789,11 @@ ] } }, - "cost_before": 762, - "cost_after": 711 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 711, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "ieee", @@ -7615,7 +7841,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7652,8 +7878,11 @@ ] } }, - "cost_before": 556, - "cost_after": 166 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 166, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "jean-michel", @@ -7678,7 +7907,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7715,8 +7944,11 @@ ] } }, - "cost_before": 711, - "cost_after": 330 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": 330, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "jean-michele", @@ -7741,7 +7973,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7778,8 +8010,11 @@ ] } }, - "cost_before": 762, - "cost_after": 383 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 383, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "juarez-espin", @@ -7822,7 +8057,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7862,8 +8097,11 @@ ] } }, - "cost_before": 762, - "cost_after": 695 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 695, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "kebab-n-curry", @@ -7890,7 +8128,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7933,8 +8171,11 @@ ] } }, - "cost_before": 813, - "cost_after": 25 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": 25, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "life-affirming", @@ -7960,7 +8201,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -7997,8 +8238,11 @@ ] } }, - "cost_before": 863, - "cost_after": 487 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 487, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "might-have-beens", @@ -8024,7 +8268,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8067,8 +8311,11 @@ ] } }, - "cost_before": 965, - "cost_after": 211 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": 211, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "modus-operandi", @@ -8096,7 +8343,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8133,8 +8380,11 @@ ] } }, - "cost_before": 863, - "cost_after": 489 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 489, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "moholy-nagy", @@ -8161,7 +8411,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8201,8 +8451,11 @@ ] } }, - "cost_before": 711, - "cost_after": 657 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": 657, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "moseley-braun", @@ -8228,7 +8481,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8265,8 +8518,11 @@ ] } }, - "cost_before": 813, - "cost_after": 437 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": 437, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "non-communists", @@ -8296,7 +8552,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8336,8 +8592,11 @@ ] } }, - "cost_before": 863, - "cost_after": -226 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": -226, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "non-discrimination", @@ -8370,7 +8629,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8418,8 +8677,11 @@ ] } }, - "cost_before": 1066, - "cost_after": -488 + "retired_cost_calibration": { + "source_cost": 1066, + "previous_derivative_cost": -488, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "non-governmental", @@ -8450,7 +8712,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8490,8 +8752,11 @@ ] } }, - "cost_before": 965, - "cost_after": -123 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": -123, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "non-transferable", @@ -8522,7 +8787,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8562,8 +8827,11 @@ ] } }, - "cost_before": 965, - "cost_after": -123 + "retired_cost_calibration": { + "source_cost": 965, + "previous_derivative_cost": -123, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "o'canas", @@ -8586,7 +8854,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8623,8 +8891,11 @@ ] } }, - "cost_before": 504, - "cost_after": -1361 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": -1361, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "off-duty", @@ -8647,7 +8918,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8684,8 +8955,11 @@ ] } }, - "cost_before": 556, - "cost_after": 166 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 166, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "one-on-one", @@ -8709,7 +8983,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8753,8 +9027,11 @@ ] } }, - "cost_before": 659, - "cost_after": -290 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": -290, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "one-year", @@ -8777,7 +9054,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8815,8 +9092,11 @@ ] } }, - "cost_before": 556, - "cost_after": 163 + "retired_cost_calibration": { + "source_cost": 556, + "previous_derivative_cost": 163, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "pee-pee", @@ -8837,7 +9117,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8874,8 +9154,11 @@ ] } }, - "cost_before": 504, - "cost_after": 109 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": 109, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "peeping", @@ -8922,7 +9205,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -8965,8 +9248,11 @@ ] } }, - "cost_before": 762, - "cost_after": -14 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -14, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "rock-and-roll", @@ -8992,7 +9278,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9035,8 +9321,11 @@ ] } }, - "cost_before": 813, - "cost_after": 46 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": 46, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "rpm", @@ -9112,7 +9401,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9155,8 +9444,11 @@ ] } }, - "cost_before": 914, - "cost_after": -289 + "retired_cost_calibration": { + "source_cost": 914, + "previous_derivative_cost": -289, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "serbo-croatian", @@ -9184,7 +9476,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9224,8 +9516,11 @@ ] } }, - "cost_before": 863, - "cost_after": 800 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 800, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "sheath-knife", @@ -9248,7 +9543,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9285,8 +9580,11 @@ ] } }, - "cost_before": 762, - "cost_after": 384 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 384, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "sisters-in-law", @@ -9313,7 +9611,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9356,8 +9654,11 @@ ] } }, - "cost_before": 863, - "cost_after": 89 + "retired_cost_calibration": { + "source_cost": 863, + "previous_derivative_cost": 89, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "sons-in-law", @@ -9381,7 +9682,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9430,8 +9731,11 @@ ] } }, - "cost_before": 711, - "cost_after": -205 + "retired_cost_calibration": { + "source_cost": 711, + "previous_derivative_cost": -205, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "soviet-union", @@ -9460,7 +9764,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9499,8 +9803,11 @@ ] } }, - "cost_before": 762, - "cost_after": -1081 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": -1081, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "speaking", @@ -9549,7 +9856,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9586,8 +9893,11 @@ ] } }, - "cost_before": 762, - "cost_after": 384 + "retired_cost_calibration": { + "source_cost": 762, + "previous_derivative_cost": 384, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "two-day", @@ -9608,7 +9918,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9645,8 +9955,11 @@ ] } }, - "cost_before": 504, - "cost_after": 109 + "retired_cost_calibration": { + "source_cost": 504, + "previous_derivative_cost": 109, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "two-dimensional", @@ -9675,7 +9988,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9712,8 +10025,11 @@ ] } }, - "cost_before": 914, - "cost_after": 533 + "retired_cost_calibration": { + "source_cost": 914, + "previous_derivative_cost": 533, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "two-thirds", @@ -9736,7 +10052,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9773,8 +10089,11 @@ ] } }, - "cost_before": 659, - "cost_after": 273 + "retired_cost_calibration": { + "source_cost": 659, + "previous_derivative_cost": 273, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "untruths", @@ -9836,7 +10155,7 @@ ] } ], - "native_cost_calibration": { + "retired_native_cost_calibration": { "runtime": "0.4.1.post9", "rule": "Minimal cost reduction to beat observed unintended splits in three synthetic contexts; no whole-word reading alternative is overridden.", "observations": [ @@ -9879,8 +10198,11 @@ ] } }, - "cost_before": 813, - "cost_after": 46 + "retired_cost_calibration": { + "source_cost": 813, + "previous_derivative_cost": 46, + "status": "superseded_by_whole_token_boundaries; source cost restored" + } }, { "word": "let's", @@ -9908,11 +10230,9 @@ "baseline_sha256": "91b3a3c1aa37b7eea96b5815b40e48c0f6a15d02136f956d9deb340df35131f2", "baseline_review_entries": 317, "policy": "Exact bad-reading repairs preserve other lexical alternatives. Unconfirmed lexical transcriptions or accent nuclei are retained. Native compilation and labels do not replace human listening.", - "cost_calibration_completed": true, "lexical_repairs": 94, "representation_repairs": 65, "retained_entries": 158, - "calibrated_entry_costs": 109, "evidence_files": { "cmudict": { "path": "GPT_SoVITS/text/cmudict-fast.rep", @@ -9926,6 +10246,10 @@ "char.bin": "888ee94c5a8a7a26d24ab3f1b7155441351954fd51ea06b4a2f78bd742492b2f" } } + }, + "retired_cost_calibration": { + "entries": 109, + "status": "Historical selection evidence only; whole-token boundaries supersede every cost override. All source row costs are preserved." } }, "extension_filter": { @@ -9937,12 +10261,11 @@ "char.bin": "888ee94c5a8a7a26d24ab3f1b7155441351954fd51ea06b4a2f78bd742492b2f" }, "rules": [ - "Exclude every imported row for a surface selected as one known whole word by an independent base MeCab instance; unknown letter-spelled tokens do not count as known words.", + "Exclude every imported surface selected as one known whole word by an independent base MeCab instance. Also exclude redundant whole-token readings identical to the base letter/initialism reading (e.g. rpm); other unknown letter-spelled words remain eligible for improved readings.", "Quarantine all variants of a surface if any reading ends in ズ without CMU S/Z, ド without D/T, or ング without NG. Accept every CMU pronunciation variant. These are the external audit rules; omit its unreachable ッド branch. Do not promote another unverified shifted reading.", - "Raise plain Latin word costs to one common floor, keeping higher existing costs and punctuation-bearing compound costs; prevent cheap imported fragments from splitting known base words." + "Surround each retained Latin surface with native fullwidth spaces. Immediately before frontend analysis, mark only complete tokens present in the successfully registered official CSV. Both edges prevent prefix, interior and suffix matches. Unlisted tokens, units, base/legacy/personal-only paths keep their input unchanged. Preserve every original source cost." ], - "limitations": "Final-phone screening is conservative quarantine, not proof of an error or a proposed replacement. Legitimate loanword adaptations can be excluded. Interior shifts and vowel-ending errors can escape this rule. The common cost floor is qualified against all base-owned imported words in three native contexts and the retained reviewed-word gate, not all possible Japanese contexts. No runtime word exceptions are added.", - "latin_word_cost_floor": 10300, + "limitations": "Final-phone screening is conservative quarantine, not proof of an error or a proposed replacement. Legitimate loanword adaptations can be excluded. Interior shifts and vowel-ending errors can escape this rule. The prepared CSV requires the matching frontend token-boundary integration; raw pyopenjtalk callers must apply it too. No runtime word exceptions are added. Native selection/prosody checks do not replace listening review.", "cmudict_hash_normalization": "UTF-8 bytes with CRLF normalized to LF", "external_audit": { "input_pack_version": "2026.10.18.1", @@ -9956,6 +10279,7 @@ "frequent_doc_rows": 49, "frequent_doc_distinct_words": 48, "flag_list_sha256": "b8446675edbd1ff0796b76ad6167e8e20266b198a9829d8f60bc0e37e0f73e2a" - } + }, + "latin_word_boundary": "U+3000 at both edges" } } diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py index fe2f5110..868980e0 100644 --- a/tools/prepare_japanese_dictionary.py +++ b/tools/prepare_japanese_dictionary.py @@ -31,7 +31,7 @@ def prepare_dictionary(source: Path, *, project_root: Path = PROJECT_ROOT, recipe_path: Path = RECIPE_PATH, corrections_path: Path = CORRECTIONS_PATH, overwrite: bool = False, count_mora: Callable[[str], int | None] | None = None, normalize_surface: Callable[[str], str] | None = None, - base_word: Callable[[str], bool] | None = None) -> Path: + base_word: Callable[..., bool] | None = None) -> Path: recipe = json.loads(Path(recipe_path).read_text(encoding="utf-8")) data = Path(source).read_bytes().replace(b"\r\n", b"\n") if hashlib.sha256(data).hexdigest() != recipe["source"]["sha256"]: @@ -68,7 +68,9 @@ def normalize(value: str) -> str: expected = before.get(word) matches = expected is not None and unicodedata.normalize("NFC", row[12]) == unicodedata.normalize("NFC", expected) if word in corrections and matches: + original_cost = row[3] row = list(corrections[word]) + row[3] = original_cost corrected.append(row) existing = {normalize(row[0]) for row in rows} for word, correction in corrections.items(): @@ -76,8 +78,7 @@ def normalize(value: str) -> str: row = list(correction) corrected.append(row) corrected, normalization = normalize_rows(corrected, count_mora, normalize_surface) - corrected, filtering = filter_extension_rows(corrected, cmu_pronunciations(cmu_data), base_word, - cost_floor=policy["latin_word_cost_floor"]) + corrected, filtering = filter_extension_rows(corrected, cmu_pronunciations(cmu_data), base_word) buffer = io.StringIO(newline="") csv.writer(buffer, lineterminator="\n").writerows(corrected) derivative = buffer.getvalue().encode("utf-8") diff --git a/tools/validate_japanese_dictionary.py b/tools/validate_japanese_dictionary.py index fc4b298e..72f796c5 100644 --- a/tools/validate_japanese_dictionary.py +++ b/tools/validate_japanese_dictionary.py @@ -11,12 +11,12 @@ ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) -from config.japanese_dictionary import PACK_TREE, dictionary_rows, load_dictionary_pack # noqa: E402 +from config.japanese_dictionary import LATIN_WORD_BOUNDARY, PACK_TREE, dictionary_rows, load_dictionary_pack # noqa: E402 from tools.audit_japanese_dictionary import ( # noqa: E402 cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, word_key, ) from tools.tts_text_processor import correct_pronunciation_for_tts # noqa: E402 -from tts.japanese_dictionary import configure_japanese_dictionary # noqa: E402 +from tts.japanese_dictionary import configure_japanese_dictionary, with_latin_word_boundaries # noqa: E402 # Numeric Latin suffixes, inflected initialisms/names, native Japanese and # punctuation exercise both sides of the optional pack's authority boundary. @@ -68,28 +68,37 @@ def production_phones(text: str) -> list[str]: if hashlib.sha256(cmu_data).hexdigest() != policy["cmudict_sha256"]: raise RuntimeError("Release gate requires the recipe's CMU screening data") cmu = cmu_pronunciations(cmu_data) - _, screening = filter_extension_rows(rows, cmu, base_word, cost_floor=policy["latin_word_cost_floor"]) + correction_data = (ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes().replace(b"\r\n", b"\n") + if hashlib.sha256(correction_data).hexdigest() != metadata["corrections_sha256"]: + raise RuntimeError("Release gate requires the pack's correction recipe") + corrected = list(dictionary_rows(correction_data)) + excluded = policy["results"]["excluded"] + _, screening = filter_extension_rows(rows, cmu, base_word) vocabulary_data = json.loads((ROOT / "tools/data/japanese_dictionary_common_words.json").read_text(encoding="utf-8")) common = vocabulary_data["words"] - excluded = policy["results"]["excluded"] base_owned = sorted({item["surface"] for item in excluded if "base_dictionary_word" in item["reasons"]}) - corrected = list(dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) surface = jtalk.normalize_for_mecab + def native_text(text: str) -> str: + return with_latin_word_boundaries(correct_pronunciation_for_tts(text).lower()) + + def lexical_surface(value: str) -> str: + return value.strip(LATIN_WORD_BOUNDARY) + def target_readings(text: str, word: str) -> list[str]: - return [f["read"] for f in pyopenjtalk.run_frontend(correct_pronunciation_for_tts(text).lower()) - if f["string"] == surface(word)] + return [f["read"] for f in pyopenjtalk.run_frontend(native_text(text)) + if lexical_surface(f["string"]) == surface(word)] expected = defaultdict(set) for row in rows: - expected[row[0]].add(row[11]) + expected[lexical_surface(row[0])].add(row[11]) failures, contexts, frontend_words = [], 0, 0 if screening["excluded_entries"]: failures.append({"kind": "unscreened_entries", "entries": screening["excluded"]}) - if screening["latin_word_costs_raised"]: - failures.append({"kind": "unbounded_word_costs", "count": screening["latin_word_costs_raised"]}) + if screening["latin_word_boundaries_added"]: + failures.append({"kind": "missing_word_boundaries", "count": screening["latin_word_boundaries_added"]}) try: - pyopenjtalk.unset_user_dict() + configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=False) baseline = {text: production_phones(text) for text in BASE_SAMPLES} unit_baseline = {text: production_phones(text) for text in UNIT_SAMPLES} # Compare every base-owned imported surface, not just a hand-picked @@ -97,11 +106,19 @@ def target_readings(text: str, word: str) -> list[str]: base_controls = {prefix + word + suffix for word in base_owned for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。"))} english_controls = base_controls | set(common) | set(REPORTED_CONTEXTS) + space_controls = base_controls | set(BASE_SAMPLES) | set(UNIT_SAMPLES) | set(vocabulary_data["base_fallback_words"]) english_baseline = {text: production_phones(text) for text in sorted(english_controls)} context_baseline = {text: target_readings(text, word) for text, word in REPORTED_CONTEXTS.items()} status = configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=True) if not status["ready"]: raise RuntimeError(status["detail"]) + boundary_checks = 0 + for text in sorted(space_controls): + prepared = correct_pronunciation_for_tts(text).lower() + bounded = with_latin_word_boundaries(prepared) + boundary_checks += 1 + if bounded != prepared: + failures.append({"kind": "unexpected_base_token_boundary", "text": text}) for text, before in baseline.items(): after = production_phones(text) if after != before: @@ -125,12 +142,12 @@ def target_readings(text: str, word: str) -> list[str]: if must_preserve and after != before: failures.append({"kind": "english_base_regression", "text": text, "before": before, "after": after}) if text in common: - observed = pyopenjtalk.run_frontend(correct_pronunciation_for_tts(text).lower()) + observed = pyopenjtalk.run_frontend(native_text(text)) reading = vocabulary_data["reviewed_readings"].get(text) if reading and [f["read"] for f in observed] != [reading]: failures.append({"kind": "reviewed_english_reading", "text": text, "expected": reading, "observed": observed}) - vocabulary.append({"word": text, "base_owned": base_word(text), + vocabulary.append({"word": text, "base_owned": word_key(text) in base_owned, "quarantined": word_key(text) in quarantined, "changed": after != before, "readings": [f["read"] for f in observed]}) sentence_results = [] @@ -152,7 +169,8 @@ def target_readings(text: str, word: str) -> list[str]: # of the base or of conservative release-data exclusions. continue for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): - whole = [f for f in pyopenjtalk.run_frontend(prefix + word.lower() + suffix) if f["string"] == key] + whole = [f for f in pyopenjtalk.run_frontend(with_latin_word_boundaries(prefix + word.lower() + suffix)) + if lexical_surface(f["string"]) == key] contexts += 1 if len(whole) != 1 or whole[0]["read"] not in expected[key]: failures.append({"kind": "whole_word", "word": word, "prefix": prefix, "suffix": suffix, "observed": whole}) @@ -164,6 +182,7 @@ def target_readings(text: str, word: str) -> list[str]: failures.append({"kind": "frontend", "word": word, "phones": phones}) return {"version": pack.version, "entry_count": pack.entry_count, "sha256": pack.sha256, "base_samples": len(BASE_SAMPLES), "native_contexts": contexts, + "unmodified_base_inputs": boundary_checks, "additional_unit_phoneme_checks": len(UNIT_SAMPLES), "base_owned_words": len(base_owned), "english_base_contexts": len(base_controls), "common_english_words": len(common), "common_english_results": vocabulary, @@ -173,7 +192,7 @@ def target_readings(text: str, word: str) -> list[str]: "production_frontend_words": frontend_words, "failures": failures, "limitations": "Native phoneme/prosody and token-selection checks; no waveform or listening acceptance."} finally: - pyopenjtalk.unset_user_dict() + configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=False) def main() -> int: diff --git a/tts/japanese_dictionary.py b/tts/japanese_dictionary.py index 471afcd9..4c91d639 100644 --- a/tts/japanese_dictionary.py +++ b/tts/japanese_dictionary.py @@ -5,14 +5,36 @@ import logging import os from pathlib import Path +import re import tempfile from config.asset_paths import PROJECT_ROOT -from config.japanese_dictionary import PACK_TREE, dictionary_rows, load_dictionary_pack +from config.japanese_dictionary import ( + LATIN_WORD_BOUNDARY, PACK_TREE, dictionary_rows, latin_word_key, load_dictionary_pack, +) logger = logging.getLogger(__name__) _runtime_status = {"state": "not_loaded", "ready": False, "version": "", "identity": None, "detail": "Japanese frontend has not loaded."} +_official_latin_words: frozenset[str] = frozenset() + +_LATIN_TOKEN = re.compile( + r"[0-90-9]*[A-Za-zA-Za-z][A-Za-zA-Za-z0-90-9]*" + r"(?:['’'−--][A-Za-zA-Za-z0-90-9]+)*['’']?" +) + + +def with_latin_word_boundaries(text: str, words: frozenset[str] | None = None) -> str: + """Native-ignored spaces make official entries match complete Latin tokens. + + Apply after sentence/punctuation splitting. Both edges are necessary: an + imported word must not consume either the prefix or suffix of another word. + Only registered official words are marked. Other text keeps the original + base, legacy and personal dictionary behavior, including measurement units. + """ + words = _official_latin_words if words is None else words + return _LATIN_TOKEN.sub(lambda match: (LATIN_WORD_BOUNDARY + match.group() + LATIN_WORD_BOUNDARY + if latin_word_key(match.group()) in words else match.group()), text) def _enabled() -> bool: @@ -26,7 +48,8 @@ def dictionary_runtime_status() -> dict: def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, enabled: bool | None = None) -> dict: """Load independent sources without making optional failures fatal.""" - global _runtime_status + global _runtime_status, _official_latin_words + _official_latin_words = frozenset() state, version, identity = "invalid", "", None detail = "Optional Japanese dictionary unavailable." reset = None @@ -40,7 +63,8 @@ def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, e project_root = Path(project_root).resolve() base = Path(os.fsdecode(jtalk.OPEN_JTALK_DICT_DIR)) system = (base / "sys.dic").stat() - native_key = (jtalk.__version__, str(base), system.st_size, system.st_mtime_ns) + native_key = (jtalk.__version__, str(base), system.st_size, system.st_mtime_ns, + "latin-whole-words.v1") compiled, digests, issues = [], [], [] cache = project_root / ".cache/japanese_dictionary" official = project_root / "assets" / PACK_TREE / "userdict.csv" @@ -63,6 +87,9 @@ def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, e if pack is not None: if digest != pack.sha256: raise ValueError("Dictionary changed during loading; restart after installation finishes") + official_words = frozenset(latin_word_key(row[0]) for row in dictionary_rows(data) + if row[0].startswith(LATIN_WORD_BOUNDARY) + and row[0].endswith(LATIN_WORD_BOUNDARY)) else: for _ in dictionary_rows(data): pass @@ -102,6 +129,7 @@ def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, e digests.append(digest) if pack is not None: version = pack.version + _official_latin_words = official_words elif source == legacy: version = "legacy-local" identity = hashlib.sha256(repr((native_key, tuple(digests))).encode()).hexdigest() @@ -128,6 +156,7 @@ def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, e # Configuration, native API and reset errors belong to this optional # boundary. An unsupported runtime must still import the base frontend. identity = None + _official_latin_words = frozenset() state = "unsupported" if state == "unsupported" else "invalid" detail = str(exc) if callable(reset): From 47f1d9cf627be04acf873361412acfd7696ba1be Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 07:36:29 +1300 Subject: [PATCH 06/10] feat(tts): add optional contextual Japanese reading model --- .env.example | 5 + GPT_SoVITS/text/japanese.py | 7 +- LICENSES/TSQYOMI-MIT.txt | 21 + LICENSES/TSQYOMI.md | 29 + LICENSES/provenance.json | 19 + THIRD_PARTY_NOTICES.md | 7 + assets/index.json | 23 + .../catalog/voice/tts_japanese_reading.json | 23 + config/japanese_reading.py | 60 ++ config/settings.py | 1 + docs/japanese_context_reading.md | 111 +++ docs/japanese_pronunciation_dictionary.md | 5 + .../src/renderer/components/SettingsPage.tsx | 4 +- .../components/voiceConfigurationCatalog.ts | 15 +- .../src/shared/configCatalog.generated.ts | 36 + .../tests/emotionReferenceSettings.test.mjs | 2 +- .../tests/voiceConfigurationCatalog.test.mjs | 12 +- release/source_release_policy.json | 8 +- server/handlers/system_handler.py | 19 + tests/test_japanese_reading.py | 144 ++++ tests/test_release_tooling.py | 11 +- tests/test_system_settings.py | 34 + tools/data/japanese_reading_cases.json | 748 ++++++++++++++++++ tools/prepare_japanese_reading.py | 59 ++ tools/validate_japanese_reading.py | 108 +++ tts/japanese_dictionary.py | 10 +- tts/japanese_reading.py | 55 ++ 27 files changed, 1564 insertions(+), 12 deletions(-) create mode 100644 LICENSES/TSQYOMI-MIT.txt create mode 100644 LICENSES/TSQYOMI.md create mode 100644 config/catalog/voice/tts_japanese_reading.json create mode 100644 config/japanese_reading.py create mode 100644 docs/japanese_context_reading.md create mode 100644 tests/test_japanese_reading.py create mode 100644 tools/data/japanese_reading_cases.json create mode 100644 tools/prepare_japanese_reading.py create mode 100644 tools/validate_japanese_reading.py create mode 100644 tts/japanese_reading.py diff --git a/.env.example b/.env.example index 018d97da..7adbee63 100644 --- a/.env.example +++ b/.env.example @@ -575,3 +575,8 @@ TTS_OUTPUT_LANGUAGE=日文 # 日文 | 英文 # Japanese pronunciation dictionary ENABLE_JAPANESE_USER_DICTIONARY=true # END GENERATED CONFIG: tts_japanese_dictionary + +# BEGIN GENERATED CONFIG: tts_japanese_reading +# Japanese contextual readings +ENABLE_JAPANESE_CONTEXT_READING=true +# END GENERATED CONFIG: tts_japanese_reading diff --git a/GPT_SoVITS/text/japanese.py b/GPT_SoVITS/text/japanese.py index 4c6d9751..cc62a27a 100644 --- a/GPT_SoVITS/text/japanese.py +++ b/GPT_SoVITS/text/japanese.py @@ -3,6 +3,7 @@ import pyopenjtalk from tts.japanese_dictionary import configure_japanese_dictionary, with_latin_word_boundaries +from tts.japanese_reading import run_frontend configure_japanese_dictionary(pyopenjtalk) @@ -93,7 +94,9 @@ def preprocess_jap(text, with_prosody=False): if with_prosody: text += pyopenjtalk_g2p_prosody(sentence)[1:-1] else: - p = pyopenjtalk.g2p(with_latin_word_boundaries(sentence)) + features = run_frontend(pyopenjtalk, with_latin_word_boundaries(sentence)) + labels = pyopenjtalk.make_label(features) + p = " ".join(re.search(r"\-(.*?)\+", label).group(1) for label in labels[1:-1]) text += p.split(" ") if i < len(marks): @@ -133,7 +136,7 @@ def pyopenjtalk_g2p_prosody(text, drop_unvoiced_vowels=True): modeling for neural TTS`: https://doi.org/10.1587/transinf.2020EDP7104 """ - labels = pyopenjtalk.make_label(pyopenjtalk.run_frontend(with_latin_word_boundaries(text))) + labels = pyopenjtalk.make_label(run_frontend(pyopenjtalk, with_latin_word_boundaries(text))) N = len(labels) phones = [] diff --git a/LICENSES/TSQYOMI-MIT.txt b/LICENSES/TSQYOMI-MIT.txt new file mode 100644 index 00000000..dbecf11e --- /dev/null +++ b/LICENSES/TSQYOMI-MIT.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Aivis Project + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSES/TSQYOMI.md b/LICENSES/TSQYOMI.md new file mode 100644 index 00000000..e4000165 --- /dev/null +++ b/LICENSES/TSQYOMI.md @@ -0,0 +1,29 @@ +# Optional tsqyomi v5 Japanese context-reading model + +Source: https://huggingface.co/tsukumijima/tsqyomi-models + +Pinned revision: `81add61ddba9669d328e307c883d05d77d60f5f4`, directory `v5/`. +The unchanged ONNX model, tokenizer and metadata hashes are recorded in +`config/japanese_reading.py` and in the generated pack provenance. + +The upstream model card declares `license: mit` and `library_name: onnxruntime`. +It identifies these files as internal model assets for pyopenjtalk-plus and asks +consumers to use that library. Amadeus uses its native candidate-selection and +NJD APIs. The repository's `LICENSE.txt` supplies the full MIT notice, including +`Copyright (c) 2026 Aivis Project`; `MIT.txt` retains that notice verbatim. This +record relies on the immediate distributor's explicit model license declaration; +it does not establish the exact training-data provenance. + +Model-card permalink: +https://huggingface.co/tsukumijima/tsqyomi-models/blob/81add61ddba9669d328e307c883d05d77d60f5f4/README.md + +License permalink: +https://huggingface.co/tsukumijima/tsqyomi-models/blob/81add61ddba9669d328e307c883d05d77d60f5f4/LICENSE.txt + +Upstream frontend: https://github.com/tsukumijima/pyopenjtalk-plus + +The optional `japanese-context-reading` pack contains only the three unchanged +model assets, provenance, this notice and the MIT terms. No voice recordings, +personal dictionary or executable Python code is bundled. Startup never +downloads assets. This is a contextual reading selector, not an emotion planner +or a guarantee of correct readings for every Japanese sentence. diff --git a/LICENSES/provenance.json b/LICENSES/provenance.json index 3b597dfd..70dd293c 100644 --- a/LICENSES/provenance.json +++ b/LICENSES/provenance.json @@ -463,6 +463,25 @@ "release_action": "include", "gate_status": "ready", "notes": "Unmodified canonical example used to verify the pinned protocol against Host rendering policy. Exact digest is recorded in protocol-source.json." + }, + { + "id": "japanese-context-reading", + "name": "Optional tsqyomi v5 Japanese context-reading model", + "category": "external-runtime-data", + "paths": [ + "assets/text/ja/context-reading/**" + ], + "source_url": "https://huggingface.co/tsukumijima/tsqyomi-models", + "source_revision": "81add61ddba9669d328e307c883d05d77d60f5f4; v5/model.onnx, v5/tokenizer.json, v5/metadata.json", + "license_expression": "MIT", + "license_evidence": [ + "LICENSES/TSQYOMI.md", + "LICENSES/TSQYOMI-MIT.txt" + ], + "modified": false, + "release_action": "exclude", + "gate_status": "ready", + "notes": "Unchanged model assets are optional external runtime data. Exact hashes and the upstream MIT notice (Copyright (c) 2026 Aivis Project) are retained; exact training-data provenance is not established. No model publication is authorized by this record." } ] } diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index eb16e434..98e8b6c8 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -165,6 +165,13 @@ compiled caches remain excluded from source archives. `tools/uvr5/**` is also excluded because no Amadeus Electron/server/TTS runtime caller reaches that upstream WebUI utility tree. +### Optional Japanese context-reading model + +The optional Japanese context-reading model is distributed separately. Its exact +upstream model/tokenizer/metadata revision, MIT copyright/license notice and +training-provenance limits are recorded in `LICENSES/TSQYOMI.md`. No model bytes +are included in the application source archive. + ### Character, model, and voice material Character frames, videos, unverified packaged audio, model weights, LoRA diff --git a/assets/index.json b/assets/index.json index a0a5020e..f5704535 100644 --- a/assets/index.json +++ b/assets/index.json @@ -160,6 +160,29 @@ "models/gpt-sovits/weights/sovits/v2Pro/kurisu_v2pro.pth" ], "validator": "required_files" + }, + { + "id": "japanese-context-reading", + "display_name": "Japanese Context Reading Model (tsqyomi v5)", + "spec_version": 1, + "paths": [ + "text/ja/context-reading/model.onnx", + "text/ja/context-reading/tokenizer.json", + "text/ja/context-reading/metadata.json", + "text/ja/context-reading/provenance.json", + "text/ja/context-reading/NOTICE.md", + "text/ja/context-reading/MIT.txt" + ], + "trees": [], + "required": [ + "text/ja/context-reading/model.onnx", + "text/ja/context-reading/tokenizer.json", + "text/ja/context-reading/metadata.json", + "text/ja/context-reading/provenance.json", + "text/ja/context-reading/NOTICE.md", + "text/ja/context-reading/MIT.txt" + ], + "validator": "required_files" } ] } diff --git a/config/catalog/voice/tts_japanese_reading.json b/config/catalog/voice/tts_japanese_reading.json new file mode 100644 index 00000000..a8b81515 --- /dev/null +++ b/config/catalog/voice/tts_japanese_reading.json @@ -0,0 +1,23 @@ +{ + "id": "tts_japanese_reading", + "title": {"en-US": "Japanese contextual readings", "zh-CN": "日语语境读音"}, + "description": { + "en-US": "Use an optionally installed model to distinguish Japanese readings from context.", + "zh-CN": "使用可选安装的模型,根据上下文区分日语汉字读音。" + }, + "desktop": true, + "apply": "backend_restart", + "config": { + "ENABLE_JAPANESE_CONTEXT_READING": { + "type": "boolean", + "title": {"en-US": "Use installed context-reading model", "zh-CN": "使用已安装的语境读音模型"}, + "description": { + "en-US": "Install japanese-context-reading separately. No automatic download. Restart the backend after changes.", + "zh-CN": "需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + }, + "default": true, + "accepted_values": ["true", "false", "1", "0", "yes", "no"], + "example_active": true + } + } +} diff --git a/config/japanese_reading.py b/config/japanese_reading.py new file mode 100644 index 00000000..9e0ee37c --- /dev/null +++ b/config/japanese_reading.py @@ -0,0 +1,60 @@ +"""Pinned, model-free contract for the optional Japanese context-reading model.""" +from __future__ import annotations + +from dataclasses import dataclass +from functools import lru_cache +import hashlib +import json +from pathlib import Path + +PACK_ID = "japanese-context-reading" +PACK_TREE = "text/ja/context-reading" +VERSION = "2026.10.20.1" +SOURCE_REPOSITORY = "https://huggingface.co/tsukumijima/tsqyomi-models" +SOURCE_REVISION = "81add61ddba9669d328e307c883d05d77d60f5f4" +MODEL_HASHES = { + "model.onnx": "0ff2950a6fda3befafd7792fd4b8141f3ab97756ec878057d03727c1adca2bd8", + "tokenizer.json": "6a0734cf21c802169defaffe719bc2ef12bb9d0be37e54b61ed27aa89394723d", + "metadata.json": "ccf2a795aeb846a281fbe639bc75ad3f74e0439c6eec356287819c74208386bc", +} +PACK_FILES = (*MODEL_HASHES, "provenance.json", "NOTICE.md", "MIT.txt") + + +class JapaneseReadingError(ValueError): + pass + + +@dataclass(frozen=True) +class JapaneseReadingPack: + root: Path + identity: str + + +@lru_cache(maxsize=8) +def _checked_pack(root: Path, signatures: tuple) -> JapaneseReadingPack: + for name, expected in MODEL_HASHES.items(): + with (root / name).open("rb") as stream: + if hashlib.file_digest(stream, "sha256").hexdigest() != expected: + raise JapaneseReadingError(f"Context-reading model hash mismatch: {name}") + provenance = json.loads((root / "provenance.json").read_text(encoding="utf-8")) + if (provenance.get("version") != VERSION + or provenance.get("source") != {"repository": SOURCE_REPOSITORY, "revision": SOURCE_REVISION, "directory": "v5"} + or provenance.get("sha256") != MODEL_HASHES): + raise JapaneseReadingError("Context-reading model provenance differs from the pinned source") + identity = hashlib.sha256(repr(("tsqyomi-v5.v1", MODEL_HASHES)).encode()).hexdigest() + return JapaneseReadingPack(root, identity) + + +def load_reading_pack(root: Path) -> JapaneseReadingPack: + root = Path(root).resolve() + try: + signatures = [] + for name in PACK_FILES: + path = root / name + if path.is_symlink() or not path.is_file() or not path.stat().st_size: + raise JapaneseReadingError(f"Missing or empty context-reading pack member: {name}") + info = path.stat() + signatures.append((info.st_size, info.st_mtime_ns, info.st_ctime_ns)) + return _checked_pack(root, tuple(signatures)) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise JapaneseReadingError(f"Cannot load context-reading pack: {exc}") from exc diff --git a/config/settings.py b/config/settings.py index 9232fe66..fc7894b8 100644 --- a/config/settings.py +++ b/config/settings.py @@ -135,6 +135,7 @@ ASR_VAD_SILENCE_MS: int = _catalog_values["ASR_VAD_SILENCE_MS"] ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING: bool = _catalog_values["ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING"] ENABLE_JAPANESE_USER_DICTIONARY: bool = _catalog_values["ENABLE_JAPANESE_USER_DICTIONARY"] +ENABLE_JAPANESE_CONTEXT_READING: bool = _catalog_values["ENABLE_JAPANESE_CONTEXT_READING"] TTS_REF_AUDIO_JA: str = _catalog_values["TTS_REF_AUDIO_JA"] TTS_REF_TEXT_JA: str = _catalog_values["TTS_REF_TEXT_JA"] TTS_REF_AUDIO_EN: str = _catalog_values["TTS_REF_AUDIO_EN"] diff --git a/docs/japanese_context_reading.md b/docs/japanese_context_reading.md new file mode 100644 index 00000000..f4dbf9f4 --- /dev/null +++ b/docs/japanese_context_reading.md @@ -0,0 +1,111 @@ +# Optional Japanese contextual readings / 可选日语语境读音 + +`japanese-context-reading` adds upstream tsqyomi v5 reading selection to the +existing Japanese OpenJTalk frontend. It chooses among native reading candidates +before NJD computes morae, accent and phrase connections. Original Japanese text +is retained. This is independent of the optional English loanword dictionary, +voice weights and emotion references; it is not a new semantic emotion planner. + +The pinned model comes from `tsukumijima/tsqyomi-models`, revision +`81add61ddba9669d328e307c883d05d77d60f5f4`, directory `v5/`. The native API is the +existing `pyopenjtalk-plus==0.4.1.post9`; no replacement G2P library is required. +The model, tokenizer and metadata hashes are fixed together in +`config/japanese_reading.py`. Source/license limits are recorded in +`LICENSES/TSQYOMI.md`; unchanged model files remain outside Git/source archives. + +## Install and control + +Install a compatible application version before the independent resource pack: + +```powershell +python tools/external_assets.py verify output/amadeus-japanese-context-reading-2026.10.20.1.zip +python tools/external_assets.py install output/amadeus-japanese-context-reading-2026.10.20.1.zip +``` + +The Voice setting **Use installed context-reading model** maps to +`ENABLE_JAPANESE_CONTEXT_READING`. It defaults to allowing an installed model, +but never installs/downloads one. Missing optional data is normal and inactive. +Restart the backend after installing, updating or changing this setting. Turning +it off uses the existing native reading selection; the English dictionary has +its own independent switch. Invalid/incomplete data or unsupported native APIs +are reported and leave the existing frontend usable. + +安装资源或更改“语音 → 日语语境读音”后须重启后端。未安装时不会自动下载, +仍使用原前端。语境读音模型和英文词典可以独立安装、独立关闭。 + +Startup verifies all three source hashes and loads only local files, explicitly +on CPU. It does not contact Hugging Face or choose a GPU provider. The loaded +model identity joins the actual native dictionary identity in first-sentence +audio-cache keys and in the existing sidecar ready/done status. Disabled or failed +model activation cannot reuse model-conditioned audio as base audio. + +Both plain-phone and prosody frontend paths use the selected native readings. +Checkpoint symbol IDs remain unchanged: the existing frontend's `#` boundary +mapping to its UNK ID is retained rather than adding an incompatible embedding +entry. This compatibility fact is separate from unknown lexical phonemes. + +## Offline preparation and validation + +Obtain the three pinned `v5/` files separately; startup does not fetch them. + +```powershell +python tools/prepare_japanese_reading.py --source path/to/pinned/v5 +python tools/validate_japanese_reading.py --output output/japanese_reading_validation.json +python tools/external_assets.py build japanese-context-reading --output output/amadeus-japanese-context-reading-2026.10.20.1.zip +``` + +The six-member pack contains the unchanged ONNX model, tokenizer and metadata, +provenance, source notice and MIT terms. The existing installer verifies the ZIP +manifest and installs those files. Native activation additionally checks the +fixed upstream hashes. Preparation refuses a different model or a differing +existing destination unless `--overwrite` is explicit. + +`tools/data/japanese_reading_cases.json` provides 88 synthetic, contrastive reading +targets, including ordinary readings that must not regress. The production-path +gate compares complete native nodes, checks mora counts and symbol compatibility, +and compares plain/prosody paths. It tests correctness against expected readings, +not merely equality before/after installation. + +## Experiment, 2026-10-20 + +Windows AMD64, Python 3.12.10, pyopenjtalk-plus 0.4.1.post9, ONNX Runtime 1.26.0 +and tokenizers 0.22.1 were used for the local comparison. The same 88 lexical +targets and accepted readings were used for each engine. Haqumei used its default +frontend, not Amadeus-specific adaptation. These are curated diagnostics, not an +independent estimate of general Japanese accuracy. + +| Reading selector | Correct targets | Decision | +| --- | ---: | --- | +| Existing frontend (Sudachi / nani defaults) | 69 / 88 | Retain as the base path | +| tsqyomi v4 (published native default model) | 82 / 88 | Reject: regresses ordinary 上手 / 下手 / 生 readings | +| Haqumei 0.12.2 | 69 / 88 | Reject replacement: no net gain on these targets | +| Pinned tsqyomi v5 | 88 / 88 | Optional installed model | + +The v4 snapshot was `680596dd2ad2bad59ee5db3741e197cfce79f9b4`; v5 is pinned +above. The checked frontend upstream commit was +`b17704eed5f8a7eef778310f9eb9a7f71fb5321b`; it also selects v5. Haqumei's +repository was checked at `e70fa6279a225c26f8956ce393730ca58f86db7e`. +The additional 32 contexts were frozen before the extended v5 measurement. +The gate compares whole native nodes: for example, a merged 何と is tested as +何と / ナント, rather than incorrectly demanding a separate 何 node. + +A fresh six-file install into a Japanese path containing spaces passed the +actual production `clean_text` gate: base 69/88, model 88/88, no lost correct +targets, mora mismatches, unsupported phonemes or plain/prosody disagreements. +Warm CPU median was 6.79 ms (base 0.33 ms), p95 7.77 ms; model loading added +0.76 seconds. These are single-machine observations, not performance guarantees. +The existing English-dictionary regression gate also passed with both packs +installed. Long inputs of 100 repeated / 80 mixed clauses completed natively. + +Three sentences were synthesized with the same locally configured V3 character +voice, reference and sampling seed (237), both with and without context reading: +ordinary 上手, stage-side 上手, and deserted-place 人気. All six waveforms were +nonempty and finite at 24 kHz. These private local outputs are not part of the +resource/source release, and have not received listening acceptance. + +Native checks do not prove exhaustive accuracy, appropriate character emotion or +human listening quality. Ambiguous readings accept multiple valid forms; proper +names and readings outside the model/candidate vocabulary remain limited. +Waveform comparisons use the same voice, reference and sampling seed. Their +generation/integrity does not replace listening acceptance. The optional resource +ZIP is kept local until separately approved for publication. diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index a4b01a74..4058f4bc 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -103,6 +103,11 @@ model paths or rebuild any existing model/voice archive. ## Quality boundary / 质量范围 +Japanese kanji readings that depend on context are a separate optional capability; +see [Japanese contextual readings](japanese_context_reading.md). Its model and +switch are independent of this English loanword pack. Disabling this dictionary +does not disable an installed context-reading model. + Version 2026.10.20.2 retains the historical review of 317 entries: 94 lexical/script/annotation repairs, 65 separator/format repairs, and 158 unconfirmed readings. Those decisions are evidence, not mandatory release diff --git a/electron/src/renderer/components/SettingsPage.tsx b/electron/src/renderer/components/SettingsPage.tsx index b662edc7..fe5659db 100644 --- a/electron/src/renderer/components/SettingsPage.tsx +++ b/electron/src/renderer/components/SettingsPage.tsx @@ -1118,6 +1118,7 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack const artifactConfiguration = mergeGroups([catalogConfiguration("auip_artifact_style", catalogDesktop)], asConfigurationGroups(config.artifact_configuration)) const backendVoiceConfiguration = asConfigurationGroups(config.voice_configuration) const dictionaryRuntimeValue = backendVoiceConfiguration.find(group => group.id === 'tts_japanese_dictionary')?.fields.find(field => field.key === 'ENABLE_JAPANESE_USER_DICTIONARY')?.value + const readingRuntimeValue = backendVoiceConfiguration.find(group => group.id === 'tts_japanese_reading')?.fields.find(field => field.key === 'ENABLE_JAPANESE_CONTEXT_READING')?.value const voiceCatalog = buildVoiceConfigurationCatalog({ asrBackend: desktop?.sources?.ASR_BACKEND === 'user' ? desktop.values.ASR_BACKEND : val('asr_backend', 'qwen3_asr'), ttsBackend: desktop?.sources?.TTS_BACKEND === 'user' ? desktop.values.TTS_BACKEND : val('tts_backend', 'gpt_sovits'), @@ -1125,6 +1126,7 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack aecEnabled: desktop?.sources?.AEC_REALTIME_ENABLED === 'user' ? desktop.values.AEC_REALTIME_ENABLED === 'true' : config.aec_realtime_enabled === undefined ? Boolean(desktopCatalogFields.AEC_REALTIME_ENABLED.default) : bool('aec_realtime_enabled'), emotionReferencesEnabled: backendVoiceConfiguration.find(group => group.id === 'tts_emotion_references')?.fields.find(field => field.key === 'ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING')?.value === true, japaneseDictionaryEnabled: typeof dictionaryRuntimeValue === 'boolean' ? dictionaryRuntimeValue : undefined, + japaneseReadingEnabled: typeof readingRuntimeValue === 'boolean' ? readingRuntimeValue : undefined, ttsOutputLanguage: val('tts_output_language', 'ja'), }, { ...catalogDesktop, extensions: desktop?.extensions }, locale) const voiceConfiguration: ConfigurationGroup[] = voiceCatalog.map(base => { @@ -1138,7 +1140,7 @@ export default function SettingsPage({ send, subscribe, connected, reconnectBack fields, // This optional capability also requires a present/loaded native source; // the enabled startup switch alone does not make it active. - ...(base.id === 'tts_japanese_dictionary' ? { active: Boolean(base.active && backend.active) } : {}), + ...(['tts_japanese_dictionary', 'tts_japanese_reading'].includes(base.id) ? { active: Boolean(base.active && backend.active) } : {}), configured: backend.configured ?? base.configured, status: backend.status || base.status, status_ok: backend.status_ok ?? base.status_ok, diff --git a/electron/src/renderer/components/voiceConfigurationCatalog.ts b/electron/src/renderer/components/voiceConfigurationCatalog.ts index 9a5429e1..cca775c8 100644 --- a/electron/src/renderer/components/voiceConfigurationCatalog.ts +++ b/electron/src/renderer/components/voiceConfigurationCatalog.ts @@ -17,6 +17,7 @@ interface VoiceRuntimeSelection { aecEnabled: boolean emotionReferencesEnabled?: boolean japaneseDictionaryEnabled?: boolean + japaneseReadingEnabled?: boolean ttsOutputLanguage?: string } @@ -44,6 +45,11 @@ export function buildVoiceConfigurationCatalog( const acoustic = catalogConfiguration('acoustic_pipeline', snapshot, runtimeValues) const emotion = catalogConfiguration('tts_emotion_references', snapshot, runtimeValues) const dictionary = catalogConfiguration('tts_japanese_dictionary', snapshot, runtimeValues) + const reading = catalogConfiguration('tts_japanese_reading', snapshot, { + ...runtimeValues, + ...(selection.japaneseReadingEnabled === undefined ? {} : { ENABLE_JAPANESE_CONTEXT_READING: selection.japaneseReadingEnabled }), + }) + const readingEnabled = reading.fields[0].value === true const dictionaryEnabled = dictionary.fields[0].value === true const asrBackend = conversation.fields.find(field => field.key === 'ASR_BACKEND')?.value const wakeEnabled = wake.fields.find(field => field.key === 'WAKE_ENABLED')?.value @@ -121,6 +127,13 @@ export function buildVoiceConfigurationCatalog( status: dictionaryEnabled ? unknown : 'Off', status_ok: !dictionaryEnabled, }, + { + ...reading, + active: ttsBackend === 'gpt_sovits' && readingEnabled && ['日文', 'ja'].includes(selection.ttsOutputLanguage || value('TTS_OUTPUT_LANGUAGE')), + configured: !readingEnabled, + status: readingEnabled ? unknown : 'Off', + status_ok: !readingEnabled, + }, { ...emotion, @@ -134,7 +147,7 @@ export function buildVoiceConfigurationCatalog( } export const voiceConfigurationSections = { - output: new Set(['speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', + output: new Set(['speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', 'tts_japanese_reading', ...voiceBackendGroups.filter(group => group.section === 'output').map(group => group.id)]), input: new Set(['conversation_asr', 'wake_asr', 'acoustic_pipeline']), remote: new Set(['asr_remote', ...voiceBackendGroups.filter(group => group.section === 'remote').map(group => group.id)]), diff --git a/electron/src/shared/configCatalog.generated.ts b/electron/src/shared/configCatalog.generated.ts index 56a09fb7..1f3552d7 100644 --- a/electron/src/shared/configCatalog.generated.ts +++ b/electron/src/shared/configCatalog.generated.ts @@ -3143,6 +3143,42 @@ export const catalogGroups: CatalogGroup[] = [ } } }, + { + "id": "tts_japanese_reading", + "title": { + "en-US": "Japanese contextual readings", + "zh-CN": "日语语境读音" + }, + "description": { + "en-US": "Use an optionally installed model to distinguish Japanese readings from context.", + "zh-CN": "使用可选安装的模型,根据上下文区分日语汉字读音。" + }, + "desktop": true, + "apply": "backend_restart", + "config": { + "ENABLE_JAPANESE_CONTEXT_READING": { + "type": "boolean", + "title": { + "en-US": "Use installed context-reading model", + "zh-CN": "使用已安装的语境读音模型" + }, + "description": { + "en-US": "Install japanese-context-reading separately. No automatic download. Restart the backend after changes.", + "zh-CN": "需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + }, + "default": true, + "accepted_values": [ + "true", + "false", + "1", + "0", + "yes", + "no" + ], + "example_active": true + } + } + }, { "id": "voice_reference_profile", "title": { diff --git a/electron/tests/emotionReferenceSettings.test.mjs b/electron/tests/emotionReferenceSettings.test.mjs index 038eb7cb..a9873940 100644 --- a/electron/tests/emotionReferenceSettings.test.mjs +++ b/electron/tests/emotionReferenceSettings.test.mjs @@ -15,7 +15,7 @@ new Function('require', 'exports', compiled)(name => name === 'electron' ? { safeStorage: { isEncryptionAvailable: () => false } } : require(name), exports) -for (const key of ['ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING', 'ENABLE_JAPANESE_USER_DICTIONARY']) test(`${key} persists both values and reaches the next backend`, () => { +for (const key of ['ENABLE_EXPERIMENTAL_V3_EMOTION_ROUTING', 'ENABLE_JAPANESE_USER_DICTIONARY', 'ENABLE_JAPANESE_CONTEXT_READING']) test(`${key} persists both values and reaches the next backend`, () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'amadeus-emotion-settings-')) const file = path.join(root, 'settings.json') const dotenv = path.join(root, '.env') diff --git a/electron/tests/voiceConfigurationCatalog.test.mjs b/electron/tests/voiceConfigurationCatalog.test.mjs index dcbeb02d..2838de26 100644 --- a/electron/tests/voiceConfigurationCatalog.test.mjs +++ b/electron/tests/voiceConfigurationCatalog.test.mjs @@ -11,7 +11,7 @@ test('voice setup remains discoverable without a running backend', () => { }, null) assert.deepEqual(new Set(groups.map(group => group.id)), new Set([ 'conversation_asr', 'asr_remote', 'wake_asr', 'acoustic_pipeline', - 'speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', + 'speech_synthesis', 'voice_reference_profile', 'tts_emotion_references', 'tts_japanese_dictionary', 'tts_japanese_reading', ...voiceBackendGroups.map(group => group.id), ])) assert.equal(groups.find(group => group.id === 'asr_remote').status, 'Optional') @@ -106,6 +106,16 @@ test('Japanese dictionaries are optional and can be disabled with a restart', () assert.equal(switched.active, false) }) +test('Japanese context reading follows the saved switch and output language', () => { + const selection = { asrBackend: 'qwen3_asr', ttsBackend: 'gpt_sovits', wakeEnabled: false, aecEnabled: false, ttsOutputLanguage: 'ja' } + const group = (selected, values = {}) => exports.buildVoiceConfigurationCatalog(selected, { values }).find(item => item.id === 'tts_japanese_reading') + assert.equal(group(selection).fields[0].value, true) + assert.equal(group(selection, { ENABLE_JAPANESE_CONTEXT_READING: 'false' }).active, false) + assert.equal(group({ ...selection, ttsOutputLanguage: 'en' }).active, false) + assert.equal(group({ ...selection, ttsBackend: 'remote' }).active, false) + assert.equal(group({ ...selection, japaneseReadingEnabled: false }).fields[0].value, false) +}) + test('effective dotenv emotion setting is visible until a stored override changes it', () => { const selection = { asrBackend: 'qwen3_asr', ttsBackend: 'gpt_sovits', wakeEnabled: false, aecEnabled: true, emotionReferencesEnabled: true } const field = snapshot => exports.buildVoiceConfigurationCatalog(selection, snapshot).find(group => group.id === 'tts_emotion_references').fields[0] diff --git a/release/source_release_policy.json b/release/source_release_policy.json index 46987bd3..bc570085 100644 --- a/release/source_release_policy.json +++ b/release/source_release_policy.json @@ -109,7 +109,10 @@ "docs/extension_host_profile_v0.1.md", "server/extensions/theme-tokens.v0.json", "tests/fixtures/extension_protocol_theme.json", - "tests/fixtures/extension_theme_colors.json" + "tests/fixtures/extension_theme_colors.json", + "docs/japanese_context_reading.md", + "LICENSES/TSQYOMI.md", + "LICENSES/TSQYOMI-MIT.txt" ], "include_roots": [ "characters", @@ -269,7 +272,8 @@ "docs/images/extensions/theme-native-mixed-update.jpg", "docs/images/extensions/theme-native-mixed-pending.jpg", "docs/images/extensions/theme-native-mixed-chat.jpg", - "docs/images/extensions/theme-native-removed-identity.jpg" + "docs/images/extensions/theme-native-removed-identity.jpg", + "docs/japanese_context_reading.md" ], "exclude_globs": [ "docs/reports/**", diff --git a/server/handlers/system_handler.py b/server/handlers/system_handler.py index 6c87c12e..8752ead4 100644 --- a/server/handlers/system_handler.py +++ b/server/handlers/system_handler.py @@ -137,6 +137,15 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No dictionary_runtime["state"] if dictionary_runtime["state"] != "not_loaded" else dictionary_pack["state"] ) + from config.japanese_reading import PACK_ID as READING_PACK_ID + reading_pack = external_asset_pack_status(READING_PACK_ID) + reading_runtime = dictionary_runtime.get("reading_model", {"state": "not_loaded", "ready": False, + "detail": "Japanese frontend has not loaded."}) + reading_enabled = bool(settings.ENABLE_JAPANESE_CONTEXT_READING) + reading_state = "disabled" if not reading_enabled else str(reading_runtime["state"]) + if reading_enabled and reading_state == "not_loaded": + reading_state = reading_pack["state"] + asr_selected = str(settings.ASR_BACKEND or "qwen3_asr").strip().lower() tts_selected = str(settings.TTS_BACKEND or "gpt_sovits").strip().lower() asr_statuses = asr_backend_statuses(asr_selected) @@ -260,6 +269,16 @@ def _voice_configuration(settings: Any, emotion_pack: dict[str, Any] | None = No " Pack version: " + dictionary_pack["version"] if dictionary_pack.get("version") else "" ), }, + { + **_catalog_configuration("tts_japanese_reading", settings), + "active": tts_selected == "gpt_sovits" and settings.TTS_OUTPUT_LANGUAGE == "日文" and reading_enabled and (reading_pack["installed"] or reading_runtime["ready"]), + "configured": not reading_enabled or reading_pack["installed"] or reading_runtime["ready"], + "status": reading_state, + "status_ok": reading_state in {"disabled", "ready", "installed", "not_installed"}, + "status_detail": reading_runtime["detail"] if reading_state != "not_installed" else ( + "Optional context-reading model is not installed; base Japanese speech remains available." + ), + }, { **_catalog_configuration("tts_emotion_references", settings), diff --git a/tests/test_japanese_reading.py b/tests/test_japanese_reading.py new file mode 100644 index 00000000..9813ab7c --- /dev/null +++ b/tests/test_japanese_reading.py @@ -0,0 +1,144 @@ +"""Optional context reading must preserve native identity and work offline.""" +from types import SimpleNamespace +import hashlib +import json +from pathlib import Path + +import pytest + +from config import japanese_reading as pack +from tts import japanese_reading as runtime +from tools.validate_japanese_reading import target_reading + + +@pytest.fixture(autouse=True) +def isolate_state(monkeypatch): + monkeypatch.setattr(runtime, "_runtime_status", {"state": "not_loaded", "ready": False, + "identity": None, "version": "", "detail": "Not loaded"}) + + +def _prepare(root, monkeypatch): + directory = root / "assets" / pack.PACK_TREE + directory.mkdir(parents=True) + hashes = {} + for name in pack.MODEL_HASHES: + data = name.encode() + (directory / name).write_bytes(data) + hashes[name] = hashlib.sha256(data).hexdigest() + monkeypatch.setattr(pack, "MODEL_HASHES", hashes) + (directory / "provenance.json").write_text(json.dumps({"version": pack.VERSION, + "source": {"repository": pack.SOURCE_REPOSITORY, "revision": pack.SOURCE_REVISION, "directory": "v5"}, + "sha256": hashes}), encoding="utf-8") + (directory / "NOTICE.md").write_text("Source and license", encoding="utf-8") + (directory / "MIT.txt").write_text("MIT terms", encoding="utf-8") + return directory + + +def _native(): + calls = [] + native = SimpleNamespace() + native.tsqyomi = SimpleNamespace(unload_model=lambda: calls.append("unload"), + load_model=lambda **kwargs: calls.append(kwargs), is_model_loaded=lambda: True) + native.run_frontend = lambda text, **kwargs: calls.append((text, kwargs)) or [] + return native, calls + + +def test_optional_missing_and_disabled_models_keep_original_frontend(tmp_path): + native, calls = _native() + assert runtime.configure_japanese_reading(native, project_root=tmp_path, enabled=True)["state"] == "not_installed" + runtime.run_frontend(native, "生きる") + assert calls == [("生きる", {})] + assert runtime.configure_japanese_reading(native, project_root=tmp_path, enabled=False)["state"] == "disabled" + + +def test_ready_model_uses_only_pinned_local_files_and_cpu(tmp_path, monkeypatch): + directory = _prepare(tmp_path, monkeypatch) + native, calls = _native() + status = runtime.configure_japanese_reading(native, project_root=tmp_path, enabled=True) + assert status["ready"] and status["identity"] + assert calls == ["unload", {"model_dir": directory.resolve(), "onnx_providers": ["CPUExecutionProvider"], + "allow_provider_fallback": False}] + runtime.run_frontend(native, "舞台の上手") + assert calls[-1] == ("舞台の上手", {"use_tsqyomi": True}) + runtime.configure_japanese_reading(native, project_root=tmp_path, enabled=False) + runtime.run_frontend(native, "舞台の上手") + assert calls[-1] == ("舞台の上手", {}) + + +@pytest.mark.parametrize("problem", ["corrupt", "missing", "native_failure", "unsupported"]) +def test_invalid_optional_model_is_reported_without_breaking_base_frontend(tmp_path, monkeypatch, problem): + directory = _prepare(tmp_path, monkeypatch) + native, calls = _native() + if problem == "corrupt": + (directory / "model.onnx").write_bytes(b"wrong") + elif problem == "missing": + (directory / "tokenizer.json").unlink() + elif problem == "native_failure": + def fail(**_): + raise RuntimeError("invalid ONNX contract") + native.tsqyomi.load_model = fail + else: + del native.tsqyomi + status = runtime.configure_japanese_reading(native, project_root=tmp_path, enabled=True) + assert status["state"] == "invalid" and not status["ready"] and status["identity"] is None + runtime.run_frontend(native, "人気") + assert calls[-1] == ("人気", {}) + + +def test_context_model_changes_actual_audio_cache_identity(monkeypatch): + from tts import japanese_dictionary as dictionary + monkeypatch.setattr(dictionary, "_runtime_status", {"state": "ready", "identity": "native-dictionary"}) + before = dictionary.dictionary_runtime_status()["identity"] + monkeypatch.setattr(runtime, "_runtime_status", {"state": "ready", "ready": True, "identity": "model-v5"}) + after = dictionary.dictionary_runtime_status()["identity"] + assert before != after and after == dictionary.dictionary_runtime_status()["identity"] + monkeypatch.setattr(dictionary, "_runtime_status", {"identity": None}) + assert dictionary.dictionary_runtime_status()["identity"] is None + + +def test_golden_target_extraction_handles_native_merged_and_split_nodes(): + assert target_reading([{"string": "何と", "read": "ナント"}], "何と") == "ナント" + assert target_reading([{"string": "日本", "read": "ニホン"}, {"string": "中", "read": "ジュウ"}], "日本中") == "ニホンジュウ" + assert target_reading([{"string": "何と", "read": "ナント"}], "何") is None + + +def test_optional_model_pack_roundtrip_and_native_pin_boundary(tmp_path, monkeypatch): + from tools import prepare_japanese_reading as preparation + from tools.external_assets import build_bundle, install_bundle, verify_bundle + + source = tmp_path / "source" + original = _prepare(source, monkeypatch) + monkeypatch.setattr(preparation, "MODEL_HASHES", pack.MODEL_HASHES) + producer = tmp_path / "producer" + prepared = preparation.prepare(original, project_root=producer) + index = Path(__file__).resolve().parents[1] / "assets/index.json" + bundle = tmp_path / "context.zip" + manifest = build_bundle(project_root=producer, output=bundle, pack_ids=[pack.PACK_ID], index_path=index) + assert {item["path"] for item in manifest["files"]} == {f"assets/{pack.PACK_TREE}/{name}" for name in pack.PACK_FILES} + assert verify_bundle(bundle, index_path=index) == manifest + recipient = tmp_path / "日本語 with spaces" + result = install_bundle(bundle, project_root=recipient, index_path=index) + assert result["installed_files"] == len(pack.PACK_FILES) + installed = recipient / "assets" / pack.PACK_TREE + assert pack.load_reading_pack(installed).identity == pack.load_reading_pack(prepared).identity + + # An internally consistent ZIP is not authority to activate different weights. + # Transport integrity belongs to the installer; native compatibility pins here. + (prepared / "model.onnx").write_bytes(b"different model") + replacement = tmp_path / "different.zip" + build_bundle(project_root=producer, output=replacement, pack_ids=[pack.PACK_ID], index_path=index) + install_bundle(replacement, project_root=recipient, index_path=index, overwrite=True) + native, _ = _native() + assert runtime.configure_japanese_reading(native, project_root=recipient, enabled=True)["state"] == "invalid" + + +def test_preparation_rejects_wrong_source_before_writing(tmp_path, monkeypatch): + from tools import prepare_japanese_reading as preparation + + source = _prepare(tmp_path / "source", monkeypatch) + monkeypatch.setattr(preparation, "MODEL_HASHES", pack.MODEL_HASHES) + (source / "metadata.json").write_bytes(b"wrong metadata") + destination = tmp_path / "destination" + with pytest.raises(pack.JapaneseReadingError, match="Source model differs"): + preparation.prepare(source, project_root=destination) + assert not destination.exists() diff --git a/tests/test_release_tooling.py b/tests/test_release_tooling.py index 25d71a19..517da39c 100644 --- a/tests/test_release_tooling.py +++ b/tests/test_release_tooling.py @@ -350,11 +350,16 @@ def test_gpt_sovits_release_boundary_excludes_optional_or_unverified_material() def test_optional_japanese_dictionary_sources_and_notices_have_separate_release_boundaries() -> None: policy = json.loads((ROOT / "release/source_release_policy.json").read_text(encoding="utf-8")) - sources = ["assets/text/ja/english/userdict.csv", "assets/text/ja/user.csv"] - code_and_notices = ["config/japanese_dictionary.py", "tools/data/japanese_dictionary_corrections.csv", "tools/audit_japanese_dictionary.py", + sources = ["assets/text/ja/english/userdict.csv", "assets/text/ja/user.csv", + "assets/text/ja/context-reading/model.onnx", "assets/text/ja/context-reading/tokenizer.json", + "assets/text/ja/context-reading/metadata.json", "assets/text/ja/context-reading/provenance.json"] + code_and_notices = ["config/japanese_reading.py", "tts/japanese_reading.py", "tools/prepare_japanese_reading.py", + "tools/validate_japanese_reading.py", "tools/data/japanese_reading_cases.json", + "docs/japanese_context_reading.md", "LICENSES/TSQYOMI.md", "LICENSES/TSQYOMI-MIT.txt", + "config/japanese_dictionary.py", "tools/data/japanese_dictionary_corrections.csv", "tools/audit_japanese_dictionary.py", "LICENSES/JAPANESE-DICTIONARY.md", "docs/japanese_pronunciation_dictionary.md"] selected, excluded = select_paths([*sources, *code_and_notices], policy) - assert excluded == sources + assert excluded == sorted(sources) assert selected == sorted(code_and_notices) diff --git a/tests/test_system_settings.py b/tests/test_system_settings.py index 721f8d3c..387d4651 100644 --- a/tests/test_system_settings.py +++ b/tests/test_system_settings.py @@ -25,6 +25,40 @@ def close(self) -> None: self.closed = True +@pytest.mark.parametrize("enabled,state,installed,active,ok", [ + (True, "not_loaded", False, False, True), + (True, "ready", True, True, True), + (True, "invalid", True, True, False), + (False, "ready", True, False, True), +]) +def test_context_reading_settings_show_optional_availability_and_actual_runtime( + monkeypatch, enabled, state, installed, active, ok, +) -> None: + from config import settings + from config import asset_packages + from tts import pipeline + + original_status = asset_packages.external_asset_pack_status + + def pack_status(pack_id): + if pack_id == "japanese-context-reading": + return {"installed": installed, "state": "installed" if installed else "not_installed"} + return original_status(pack_id) + + monkeypatch.setattr(settings, "ENABLE_JAPANESE_CONTEXT_READING", enabled) + monkeypatch.setattr(settings, "TTS_BACKEND", "gpt_sovits") + monkeypatch.setattr(settings, "TTS_OUTPUT_LANGUAGE", "日文") + monkeypatch.setattr(asset_packages, "external_asset_pack_status", pack_status) + monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: { + "state": "not_loaded", "detail": "Not loaded", + "reading_model": {"state": state, "ready": state == "ready", "detail": "Observed native state"}, + }) + group = next(group for group in _voice_configuration(settings) if group["id"] == "tts_japanese_reading") + assert group["active"] is active + assert group["status_ok"] is ok + assert group["status"] == ("disabled" if not enabled else "not_installed" if not installed else state) + + def test_settings_connection_descriptors_never_return_secret_values() -> None: from config import settings diff --git a/tools/data/japanese_reading_cases.json b/tools/data/japanese_reading_cases.json new file mode 100644 index 00000000..97e423e2 --- /dev/null +++ b/tools/data/japanese_reading_cases.json @@ -0,0 +1,748 @@ +{ + "description": "Synthetic contrastive Japanese reading cases. Expected lexical readings are acceptance targets, not a claim of native-speaker listening acceptance. Multiple accepted forms record real ambiguity or orthographic conventions.", + "cases": [ + { + "text": "彼は上手に歌う。", + "surface": "上手", + "readings": [ + "ジョウズ" + ], + "sense": "skilled" + }, + { + "text": "彼女は料理が上手だ。", + "surface": "上手", + "readings": [ + "ジョウズ" + ], + "sense": "skilled" + }, + { + "text": "舞台の上手に立つ。", + "surface": "上手", + "readings": [ + "カミテ" + ], + "sense": "stage right" + }, + { + "text": "役者が舞台の上手から登場する。", + "surface": "上手", + "readings": [ + "カミテ" + ], + "sense": "stage right" + }, + { + "text": "彼の方が一枚上手だ。", + "surface": "上手", + "readings": [ + "ウワテ" + ], + "sense": "one step ahead" + }, + { + "text": "彼は歌が下手だ。", + "surface": "下手", + "readings": [ + "ヘタ" + ], + "sense": "unskilled" + }, + { + "text": "下手に触ると壊れる。", + "surface": "下手", + "readings": [ + "ヘタ" + ], + "sense": "carelessly" + }, + { + "text": "舞台の下手から登場する。", + "surface": "下手", + "readings": [ + "シモテ" + ], + "sense": "stage left" + }, + { + "text": "照明を舞台の下手に置く。", + "surface": "下手", + "readings": [ + "シモテ" + ], + "sense": "stage left" + }, + { + "text": "人気のある店です。", + "surface": "人気", + "readings": [ + "ニンキ" + ], + "sense": "popularity" + }, + { + "text": "この商品は人気が高い。", + "surface": "人気", + "readings": [ + "ニンキ" + ], + "sense": "popularity" + }, + { + "text": "人気のない夜道を歩く。", + "surface": "人気", + "readings": [ + "ヒトケ" + ], + "sense": "absence of people" + }, + { + "text": "深夜の公園には人気がない。", + "surface": "人気", + "readings": [ + "ヒトケ" + ], + "sense": "absence of people" + }, + { + "text": "大人気の映画を見た。", + "surface": "大人気", + "readings": [ + "ダイニンキ" + ], + "sense": "very popular" + }, + { + "text": "大人気ないことを言うな。", + "surface": "大人気", + "readings": [ + "オトナゲ" + ], + "sense": "immature" + }, + { + "text": "市場価格を確認する。", + "surface": "市場", + "readings": [ + "シジョウ" + ], + "sense": "economic market" + }, + { + "text": "市場の動向を分析する。", + "surface": "市場", + "readings": [ + "シジョウ" + ], + "sense": "economic market" + }, + { + "text": "朝の市場で野菜を買う。", + "surface": "市場", + "readings": [ + "イチバ" + ], + "sense": "physical marketplace" + }, + { + "text": "魚市場へ行く。", + "surface": "魚市場", + "readings": [ + "ウオイチバ" + ], + "sense": "fish market" + }, + { + "text": "今日は生ビールを飲む。", + "surface": "生ビール", + "readings": [ + "ナマビール" + ], + "sense": "draft beer" + }, + { + "text": "生きる意味を考える。", + "surface": "生きる", + "readings": [ + "イキル" + ], + "sense": "live" + }, + { + "text": "生徒が学校へ行く。", + "surface": "生徒", + "readings": [ + "セイト" + ], + "sense": "student" + }, + { + "text": "生の演奏を聞く。", + "surface": "生", + "readings": [ + "ナマ" + ], + "sense": "live performance" + }, + { + "text": "この世に生を受ける。", + "surface": "生", + "readings": [ + "セイ" + ], + "sense": "life" + }, + { + "text": "本を開く。", + "surface": "開く", + "readings": [ + "ヒラク" + ], + "sense": "open a book" + }, + { + "text": "会議を開く。", + "surface": "開く", + "readings": [ + "ヒラク" + ], + "sense": "hold a meeting" + }, + { + "text": "ドアが開く。", + "surface": "開く", + "readings": [ + "アク", + "ヒラク" + ], + "sense": "both ordinary readings are acceptable here" + }, + { + "text": "この行を削除する。", + "surface": "行", + "readings": [ + "ギョウ" + ], + "sense": "line of text" + }, + { + "text": "銀行で働く。", + "surface": "銀行", + "readings": [ + "ギンコウ" + ], + "sense": "bank" + }, + { + "text": "学校へ行く。", + "surface": "行く", + "readings": [ + "イク", + "ユク" + ], + "sense": "go" + }, + { + "text": "一日中待つ。", + "surface": "一日中", + "readings": [ + "イチニチジュウ" + ], + "sense": "all day" + }, + { + "text": "四月一日に出発する。", + "surface": "一日", + "readings": [ + "ツイタチ" + ], + "sense": "first day of the month" + }, + { + "text": "一日だけ休む。", + "surface": "一日", + "readings": [ + "イチニチ" + ], + "sense": "one day" + }, + { + "text": "何を食べますか。", + "surface": "何", + "readings": [ + "ナニ" + ], + "sense": "what object" + }, + { + "text": "何が起きたのですか。", + "surface": "何", + "readings": [ + "ナニ" + ], + "sense": "what subject" + }, + { + "text": "これは何ですか。", + "surface": "何", + "readings": [ + "ナン" + ], + "sense": "what is this" + }, + { + "text": "何という名前ですか。", + "surface": "何と", + "readings": [ + "ナント" + ], + "sense": "what name; native lexicon groups the following particle with 何" + }, + { + "text": "今日は風が強い。", + "surface": "風", + "readings": [ + "カゼ" + ], + "sense": "wind" + }, + { + "text": "そんな風に言わないで。", + "surface": "風", + "readings": [ + "フウ" + ], + "sense": "in that way" + }, + { + "text": "風邪をひいた。", + "surface": "風邪", + "readings": [ + "カゼ" + ], + "sense": "cold illness" + }, + { + "text": "この方は医者です。", + "surface": "方", + "readings": [ + "カタ" + ], + "sense": "person" + }, + { + "text": "赤い方を選ぶ。", + "surface": "方", + "readings": [ + "ホウ", + "ホオ" + ], + "sense": "alternative; old frontend uses phonetic read field" + }, + { + "text": "食べ方を教える。", + "surface": "方", + "readings": [ + "カタ" + ], + "sense": "way of doing" + }, + { + "text": "唐辛子は辛い。", + "surface": "辛い", + "readings": [ + "カライ" + ], + "sense": "spicy" + }, + { + "text": "失恋して辛い。", + "surface": "辛い", + "readings": [ + "ツライ" + ], + "sense": "painful" + }, + { + "text": "部屋の中に入る。", + "surface": "中", + "readings": [ + "ナカ" + ], + "sense": "inside" + }, + { + "text": "日本中で話題になった。", + "surface": "日本中", + "readings": [ + "ニホンジュウ", + "ニッポンジュウ" + ], + "sense": "throughout Japan" + }, + { + "text": "荷物が重い。", + "surface": "重い", + "readings": [ + "オモイ" + ], + "sense": "heavy" + }, + { + "text": "荷物を重ねる。", + "surface": "重ねる", + "readings": [ + "カサネル" + ], + "sense": "stack" + }, + { + "text": "橋の端で箸を使う。", + "surface": "箸", + "readings": [ + "ハシ" + ], + "sense": "chopsticks" + }, + { + "text": "雨が降り、飴を買った。", + "surface": "飴", + "readings": [ + "アメ" + ], + "sense": "candy; lexical accent still needs separate acceptance" + }, + { + "text": "明日は晴れる。", + "surface": "明日", + "readings": [ + "アシタ", + "アス" + ], + "sense": "both ordinary readings are accepted" + }, + { + "text": "大人になった。", + "surface": "大人", + "readings": [ + "オトナ" + ], + "sense": "adult" + }, + { + "text": "小豆を煮る。", + "surface": "小豆", + "readings": [ + "アズキ" + ], + "sense": "azuki beans" + }, + { + "text": "紅茶を飲む。", + "surface": "紅茶", + "readings": [ + "コウチャ" + ], + "sense": "black tea" + }, + { + "text": "こんにちは、元気ですか。", + "surface": "元気", + "readings": [ + "ゲンキ" + ], + "sense": "wellbeing" + }, + { + "text": "ピアノが上手な友達がいる。", + "surface": "上手", + "readings": [ + "ジョウズ" + ], + "sense": "skilled", + "partition": "additional_contexts" + }, + { + "text": "彼女は字を書くのが上手です。", + "surface": "上手", + "readings": [ + "ジョウズ" + ], + "sense": "skilled", + "partition": "additional_contexts" + }, + { + "text": "上手な説明で助かった。", + "surface": "上手", + "readings": [ + "ジョウズ" + ], + "sense": "skilled", + "partition": "additional_contexts" + }, + { + "text": "舞台の上手側に幕がある。", + "surface": "上手", + "readings": [ + "カミテ" + ], + "sense": "stage right", + "partition": "additional_contexts" + }, + { + "text": "舞台の下手で待機してください。", + "surface": "下手", + "readings": [ + "シモテ" + ], + "sense": "stage left", + "partition": "additional_contexts" + }, + { + "text": "彼は下手な嘘をついた。", + "surface": "下手", + "readings": [ + "ヘタ" + ], + "sense": "unskilled", + "partition": "additional_contexts" + }, + { + "text": "下手をすると事故になる。", + "surface": "下手", + "readings": [ + "ヘタ" + ], + "sense": "carelessly", + "partition": "additional_contexts" + }, + { + "text": "上手を取って相手を投げる。", + "surface": "上手", + "readings": [ + "ウワテ" + ], + "sense": "sumo grip", + "partition": "additional_contexts" + }, + { + "text": "人気がある歌手の新曲を聞く。", + "surface": "人気", + "readings": [ + "ニンキ" + ], + "sense": "popularity", + "partition": "additional_contexts" + }, + { + "text": "人気のない廃屋に入った。", + "surface": "人気", + "readings": [ + "ヒトケ" + ], + "sense": "absence of people", + "partition": "additional_contexts" + }, + { + "text": "市場経済の仕組みを学ぶ。", + "surface": "市場", + "readings": [ + "シジョウ" + ], + "sense": "economic market", + "partition": "additional_contexts" + }, + { + "text": "市場で鮮魚を仕入れる。", + "surface": "市場", + "readings": [ + "イチバ" + ], + "sense": "physical marketplace", + "partition": "additional_contexts" + }, + { + "text": "国際市場で取引する。", + "surface": "市場", + "readings": [ + "シジョウ" + ], + "sense": "economic market", + "partition": "additional_contexts" + }, + { + "text": "彼の生き方が好きです。", + "surface": "生き方", + "readings": [ + "イキカタ" + ], + "sense": "way of life", + "partition": "additional_contexts" + }, + { + "text": "生半可な知識では足りない。", + "surface": "生半可", + "readings": [ + "ナマハンカ" + ], + "sense": "superficial", + "partition": "additional_contexts" + }, + { + "text": "辛いカレーを食べた。", + "surface": "辛い", + "readings": [ + "カライ" + ], + "sense": "spicy", + "partition": "additional_contexts" + }, + { + "text": "仕事を失って辛い。", + "surface": "辛い", + "readings": [ + "ツライ" + ], + "sense": "painful", + "partition": "additional_contexts" + }, + { + "text": "何を買ったのですか。", + "surface": "何", + "readings": [ + "ナニ" + ], + "sense": "what object", + "partition": "additional_contexts" + }, + { + "text": "何でも相談してください。", + "surface": "何でも", + "readings": [ + "ナンデモ" + ], + "sense": "anything", + "partition": "additional_contexts" + }, + { + "text": "何故そう思うのですか。", + "surface": "何故", + "readings": [ + "ナゼ" + ], + "sense": "why", + "partition": "additional_contexts" + }, + { + "text": "今何時ですか。", + "surface": "何時", + "readings": [ + "ナンジ" + ], + "sense": "what clock time", + "partition": "additional_contexts" + }, + { + "text": "工事中の道路は通れない。", + "surface": "工事中", + "readings": [ + "コウジチュウ" + ], + "sense": "during construction", + "partition": "additional_contexts" + }, + { + "text": "世界中を旅したい。", + "surface": "世界中", + "readings": [ + "セカイジュウ" + ], + "sense": "throughout the world", + "partition": "additional_contexts" + }, + { + "text": "風に揺れる木を見上げる。", + "surface": "風", + "readings": [ + "カゼ" + ], + "sense": "wind", + "partition": "additional_contexts" + }, + { + "text": "こんな風に書いてください。", + "surface": "風", + "readings": [ + "フウ" + ], + "sense": "in this way", + "partition": "additional_contexts" + }, + { + "text": "彼女の方が速い。", + "surface": "方", + "readings": [ + "ホウ", + "ホオ" + ], + "sense": "alternative", + "partition": "additional_contexts" + }, + { + "text": "親方が来た。", + "surface": "親方", + "readings": [ + "オヤカタ" + ], + "sense": "master", + "partition": "additional_contexts" + }, + { + "text": "大人の対応をする。", + "surface": "大人", + "readings": [ + "オトナ" + ], + "sense": "adult", + "partition": "additional_contexts" + }, + { + "text": "東京の日本橋を渡る。", + "surface": "日本橋", + "readings": [ + "ニホンバシ" + ], + "sense": "Tokyo place name", + "partition": "additional_contexts" + }, + { + "text": "大阪の日本橋で買い物をする。", + "surface": "日本橋", + "readings": [ + "ニッポンバシ" + ], + "sense": "Osaka place name", + "partition": "additional_contexts" + }, + { + "text": "明後日にまた会おう。", + "surface": "明後日", + "readings": [ + "アサッテ", + "ミョウゴニチ" + ], + "sense": "day after tomorrow", + "partition": "additional_contexts" + }, + { + "text": "四月一日は休日です。", + "surface": "一日", + "readings": [ + "ツイタチ" + ], + "sense": "calendar day", + "partition": "additional_contexts" + } + ] +} diff --git a/tools/prepare_japanese_reading.py b/tools/prepare_japanese_reading.py new file mode 100644 index 00000000..689def43 --- /dev/null +++ b/tools/prepare_japanese_reading.py @@ -0,0 +1,59 @@ +"""Prepare an optional pinned tsqyomi v5 pack from existing local files.""" +from __future__ import annotations + +import argparse +import hashlib +import json +from pathlib import Path +import sys + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from config.japanese_reading import ( # noqa: E402 + MODEL_HASHES, PACK_TREE, SOURCE_REPOSITORY, SOURCE_REVISION, VERSION, + JapaneseReadingError, load_reading_pack, +) + + +def prepare(source: Path, *, project_root: Path = ROOT, overwrite: bool = False) -> Path: + destination = Path(project_root).resolve() / "assets" / PACK_TREE + if not destination.resolve().is_relative_to(Path(project_root).resolve()): + raise JapaneseReadingError("Model output escapes the project root") + source = Path(source) + for name, expected in MODEL_HASHES.items(): + with (source / name).open("rb") as stream: + if hashlib.file_digest(stream, "sha256").hexdigest() != expected: + raise JapaneseReadingError(f"Source model differs from the pinned release: {name}") + provenance = {"version": VERSION, "language": "ja", "model": "tsqyomi-v5", + "source": {"repository": SOURCE_REPOSITORY, "revision": SOURCE_REVISION, "directory": "v5"}, + "sha256": MODEL_HASHES, "license": "MIT", "copyright": "Copyright (c) 2026 Aivis Project"} + files = {name: source / name for name in MODEL_HASHES} + content = {"provenance.json": (json.dumps(provenance, indent=2) + "\n").encode(), + "NOTICE.md": (ROOT / "LICENSES/TSQYOMI.md").read_bytes().replace(b"\r\n", b"\n"), + "MIT.txt": (ROOT / "LICENSES/TSQYOMI-MIT.txt").read_bytes().replace(b"\r\n", b"\n")} + for name in (*files, *content): + target = destination / name + if target.is_symlink(): + raise JapaneseReadingError(f"Model output must not be a symlink: {name}") + expected = MODEL_HASHES.get(name) or hashlib.sha256(content[name]).hexdigest() + if target.exists() and not overwrite: + with target.open("rb") as stream: + if hashlib.file_digest(stream, "sha256").hexdigest() != expected: + raise JapaneseReadingError(f"Existing pack differs: {name}; use --overwrite explicitly") + destination.mkdir(parents=True, exist_ok=True) + import shutil + for name, path in files.items(): + shutil.copyfile(path, destination / name) + for name, data in content.items(): + (destination / name).write_bytes(data) + load_reading_pack(destination) + return destination + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, required=True) + parser.add_argument("--project-root", type=Path, default=ROOT) + parser.add_argument("--overwrite", action="store_true") + args = parser.parse_args() + print(prepare(args.source, project_root=args.project_root, overwrite=args.overwrite)) diff --git a/tools/validate_japanese_reading.py b/tools/validate_japanese_reading.py new file mode 100644 index 00000000..b9564a77 --- /dev/null +++ b/tools/validate_japanese_reading.py @@ -0,0 +1,108 @@ +"""Offline contrastive reading gate through the actual Japanese production frontend.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import statistics +import sys +import time + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) +from tools.tts_text_processor import correct_pronunciation_for_tts # noqa: E402 + +CASES_PATH = ROOT / "tools/data/japanese_reading_cases.json" + + +def target_reading(features: list[dict], surface: str) -> str | None: + """Compare complete native nodes, including targets spanning several nodes.""" + for start in range(len(features)): + word, reading = "", "" + for feature in features[start:]: + word += feature["string"] + reading += feature["read"] + if word == surface: + return reading + if not surface.startswith(word): + break + return None + + +def validate(project_root: Path) -> dict: + import pyopenjtalk + sys.path.insert(0, str(ROOT / "GPT_SoVITS")) + from text import japanese + from text.cleaner import clean_text + from tts.japanese_reading import configure_japanese_reading + from pyopenjtalk.utils import split_kana_mora + from text.symbols2 import symbols + + cases = json.loads(CASES_PATH.read_text(encoding="utf-8"))["cases"] + original = japanese.run_frontend + collected = [] + + def observe(jtalk, text): + features = original(jtalk, text) + collected.extend(features) + return features + + results, summaries = {}, {} + try: + japanese.run_frontend = observe + for name, enabled in (("base", False), ("context", True)): + start = time.perf_counter() + status = configure_japanese_reading(pyopenjtalk, project_root=project_root, enabled=enabled) + load_seconds = time.perf_counter() - start + if enabled and not status["ready"]: + raise RuntimeError(status["detail"]) + observations, timings = [], [] + for case in cases: + collected.clear() + start = time.perf_counter() + phones, _, normalized = clean_text(correct_pronunciation_for_tts(case["text"]), "ja", "v2") + timings.append((time.perf_counter() - start) * 1000) + observed = target_reading(collected, case["surface"]) + features = list(collected) + raw = japanese.g2p(correct_pronunciation_for_tts(case["text"])) + plain = japanese.g2p(correct_pronunciation_for_tts(case["text"]), with_prosody=False) + phonemes = [phone for phone in raw if phone not in {"[", "]", "#"}] + plain_matches = phonemes == [p.lower() if p in "AEIOU" else p for p in plain] + # The existing trained frontend maps '#' to its UNK ID. Keep + # that checkpoint contract; new vocabulary IDs would invalidate + # the existing embedding table. Distinguish it from unknown phones. + unsupported = sorted(set(raw) - set(symbols) - {"#"}) + count_mora = lambda value: len(split_kana_mora(value.replace("’", ""))) + mismatches = [{"surface": f["string"], "pron": f["pron"], "declared": f["mora_size"], + "actual": count_mora(f["pron"])} for f in features + if f["mora_size"] and count_mora(f["pron"]) != f["mora_size"]] + observations.append({**case, "observed": observed, "passed": observed in case["readings"], + "phones": phones, "normalized": normalized, + "unsupported_phonemes": unsupported, + "plain_frontend_matches": plain_matches, + "mora_mismatches": mismatches}) + results[name] = observations + summaries[name] = {"passed": sum(r["passed"] for r in observations), "cases": len(cases), + "load_seconds": load_seconds, "median_ms": statistics.median(timings), + "p95_ms": sorted(timings)[int(.95 * (len(timings) - 1))]} + regressions = [after["text"] for before, after in zip(results["base"], results["context"]) + if before["passed"] and not after["passed"]] + failures = [r for r in results["context"] if not r["passed"] or r["mora_mismatches"] + or r["unsupported_phonemes"] or not r["plain_frontend_matches"]] + return {"summary": summaries, "regressions": regressions, "failures": failures, "results": results, + "limitations": "Synthetic lexical reading and native prosody checks; not exhaustive or waveform/listening acceptance."} + finally: + japanese.run_frontend = original + configure_japanese_reading(pyopenjtalk, project_root=project_root, enabled=False) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--project-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + report = validate(args.project_root) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") + print(json.dumps({"summary": report["summary"], "failures": len(report["failures"]), "regressions": report["regressions"]})) + raise SystemExit(bool(report["failures"] or report["regressions"])) diff --git a/tts/japanese_dictionary.py b/tts/japanese_dictionary.py index 4c91d639..89ad2f3e 100644 --- a/tts/japanese_dictionary.py +++ b/tts/japanese_dictionary.py @@ -43,7 +43,13 @@ def _enabled() -> bool: def dictionary_runtime_status() -> dict: - return dict(_runtime_status) + from tts.japanese_reading import reading_runtime_status + status = dict(_runtime_status) + reading = reading_runtime_status() + status["reading_model"] = reading + if status.get("identity") and reading["ready"]: + status["identity"] = hashlib.sha256(repr((status["identity"], reading["identity"])).encode()).hexdigest() + return status def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, enabled: bool | None = None) -> dict: @@ -167,4 +173,6 @@ def configure_japanese_dictionary(jtalk, *, project_root: Path = PROJECT_ROOT, e logger.warning("[JapaneseDictionary] Optional dictionary unavailable: %s", exc) _runtime_status = {"state": state, "ready": state == "ready", "version": version, "identity": identity, "detail": detail} + from tts.japanese_reading import configure_japanese_reading + configure_japanese_reading(jtalk, project_root=project_root) return dictionary_runtime_status() diff --git a/tts/japanese_reading.py b/tts/japanese_reading.py new file mode 100644 index 00000000..3f0c07e8 --- /dev/null +++ b/tts/japanese_reading.py @@ -0,0 +1,55 @@ +"""Optional upstream contextual reading selection, before native NJD prosody.""" +from __future__ import annotations + +import logging +from pathlib import Path + +from config.asset_paths import PROJECT_ROOT +from config.japanese_reading import PACK_TREE, VERSION, load_reading_pack + +logger = logging.getLogger(__name__) +_runtime_status = {"state": "not_loaded", "ready": False, "identity": None, + "version": "", "detail": "Japanese context reading has not loaded."} + + +def reading_runtime_status() -> dict: + return dict(_runtime_status) + + +def configure_japanese_reading(jtalk, *, project_root: Path = PROJECT_ROOT, enabled: bool | None = None) -> dict: + global _runtime_status + was_ready = _runtime_status["ready"] + _runtime_status = {"state": "invalid", "ready": False, "identity": None, "version": "", "detail": ""} + state, identity, version = "not_installed", None, "" + detail = "Optional context-reading model is not installed; using the base Japanese frontend." + try: + if enabled is None: + from config.settings import ENABLE_JAPANESE_CONTEXT_READING + enabled = ENABLE_JAPANESE_CONTEXT_READING + root = Path(project_root) / "assets" / PACK_TREE + # Never call the Hub-loading API. Startup uses only explicitly installed, + # hash-checked local files, on CPU independently of the voice accelerator. + if was_ready or enabled and root.exists(): + jtalk.tsqyomi.unload_model() + if not enabled: + state, detail = "disabled", "Japanese context reading is disabled." + elif root.exists(): + pack = load_reading_pack(root) + jtalk.tsqyomi.load_model(model_dir=pack.root, onnx_providers=["CPUExecutionProvider"], + allow_provider_fallback=False) + if not jtalk.tsqyomi.is_model_loaded(): + raise RuntimeError("Context-reading model did not become ready") + state, identity, version = "ready", pack.identity, VERSION + detail = "Installed tsqyomi v5 context-reading model loaded on CPU." + except Exception as exc: + state, detail = "invalid", str(exc) + logger.warning("[JapaneseReading] Using the base frontend: %s", exc) + _runtime_status = {"state": state, "ready": state == "ready", "identity": identity, + "version": version, "detail": detail} + return reading_runtime_status() + + +def run_frontend(jtalk, text: str) -> list[dict]: + if _runtime_status["ready"]: + return jtalk.run_frontend(text, use_tsqyomi=True) + return jtalk.run_frontend(text) From a73b83e52cf82134f32593fa5370c610e3f228b4 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 07:48:02 +1300 Subject: [PATCH 07/10] test(settings): model absent Japanese resources independently --- tests/test_system_settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_system_settings.py b/tests/test_system_settings.py index 387d4651..a02be7d3 100644 --- a/tests/test_system_settings.py +++ b/tests/test_system_settings.py @@ -43,6 +43,8 @@ def test_context_reading_settings_show_optional_availability_and_actual_runtime( def pack_status(pack_id): if pack_id == "japanese-context-reading": return {"installed": installed, "state": "installed" if installed else "not_installed"} + if pack_id == "japanese-pronunciation": + return {"installed": False, "state": "not_installed"} return original_status(pack_id) monkeypatch.setattr(settings, "ENABLE_JAPANESE_CONTEXT_READING", enabled) @@ -50,7 +52,7 @@ def pack_status(pack_id): monkeypatch.setattr(settings, "TTS_OUTPUT_LANGUAGE", "日文") monkeypatch.setattr(asset_packages, "external_asset_pack_status", pack_status) monkeypatch.setattr(pipeline, "japanese_dictionary_status", lambda: { - "state": "not_loaded", "detail": "Not loaded", + "state": "not_loaded", "ready": False, "detail": "Not loaded", "reading_model": {"state": state, "ready": state == "ready", "detail": "Observed native state"}, }) group = next(group for group in _voice_configuration(settings) if group["id"] == "tts_japanese_reading") From e59c6fc3ec401cf372cd287678c05e3afa3970a9 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 07:48:02 +1300 Subject: [PATCH 08/10] fix(deps): update aiohttp security patch required by CI --- pyproject.toml | 2 +- uv.lock | 219 +++++++++++++++++++++++++------------------------ 2 files changed, 111 insertions(+), 110 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 028669d6..c72142bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ requires-python = ">=3.12,<3.13" # chat, work, providers — no audio). Voice and local-model stacks live in # optional extras below; exact transitive versions and builds live in uv.lock. dependencies = [ - "aiohttp==3.14.3", + "aiohttp==3.14.4", "anyio==4.14.2", "beautifulsoup4==4.14.3", "boto3==1.43.16", diff --git a/uv.lock b/uv.lock index 5426231f..55b1d182 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 5 +revision = 3 requires-python = "==3.12.*" resolution-markers = [ "sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu'", @@ -66,10 +66,10 @@ dependencies = [ { name = "safetensors" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4a/8e/ac2a9566747a93f8be36ee08532eb0160558b07630a081a6056a9f89bf1d/accelerate-1.12.0.tar.gz", hash = "sha256:70988c352feb481887077d2ab845125024b2a137a5090d6d7a32b57d03a45df6", size = 398399, upload-time = "2025-11-21T11:27:46.973Z" } wheels = [ @@ -157,7 +157,7 @@ wheels = [ [[package]] name = "aiohttp" -version = "3.14.3" +version = "3.14.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohappyeyeballs" }, @@ -169,26 +169,27 @@ dependencies = [ { name = "typing-extensions" }, { name = "yarl" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/58/d9/22ce5786ac0c1653ae8b6c23bded02c1686d11f0dbb45b31ce128e0df985/aiohttp-3.14.3.tar.gz", hash = "sha256:9491196535a88924a60afd5b5f434b5b203b6cc616250878dbdb223a8f7844bc", size = 7971213, upload-time = "2026-07-23T01:57:27.037Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/d4/eb96299230e20acf2efae207cb8d69051f1f68e357e5ea5e479bf6fb097a/aiohttp-3.14.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:39aded8c7f3b935b54aab1d8d73c70ec0ee2d3ec3b943e0e86611bc150ba47f5", size = 754690, upload-time = "2026-07-23T01:53:47.332Z" }, - { url = "https://files.pythonhosted.org/packages/88/11/e7a70a209eb9a067c0d3212b518a0134e3484f5178c7533878b6b514d469/aiohttp-3.14.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5bcb6ff3fdab1258a192679ff1a05d44f59626430aa05cd1a9d2447423599228", size = 509484, upload-time = "2026-07-23T01:53:51.159Z" }, - { url = "https://files.pythonhosted.org/packages/30/07/4bbc222cc8dbe31d4c3e8a5baad2286e4d42026ac0c570027b89afce6344/aiohttp-3.14.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:617105e2c3018ee38d0c8ce5ee3c84f621a6d8b9f723202aacaff28449ca91ee", size = 511949, upload-time = "2026-07-23T01:53:55.083Z" }, - { url = "https://files.pythonhosted.org/packages/54/b9/42e74c46b7b7c794b995bbc1f573fb48950c38b19d8600c62a6804ee2d67/aiohttp-3.14.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f631fe87a6f30df5fbe6d79640b25e4cffb38c31c7fb6f10871517b84b0f8c1a", size = 1765282, upload-time = "2026-07-23T01:53:59.662Z" }, - { url = "https://files.pythonhosted.org/packages/6b/ed/62bc4d74363ad346d518e0720363a949f63e2e23439a79eb5813d4d29bb3/aiohttp-3.14.3-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a94dbaae5ae27bd849c93570669bff91e0510f33a80805738e3de72a7be0447b", size = 1741511, upload-time = "2026-07-23T01:54:04.063Z" }, - { url = "https://files.pythonhosted.org/packages/d0/9f/181e8a8bc79e47d13c7fc4540bd7a3b729d9505609c61f392a8dd2fbfe55/aiohttp-3.14.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8f2f1c4c032c7cedd7d8da6f54c97b70266c6570c3108d3fdffee7188bb70529", size = 1810680, upload-time = "2026-07-23T01:54:09.882Z" }, - { url = "https://files.pythonhosted.org/packages/5c/9a/dec94d6ad694552fe3424e3f1928d7a606a5d9d9433a04e7ecdd9d38ae7f/aiohttp-3.14.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ea05e1f97ceea523942d9b2a7d7c0359d781d683d6b043f5943a602b14da4787", size = 1905646, upload-time = "2026-07-23T01:54:13.475Z" }, - { url = "https://files.pythonhosted.org/packages/52/b7/7cd31f29d6055bd711ae6e669367fba6f5ae9de463910a793e30556a8db7/aiohttp-3.14.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:543906c127fb1d929b95076db19b83fa2d46751006ff1e23b093aa5ac4d8db42", size = 1792122, upload-time = "2026-07-23T01:54:15.752Z" }, - { url = "https://files.pythonhosted.org/packages/66/73/10b1ef93afa61f4963c746257b70ced619cf31a4798671de5fdb2608501d/aiohttp-3.14.3-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0a5ff2dfbb9ce645fa5b8ef3e02c6c0b9cc3f6030ff863d0c51fffc50cb5541b", size = 1591127, upload-time = "2026-07-23T01:54:19.489Z" }, - { url = "https://files.pythonhosted.org/packages/49/ed/3b203fa6de1b338c14acdc06bf6ca9b043b7944f005966958c2ced932cde/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:041badb8f84396357c4d3ad26de6afd7a32b112f43d3c63045c0c8278cfd2043", size = 1725210, upload-time = "2026-07-23T01:54:24.129Z" }, - { url = "https://files.pythonhosted.org/packages/28/b7/1c2aab8c706436dcc28598452488ac9cd7c409da815237c28c27d58993e6/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:530125ee1163c4219af35dc3aa1206e541e7b31b6efc1a3f93b70a136f65d427", size = 1764848, upload-time = "2026-07-23T01:54:27.973Z" }, - { url = "https://files.pythonhosted.org/packages/54/50/94c28f08b131c4bf10984ea2c7a536c9920608bb2d6e7f95642c30cc87b7/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8653fd547c93a61aadc612007790f5555cdd18946fa48cf45e26d8ea4ea473d", size = 1777102, upload-time = "2026-07-23T01:54:31.775Z" }, - { url = "https://files.pythonhosted.org/packages/13/d4/e7d09ba7d345fb2d74440fd2fa033c5e079fac05552927705986f41a364f/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:89176250f686cb9853c0fb7ead90e639e915b84a6f43eedc2a4e7ec21f1037f0", size = 1580205, upload-time = "2026-07-23T01:54:34.518Z" }, - { url = "https://files.pythonhosted.org/packages/a3/84/072a91d68e1e1eb587985b54baab94221277f877e8ef274fc213a0ceae28/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3a26434dafe408229ff3403458ca58de24fb51936504decac49ce6755f77e59d", size = 1797219, upload-time = "2026-07-23T01:54:36.995Z" }, - { url = "https://files.pythonhosted.org/packages/e0/eb/aad34e897e668424d6e995da5dff8a4a09af93363d3392488772957a63aa/aiohttp-3.14.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d1558173930a5a8d3069cee5c92fc91c87c4dbcb099debbb3622053717145a19", size = 1768629, upload-time = "2026-07-23T01:54:40.103Z" }, - { url = "https://files.pythonhosted.org/packages/b6/2b/6bb88ddba0fecd9122aa3ebcad25996cf6c083a4a7040dbb3a4f97972af6/aiohttp-3.14.3-cp312-cp312-win32.whl", hash = "sha256:16100ad3ab8d649fdfbee87602d9d2dcdca9df0b9eda8a1b5fdc0d41f96da559", size = 451481, upload-time = "2026-07-23T01:54:42.547Z" }, - { url = "https://files.pythonhosted.org/packages/76/9b/f2f8f108da17ecef2cc3efc424e8b7ad3782b1a8360f7b8eae8ced84f6ea/aiohttp-3.14.3-cp312-cp312-win_amd64.whl", hash = "sha256:33a2d7c28d33797a2e99923dffa63f83d908a19b6bf26cfe80fa790aa5e1a75a", size = 476845, upload-time = "2026-07-23T01:54:44.853Z" }, - { url = "https://files.pythonhosted.org/packages/3e/44/28dac80a8941b604f4da10ce21097614ca1bf905ce93dca28d8d7de9c1e7/aiohttp-3.14.3-cp312-cp312-win_arm64.whl", hash = "sha256:362a3fd481769cac1a824514bcd86fda51c65e8fe6e051099e008fddde6db17c", size = 448050, upload-time = "2026-07-23T01:54:47.087Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/93/2f/6a91adaa2dc26877d6ed2f54c0370c8910f019db7d77c5c6a194611e93ea/aiohttp-3.14.4.tar.gz", hash = "sha256:831fc5bd39ec2517851e348f613ddb5447a47cf4b71cb09845af7ad7ed45d8f9", size = 8077839, upload-time = "2026-10-05T17:43:48.309Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/02/db5935d45347fa8ede56b777930740fb0e72bbb4473ddb6ff8541f6f2afa/aiohttp-3.14.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:55b0b335982b0db7117dff7f6a8e3aad43c4105f9e392ca04f9fc8d958a95cce", size = 817689, upload-time = "2026-10-05T17:38:50.69Z" }, + { url = "https://files.pythonhosted.org/packages/e6/56/f00aa46127e6342f019d567853eeeb932e04fb9b9424b5fb981ad68f4178/aiohttp-3.14.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2d0d8e435582b0d53009dac99e26a3d2be932ed7c9dbfcfc70f3fce1cf943bd5", size = 549599, upload-time = "2026-10-05T17:38:52.525Z" }, + { url = "https://files.pythonhosted.org/packages/3f/bc/825c09b09962253a2103e4a88c04374ee939c0c8ea31c270daa0308efb07/aiohttp-3.14.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bf734016932d68e1324cfb638cac7a0866a83933a33badf15e836a7e24efb8c0", size = 548132, upload-time = "2026-10-05T17:38:54.629Z" }, + { url = "https://files.pythonhosted.org/packages/40/80/8a1bc8036540fe7cb38219cc5c0aa67d32e9068165725e8199ea8200b08b/aiohttp-3.14.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:322e3d741b3133bcbb981a41d4f9b8ac3545c18de49c662c2b1f4e1c770b98ad", size = 1898485, upload-time = "2026-10-05T17:38:56.439Z" }, + { url = "https://files.pythonhosted.org/packages/3c/fe/65b464d8520ace0d65b4ca69bc9abe175f842c8ce8d1522d363d9669903f/aiohttp-3.14.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:15a3bb5f90a4e515071f3617a45dfe26560beb834a6b6e108cdf4bc9e9719ec3", size = 1875334, upload-time = "2026-10-05T17:38:58.536Z" }, + { url = "https://files.pythonhosted.org/packages/be/51/9c583be502720e927560a8414f4dfb7c7de41945e1a30ccf7003b93c6f85/aiohttp-3.14.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6c044e52df1a466af82cad3518c92ea80cc4ddcfea86966ff083bb34bab29bf4", size = 1937772, upload-time = "2026-10-05T17:39:00.376Z" }, + { url = "https://files.pythonhosted.org/packages/a2/3d/b56512df7ea9cbb5f59099a6b375088dd578356a5a3fb27198ab82e19832/aiohttp-3.14.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d613d7d51bf06fe9a5e3aab86ce06d835bb0ceb1a31ea9e8766df4a403ae62a6", size = 2066220, upload-time = "2026-10-05T17:39:02.54Z" }, + { url = "https://files.pythonhosted.org/packages/11/6c/a7a42701d9ea319140a3d44a24ec72a00b0b46d1b899b26d8a705e65bdd8/aiohttp-3.14.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5e93ab6e6330dce40f5b43996f1fc5f2543296d263a5e4b59ff09e73870b7d8", size = 1922895, upload-time = "2026-10-05T17:39:04.638Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/f8337bd824ca3fe3968d34dc5037ace1c4872551c4621f8266239311d301/aiohttp-3.14.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:20134dc8e68c67e7478124d516449428684c0545b2d3faa575691c8bcbd56678", size = 1712589, upload-time = "2026-10-05T17:39:06.671Z" }, + { url = "https://files.pythonhosted.org/packages/fe/a6/d20e2c940b592d7c4a21690889dbff64fe0ca3facd9190696368694e1488/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f0daef5ef012369dddc391c61f604111424aa9a96313a69fb899018b09d1b01e", size = 1862251, upload-time = "2026-10-05T17:39:08.523Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e4/d50edd5ae5d024b380a8e892e333840bb22967a9eb9c8492108a403f678a/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:19290a108de0718b73bc69b86e788975c6a59855c671ebe2a98a07a6f968328f", size = 1895155, upload-time = "2026-10-05T17:39:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/c3/d3/f36f2931f807ccf8437ec15e8f8bfaa4748d4792639401cc767a866abaa0/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1494877625baeffab669f4a25a09ec4eea721b49e35363081a2a8e4231fe141f", size = 1906623, upload-time = "2026-10-05T17:39:12.411Z" }, + { url = "https://files.pythonhosted.org/packages/5d/0a/94821b711493142bf5e11c27b0d7502776778f7792c77ecc6647357d20c1/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:a779ce4bf099ce4ad2cdfed62f1ca536d435f53237c54ff40172fddada24d1b5", size = 1698932, upload-time = "2026-10-05T17:39:14.446Z" }, + { url = "https://files.pythonhosted.org/packages/d9/76/3d5a0d28362f0053c1c457024749911932481e8fd105fddf8305b1f93c81/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0b4cec9fca876e2d4f4c6cb32b177390132d04618867689f43fefabf3f5c3980", size = 1934528, upload-time = "2026-10-05T17:39:16.354Z" }, + { url = "https://files.pythonhosted.org/packages/a7/51/9784a21dc05d2e0f6acaf864f16db53dab0fbd7b0753cef168601eacb012/aiohttp-3.14.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:217707313e23aedde9bea67cf8365c7b138b549ae179121ef3f476cf67e3173a", size = 1901984, upload-time = "2026-10-05T17:39:18.299Z" }, + { url = "https://files.pythonhosted.org/packages/44/4f/f98c88703e6d620243aa08473a946ca4e3756b6288690e38f0d9f9c1d007/aiohttp-3.14.4-cp312-cp312-win32.whl", hash = "sha256:18a9fb9a3f6e6e63f4d27f9adeef313a2d3c69a84d31170a844623824e722534", size = 480278, upload-time = "2026-10-05T17:39:20.206Z" }, + { url = "https://files.pythonhosted.org/packages/b7/bc/8555013f84b53e4fdde423131b641af50e822c6d7d9118b24c52b31f6900/aiohttp-3.14.4-cp312-cp312-win_amd64.whl", hash = "sha256:69dcb02d33dfe415d5ee342cc63b7d320efcaf4b761701c7bd7bfb91c715481a", size = 506317, upload-time = "2026-10-05T17:39:22.706Z" }, + { url = "https://files.pythonhosted.org/packages/21/3f/ca5bad7bfdaa7ddca053a044a1c7c0d4f3ad9d1f12f96dcab10bfb9c50e7/aiohttp-3.14.4-cp312-cp312-win_arm64.whl", hash = "sha256:d36b0263e7c2fbf1750b9f35d4e7e48b0442d8c45e4a89e3e74168821fb014bb", size = 487683, upload-time = "2026-10-05T17:39:24.482Z" }, + { url = "https://files.pythonhosted.org/packages/2b/27/5e1f8446545be98e9416af59c396b1ed1f9f3294afb0a40ee3a709889273/aiohttp-3.14.4-py3-none-any.whl", hash = "sha256:5c6758ba62aea282c537179cfc8474a90f2b5f7b089cc5ff66d8920d86a9bfdd", size = 279487, upload-time = "2026-10-05T17:43:44.905Z" }, ] [[package]] @@ -595,7 +596,7 @@ requires-dist = [ { name = "aec-audio-processing", marker = "sys_platform != 'linux' and extra == 'voice'", specifier = "==1.0.1" }, { name = "agent-client-protocol", marker = "extra == 'acp'", specifier = "==0.12.1" }, { name = "agent-client-protocol", marker = "extra == 'dev'", specifier = "==0.12.1" }, - { name = "aiohttp", specifier = "==3.14.3" }, + { name = "aiohttp", specifier = "==3.14.4" }, { name = "amadeus", extras = ["local-models"], marker = "platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'local-mps'" }, { name = "amadeus", extras = ["local-models"], marker = "(sys_platform == 'linux' and extra == 'local-cu128') or (sys_platform == 'win32' and extra == 'local-cu128')" }, { name = "amadeus", extras = ["local-models"], marker = "sys_platform == 'win32' and extra == 'local-rocm'" }, @@ -1633,8 +1634,8 @@ name = "httpcore2" version = "2.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "h11" }, - { name = "truststore" }, + { name = "h11", marker = "sys_platform != 'emscripten' or extra == 'extra-7-amadeus-local-cpu' or extra == 'extra-7-amadeus-local-cu124' or extra == 'extra-7-amadeus-local-cu128' or extra == 'extra-7-amadeus-torch-cpu' or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm')" }, + { name = "truststore", marker = "sys_platform != 'emscripten' or extra == 'extra-7-amadeus-local-cpu' or extra == 'extra-7-amadeus-local-cu124' or extra == 'extra-7-amadeus-local-cu128' or extra == 'extra-7-amadeus-torch-cpu' or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/be/ad/f4f0e57345f1870f3e8cb624e058d7eca6e5a27d33bcc3311d9b618734cd/httpcore2-2.12.0.tar.gz", hash = "sha256:9293522bba0aa7c4c8e9e3f040c16575bd8868e155a77fa30c7a9085a5eae648", size = 67548, upload-time = "2026-08-18T13:22:08.211Z" } wheels = [ @@ -2471,7 +2472,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-cublas-cu12", version = "12.4.5.8", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cublas-cu12", version = "12.4.5.8", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/9f/fd/713452cd72343f682b1c7b9321e23829f00b842ceaedcda96e742ea0b0b3/nvidia_cudnn_cu12-9.1.0.70-py3-none-manylinux2014_x86_64.whl", hash = "sha256:165764f44ef8c61fcdfdfdbe769d687e06374059fbb388b6c89ecb0e28793a6f", size = 664752741, upload-time = "2024-04-22T15:24:15.253Z" }, @@ -2502,7 +2503,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-cublas-cu12", version = "12.8.3.14", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cublas-cu12", version = "12.8.3.14", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/c1/2e/ec5dda717eeb1de3afbbbb611ca556f9d6d057470759c6abd36d72f0063b/nvidia_cudnn_cu12-9.7.1.26-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:848a61d40ef3b32bd4e1fadb599f0cf04a4b942fbe5fb3be572ad75f9b8c53ef", size = 725862213, upload-time = "2025-02-06T22:14:57.169Z" }, @@ -2518,7 +2519,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/7a/8a/0e728f749baca3fbeffad762738276e5df60851958be7783af121a7221e7/nvidia_cufft_cu12-11.2.1.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5dad8008fc7f92f5ddfa2101430917ce2ffacd86824914c82e28990ad7f00399", size = 211422548, upload-time = "2024-06-18T19:33:39.396Z" }, @@ -2552,7 +2553,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/72/95/6157cb45a49f5090a470de42353a22a0ed5b13077886dca891b4b0e350fe/nvidia_cufft_cu12-11.3.3.41-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68509dcd7e3306e69d0e2d8a6d21c8b25ed62e6df8aac192ce752f17677398b5", size = 193108626, upload-time = "2025-01-23T17:55:49.192Z" }, @@ -2633,9 +2634,9 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-cublas-cu12", version = "12.4.5.8", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "nvidia-cusparse-cu12", version = "12.3.1.170", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cublas-cu12", version = "12.4.5.8", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cusparse-cu12", version = "12.3.1.170", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/46/6b/a5c33cf16af09166845345275c34ad2190944bcc6026797a39f8e0a282e0/nvidia_cusolver_cu12-11.6.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d338f155f174f90724bbde3758b7ac375a70ce8e706d70b018dd3375545fc84e", size = 127634111, upload-time = "2024-06-18T19:35:01.793Z" }, @@ -2671,9 +2672,9 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-cublas-cu12", version = "12.8.3.14", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "nvidia-cusparse-cu12", version = "12.5.7.53", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cublas-cu12", version = "12.8.3.14", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-cusparse-cu12", version = "12.5.7.53", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/8c/ce/4214a892e804b20bf66d04f04a473006fc2d3dac158160ef85f1bc906639/nvidia_cusolver_cu12-11.7.2.55-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:0fd9e98246f43c15bee5561147ad235dfdf2d037f5d07c9d41af3f7f72feb7cc", size = 260094827, upload-time = "2025-01-23T17:58:17.586Z" }, @@ -2689,7 +2690,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.4.127", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/96/a9/c0d2f83a53d40a4a41be14cea6a0bf9e668ffcf8b004bd65633f433050c0/nvidia_cusparse_cu12-12.3.1.170-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9d32f62896231ebe0480efd8a7f702e143c98cfaa0e8a76df3386c1ba2b54df3", size = 207381987, upload-time = "2024-06-18T19:35:32.989Z" }, @@ -2723,7 +2724,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux'", ] dependencies = [ - { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "nvidia-nvjitlink-cu12", version = "12.8.61", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/2e/a2/313db0453087f5324a5900380ca2e57e050c8de76f407b5e11383dc762ae/nvidia_cusparse_cu12-12.5.7.53-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d869c6146ca80f4305b62e02d924b4aaced936f8173e3cef536a67eed2a91af1", size = 291963692, upload-time = "2025-01-23T17:59:40.325Z" }, @@ -2957,7 +2958,7 @@ name = "opencv-python" version = "4.11.0.86" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, + { name = "numpy", marker = "sys_platform == 'win32' or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/17/06/68c27a523103dad5837dc5b87e71285280c4f098c60e4fe8a8db6486ab09/opencv-python-4.11.0.86.tar.gz", hash = "sha256:03d60ccae62304860d232272e4a4fda93c39d595780cb40b161b310244b736a4", size = 95171956, upload-time = "2025-01-16T13:52:24.737Z" } wheels = [ @@ -3068,10 +3069,10 @@ dependencies = [ { name = "safetensors" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "tqdm" }, { name = "transformers" }, ] @@ -3552,10 +3553,10 @@ dependencies = [ { name = "pyyaml" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchmetrics" }, { name = "tqdm" }, { name = "typing-extensions" }, @@ -3725,13 +3726,13 @@ name = "rocm" version = "7.2.1" source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/rocm-7.2.1.tar.gz" } dependencies = [ - { name = "rocm-sdk-core", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "rocm-sdk-core", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { hash = "sha256:9084902eaa69213a00a90784ad89e6e5fe73c702df0cc6cc3a70d777c7a6142b" } [package.optional-dependencies] libraries = [ - { name = "rocm-sdk-libraries-custom", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "rocm-sdk-libraries-custom", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] [package.metadata] @@ -3775,10 +3776,10 @@ dependencies = [ { name = "einops" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/71/38/74783585b1f0282fddd3faf1abd6dd20977255c27e31737eced2d7ec05f1/rotary_embedding_torch-0.8.9.tar.gz", hash = "sha256:b213f153cad1d108064d930544fb3af678d56515893d3f869a7a146f87997e3f", size = 7497, upload-time = "2025-07-27T01:26:14.675Z" } wheels = [ @@ -3943,10 +3944,10 @@ dependencies = [ { name = "scipy" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "tqdm" }, { name = "transformers" }, { name = "typing-extensions" }, @@ -3998,18 +3999,18 @@ dependencies = [ { name = "packaging" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.7.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.7.0", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torchaudio", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torchaudio", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (platform_machine != 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_python_implementation != 'CPython' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (platform_python_implementation != 'CPython' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torchaudio", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'aarch64' and platform_python_implementation == 'CPython' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'aarch64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (platform_python_implementation != 'CPython' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torchaudio", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchaudio-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torchaudio", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchaudio-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/32/d3/e31f526482782764aa4f70e20fd4545cf2e4a81a60b6fb0f089f6d107991/silero_vad-6.2.1.tar.gz", hash = "sha256:b23062b0e39fad17b1266fc23c1e7b4290219dbe82ce08510889e32f681f4b3b", size = 28913811, upload-time = "2026-02-24T08:41:59.329Z" } wheels = [ @@ -4436,6 +4437,9 @@ source = { registry = "https://pypi.org/simple" } resolution-markers = [ "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu'", "sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu'", + "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", + "sys_platform == 'emscripten' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", + "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "platform_machine == 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "platform_machine != 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", @@ -4456,10 +4460,10 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", ] dependencies = [ - { name = "filelock", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "fsspec", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "jinja2", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "networkx", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "filelock", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "fsspec", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "jinja2", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "networkx", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "nvidia-cublas-cu12", version = "12.6.4.1", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "nvidia-cuda-cupti-cu12", version = "12.6.80", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "nvidia-cuda-nvrtc-cu12", version = "12.6.77", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, @@ -4474,10 +4478,10 @@ dependencies = [ { name = "nvidia-nccl-cu12", version = "2.26.2", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "nvidia-nvjitlink-cu12", version = "12.6.85", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "nvidia-nvtx-cu12", version = "12.6.77", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "setuptools", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "sympy", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "setuptools", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "sympy", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "triton", version = "3.3.0", source = { registry = "https://pypi.org/simple" }, marker = "(platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-mps') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine == 'x86_64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (platform_machine != 'x86_64' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "typing-extensions", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "typing-extensions", marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/aa/5e/ac759f4c0ab7c01feffa777bd68b43d2ac61560a9770eeac074b450f81d4/torch-2.7.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:36a6368c7ace41ad1c0f69f18056020b6a5ca47bedaca9a2f3b578f5a104c26c", size = 99013250, upload-time = "2025-04-23T14:35:15.589Z" }, @@ -4557,20 +4561,17 @@ name = "torch" version = "2.9.1+rocm7.2.1" source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" } resolution-markers = [ - "sys_platform == 'darwin'", "sys_platform == 'win32'", - "sys_platform == 'emscripten'", - "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "filelock", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "fsspec", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "jinja2", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "networkx", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "rocm", extra = ["libraries"], marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "setuptools", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "sympy", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "typing-extensions", marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "filelock", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "fsspec", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "jinja2", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "networkx", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "rocm", extra = ["libraries"], marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "setuptools", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "sympy", version = "1.14.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "typing-extensions", marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:e88bf270163b48f7f27f7ea3db5ffb3be4ba107301933022bcb3c6ddedfeeabb" }, @@ -4613,10 +4614,10 @@ dependencies = [ { name = "einops" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/93/03/33fa0c18ed8300f1565d39fc1dc6b076a85f5e70fddab6816ac75e3e3322/torch_einops_utils-0.1.22.tar.gz", hash = "sha256:e641120b93c098383fc6c088cd6bdcfe628a54b492773b12b4a92c79c75e03a0", size = 21033, upload-time = "2026-08-27T16:41:57.36Z" } wheels = [ @@ -4694,6 +4695,9 @@ source = { registry = "https://pypi.org/simple" } resolution-markers = [ "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu'", "sys_platform != 'darwin' and sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu'", + "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", + "sys_platform == 'emscripten' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", + "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "platform_machine == 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "platform_machine != 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", "sys_platform == 'darwin' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", @@ -4714,7 +4718,7 @@ resolution-markers = [ "platform_machine != 'aarch64' and sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu'", ] dependencies = [ - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://files.pythonhosted.org/packages/dd/b9/66dd7c4e16e8e6dcc52b4702ba7bbace589972b3597627d39d9dc3aa5fdd/torchaudio-2.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:65b4fc9b7f28367f918b02ae4db4290457bc4fdd160f22b7d684e93ab8dcb956", size = 1846733, upload-time = "2025-04-23T14:47:01.068Z" }, @@ -4762,13 +4766,10 @@ name = "torchaudio" version = "2.9.1+rocm7.2.1" source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchaudio-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" } resolution-markers = [ - "sys_platform == 'darwin'", "sys_platform == 'win32'", - "sys_platform == 'emscripten'", - "sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'linux' and sys_platform != 'win32'", ] dependencies = [ - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] wheels = [ { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchaudio-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:62835605995c7812a3224733abf08e222afddb7a487c493521e613ca46e578ab" }, @@ -4787,10 +4788,10 @@ dependencies = [ { name = "packaging" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/11/3d/d14914f15b19ce971215973305d30b9a673b7fa0c6796076e149c11765a0/torchmetrics-1.5.0.tar.gz", hash = "sha256:c18e68bab4104ad7d2285af601ddc6dc04f9f3b7cafaa8ad13fa1dcc539e33b6", size = 521148, upload-time = "2024-10-18T19:42:08.781Z" } wheels = [ @@ -4802,9 +4803,9 @@ name = "torchvision" version = "0.24.1+rocm7.2.1" source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchvision-0.24.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" } dependencies = [ - { name = "numpy", marker = "sys_platform != 'linux'" }, - { name = "pillow", marker = "sys_platform != 'linux'" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "sys_platform != 'linux'" }, + { name = "numpy", marker = "sys_platform == 'win32'" }, + { name = "pillow", marker = "sys_platform == 'win32'" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "sys_platform == 'win32'" }, ] wheels = [ { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torchvision-0.24.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5c34d76a2f01fb97e1e0f224bd97fc3a58ec1ee1ef2376af2d72086e9aca34c" }, @@ -5050,8 +5051,8 @@ name = "windows-capture" version = "2.0.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "numpy" }, - { name = "opencv-python" }, + { name = "numpy", marker = "sys_platform == 'win32' or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "opencv-python", marker = "sys_platform == 'win32' or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/85/00/326a51bc2b27bc53e2a3e6d0c8f320af7e65f8315fb0d1d149699ca45995/windows_capture-2.0.1.tar.gz", hash = "sha256:2c2b394f4587b067cf520456f7f64c11b00380851b077a4d061fe5356a9a7645", size = 148413, upload-time = "2026-08-08T16:07:07.162Z" } wheels = [ @@ -5078,10 +5079,10 @@ dependencies = [ { name = "packaging" }, { name = "torch", version = "2.6.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.6.0+cu124", source = { registry = "https://download.pytorch.org/whl/cu124" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.7.0", source = { registry = "https://pypi.org/simple" }, marker = "(sys_platform != 'linux' and sys_platform != 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform != 'linux' and sys_platform != 'win32' and extra != 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-mps' and extra != 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'linux' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra != 'extra-7-amadeus-local-cpu' and extra != 'extra-7-amadeus-local-cu124' and extra != 'extra-7-amadeus-local-cu128' and extra != 'extra-7-amadeus-local-rocm' and extra != 'extra-7-amadeus-torch-cpu') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra != 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'linux' and extra == 'extra-7-amadeus-torch-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cpu') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch", version = "2.7.0+cu128", source = { registry = "https://download.pytorch.org/whl/cu128" }, marker = "(sys_platform == 'linux' and extra == 'extra-7-amadeus-local-cu128') or (sys_platform == 'win32' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, - { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform != 'linux' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu')" }, + { name = "torch", version = "2.9.1+rocm7.2.1", source = { url = "https://repo.radeon.com/rocm/windows/rocm-rel-7.2.1/torch-2.9.1%2Brocm7.2.1-cp312-cp312-win_amd64.whl" }, marker = "(sys_platform == 'win32' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-rocm' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu124') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cpu' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-cu128') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu124' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-mps') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-cu128' and extra == 'extra-7-amadeus-torch-cpu') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-local-rocm') or (extra == 'extra-7-amadeus-local-mps' and extra == 'extra-7-amadeus-torch-cpu')" }, { name = "torch-einops-utils" }, ] sdist = { url = "https://files.pythonhosted.org/packages/17/d4/9f1bea172e9ccc4736f8433be52d9c7ef4e1e4093ff0ca5a95a203d49aff/x_transformers-2.19.6.tar.gz", hash = "sha256:f791d5bdc7399588fdfa756b73d3f836a39f8bb703595e84e5b4b367f2fe16c3", size = 125610, upload-time = "2026-05-16T14:42:57.326Z" } @@ -5091,31 +5092,31 @@ wheels = [ [[package]] name = "yarl" -version = "1.24.5" +version = "1.25.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "multidict" }, { name = "propcache" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/31/33/ebe9e3d1f86c7a0b51094c0a146392045ca1631d2664889539dec8088a33/yarl-1.24.5.tar.gz", hash = "sha256:e81b83143bee16329c23db3c1b2d82b29892fcbcb849186d2f6e98a5abe9a57f", size = 228679, upload-time = "2026-07-20T02:07:45.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/1b/84/71d051c850b5af41d168c679d9eb67eb7c55283ac4ee131673edf134bc4e/yarl-1.24.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d693396e5aea78db03decd60aec9ece16c9b40ba00a587f089615ff4e718a81d", size = 136035, upload-time = "2026-07-20T02:05:25.489Z" }, - { url = "https://files.pythonhosted.org/packages/03/4d/8ad27f9a1b7e69313cca5d695b925b48efe51208d3490e0844bae97cabc0/yarl-1.24.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3363fcc96e665878946ad7a106b9a13eac0541766a690ef287c0232ac768b6ec", size = 97642, upload-time = "2026-07-20T02:05:27.429Z" }, - { url = "https://files.pythonhosted.org/packages/ea/b4/05b4131c407006cd1e410e9c6539f16a0945724677e5364447313c15ea3e/yarl-1.24.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9d399bdcfb4a0f659b9b3788bbc89babe63d9a6a65aacdf4d4e7065ff2e6316c", size = 97323, upload-time = "2026-07-20T02:05:29.441Z" }, - { url = "https://files.pythonhosted.org/packages/20/16/e618c875c73e0e39611f20a581b3d5e8d59b8857bf001bee3263044c6deb/yarl-1.24.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90333fd89b43c0d08ac85f3f1447593fc2c66de18c3d6378d7125ea118dc7a54", size = 107741, upload-time = "2026-07-20T02:05:31.367Z" }, - { url = "https://files.pythonhosted.org/packages/d9/9a/c4defeaf3ed33fcb346aacf9c6e971a8d4e2bde04a0310e79abb208e7965/yarl-1.24.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:665b0a2c463cc9423dd647e0bfd9f4ccc9b50f768c55304d5e9f80b177c1de12", size = 103570, upload-time = "2026-07-20T02:05:33.303Z" }, - { url = "https://files.pythonhosted.org/packages/5f/e7/0e0e0de5865ebd5914537ef486f36c727a59865c3ac0cf5ff1b32aececbf/yarl-1.24.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e006d3a974c4ee19512e5f058abedb6eef36a5e553c14812bdeba1758d812e6d", size = 115815, upload-time = "2026-07-20T02:05:35.292Z" }, - { url = "https://files.pythonhosted.org/packages/2b/27/ca56b700cb170aba25a3893b75355b213935657dc5714d2383354a270e62/yarl-1.24.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e7d42c531243450ef0d4d9c172e7ed6ef052640f195629065041b5add4e058d1", size = 116025, upload-time = "2026-07-20T02:05:37.503Z" }, - { url = "https://files.pythonhosted.org/packages/d6/d0/d56c859b8222116f5d68459199f48359e0bf121b6f65a69bf329b3602ba0/yarl-1.24.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f08c7513ecef5aad65687bfdf6bc601ae9fccd04a42904501f8f7141abad9eb9", size = 109835, upload-time = "2026-07-20T02:05:39.506Z" }, - { url = "https://files.pythonhosted.org/packages/70/a2/3a35557e4d1a79425040eba202ccaf08bdc8717680fc77e2498a1ad2e0a5/yarl-1.24.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95b17fe34ed802f17e205112e6e10db92275c34fee290aa9bdc55a9c724027", size = 108884, upload-time = "2026-07-20T02:05:41.584Z" }, - { url = "https://files.pythonhosted.org/packages/e4/35/ef4c26356b7913c68983bac2d72a4212b3347af551cb8d250b99b5ed7b7f/yarl-1.24.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:56b149b22de33b23b0c6077ab9518c6dcb538ad462e1830e68d06591ccf6e38b", size = 107308, upload-time = "2026-07-20T02:05:43.697Z" }, - { url = "https://files.pythonhosted.org/packages/d5/91/ff0dc66c2ccf3e0153ab97ff61eabab4400e6a5264af427ab30cd69f1857/yarl-1.24.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:a8fe66b8f300da93798025a785a5b90b42f3810dc2b72283ff84a41aaaebc293", size = 103646, upload-time = "2026-07-20T02:05:45.895Z" }, - { url = "https://files.pythonhosted.org/packages/74/f0/33b9271c7f881766359d58266fa0811d2e5210ed860e28da7dc6d7786344/yarl-1.24.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:377fe3732edbaf78ee74efdf2c9f49f6e99f20e7f9d2649fda3eb4badd77d76e", size = 115305, upload-time = "2026-07-20T02:05:47.832Z" }, - { url = "https://files.pythonhosted.org/packages/ef/65/fd79fb1868c4a80db8661091de525bf430f63c3bea1b20e8b6a84fc7d359/yarl-1.24.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:e8ffa78582120024f476a611d7befc123cee59e47e8309d470cf667d806e613b", size = 108404, upload-time = "2026-07-20T02:05:49.604Z" }, - { url = "https://files.pythonhosted.org/packages/ff/ba/dbabe6b262f17a816c70cfc09558dbf03ece3ec76684d02f911a3d3a189c/yarl-1.24.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:daba5e594f06114e37db186efd2dd916609071e59daca901a0a2e71f02b142ce", size = 115940, upload-time = "2026-07-20T02:05:51.741Z" }, - { url = "https://files.pythonhosted.org/packages/a5/43/fab2d1dad9d340a268cdde63756a123d069723efff6a372d123fa74a9517/yarl-1.24.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:65be18ec59496c13908f02a2472751d9ef840b4f3fb5726f129306bf6a2a7bba", size = 110006, upload-time = "2026-07-20T02:05:53.554Z" }, - { url = "https://files.pythonhosted.org/packages/c4/27/41eb51bbd1b8d89546b83897cfb0164f1e109304fd408dbb151b639eec0f/yarl-1.24.5-cp312-cp312-win_amd64.whl", hash = "sha256:a929d878fec099030c292803b31e5d5540a7b6a31e6a3cc76cb4685fc2a2f51b", size = 97618, upload-time = "2026-07-20T02:05:55.57Z" }, - { url = "https://files.pythonhosted.org/packages/3c/25/b2553764b3d65db711d8f45416351ec4f420847558eb669edcbcaadf5780/yarl-1.24.5-cp312-cp312-win_arm64.whl", hash = "sha256:7ce27823052e2013b597e0c738b13e7e36b8ccb9400df8959417b052ab0fd92c", size = 93018, upload-time = "2026-07-20T02:05:57.554Z" }, - { url = "https://files.pythonhosted.org/packages/61/02/962c1cbfc401a30c1d034dc67ff395f64b52302c6d62de556c1fca99acc0/yarl-1.24.5-py3-none-any.whl", hash = "sha256:a33700d13d9b7d84fd10947b09ff69fb9a792e519c8cb9764a3ca70baa6c23a7", size = 58612, upload-time = "2026-07-20T02:07:43.461Z" }, +sdist = { url = "https://files.pythonhosted.org/packages/75/16/e8be8e2fb175bbf41a0680381a319f1199fae256588241a2ac8677eafb49/yarl-1.25.1.tar.gz", hash = "sha256:03dd38de09bc213e9a8b29761eec33ee1d5318dac0e49d8af36e4d27830e23a7", size = 246245, upload-time = "2026-09-15T19:35:02.264Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/75/b3/cd32ac66ae622b854c2df0ac52106dda220d361b65a64fde7d5b3684aa3f/yarl-1.25.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:94d7aa6debf92a1dd14cb5280b083a764169a13cfb23a452111160274ed989f4", size = 144798, upload-time = "2026-09-15T19:31:01.821Z" }, + { url = "https://files.pythonhosted.org/packages/61/fb/a2c52a8007c2051ba74662afb112ecf3d00346af4c25e33df9d80fd14fb8/yarl-1.25.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83d4a37e4b95da4d8bda930d6d35b75b4cdadbacbb4980cae290ea3100b5d51d", size = 104583, upload-time = "2026-09-15T19:31:04.05Z" }, + { url = "https://files.pythonhosted.org/packages/be/dd/ee38aec8e09fdf957e50d4085453fbe202f56c6c3b4cf07b81cdb4f09ee9/yarl-1.25.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e029648f9c951db30e98a7d7ec90835db88ec4b32820efe2a9bdc2287e032eb6", size = 104325, upload-time = "2026-09-15T19:31:06.338Z" }, + { url = "https://files.pythonhosted.org/packages/1e/b3/058dbfb1857b484c9cf9cc135659f50b85ce66e03c99e44dc2f7b6161f55/yarl-1.25.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d781294bb815ecb5ea57ff6bbf8038e0a31a95fdf3e1788f66e0dc100d64b58", size = 115358, upload-time = "2026-09-15T19:31:08.593Z" }, + { url = "https://files.pythonhosted.org/packages/db/39/29693446cf0cf6b15a0e2f75a5d40f93c56819b05b0622196f45e95b5cc0/yarl-1.25.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e12c538e00e7c1b286a07061046b90e8124e6a9793efae2c70db6a4aad07faad", size = 107658, upload-time = "2026-09-15T19:31:10.802Z" }, + { url = "https://files.pythonhosted.org/packages/86/b3/3c4dd7e1af43b931fba95e0a722737f2ea94a6d199c802585282831d7abd/yarl-1.25.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7e4de3ac4adbad3d0bc7c6f4360a7dbff5de2f15e3b723be3198074e17fd9c40", size = 122660, upload-time = "2026-09-15T19:31:12.84Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b5/1b60dbc3cfc9c5712b15148c206748f2bc93953ffdbe25ea75b63dfc89c9/yarl-1.25.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:419f392a1da624877975709e3864dfe833af6cc7671b39318086d456e288380c", size = 126506, upload-time = "2026-09-15T19:31:15.088Z" }, + { url = "https://files.pythonhosted.org/packages/bc/7b/ca212cbe170ac8b96e45317ecbcf9c3c3ecf0cdec98d5b088a9c4088929b/yarl-1.25.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6f117789d22dce188e5754e8bc65b7e6ebf8cb73963b9fa761f672a5883769d", size = 117050, upload-time = "2026-09-15T19:31:17.241Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c3/72b4938cdbe619ad71ac156182faef4908846b84dc3ca4dbb4c4e6f84014/yarl-1.25.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:80e47012e730da131c9f059c80936783f9659aae22dc31c03c0595590d11ed54", size = 114174, upload-time = "2026-09-15T19:31:19.294Z" }, + { url = "https://files.pythonhosted.org/packages/e8/43/268717870f9ba0cc9701a95181587f6dc8c5f387aab4aeecc83158f38a79/yarl-1.25.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e80f557716fd765439577131e526b8942ffc2c07bdbc5e39fa62f660ba1e963f", size = 114944, upload-time = "2026-09-15T19:31:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/da/84/baa5bf504d51fe062c4bcaf62936da97fffb43285978d0b39984824231fd/yarl-1.25.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:f61964f235a43738bfac50da46fc4254943a7eea3051aeb0b6fc7c992c29fadc", size = 108263, upload-time = "2026-09-15T19:31:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/a4/28/779a2ed9e0152a601a27039bed9aead3f0b79797a67e2c44bfa444622dd8/yarl-1.25.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e546fe1d4a93ebc2910f0d768baff19faa09843ab3f2036a67ed6e69fae4419d", size = 122184, upload-time = "2026-09-15T19:31:25.343Z" }, + { url = "https://files.pythonhosted.org/packages/f8/1f/118e9e5b8f07694d63fd3222e801d7782270003f1a222aa798df3f8d5933/yarl-1.25.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:cce0727fd5ac04d372fa9bbfde9febc2bcf209aadfcf0468e45dec72719895d1", size = 114001, upload-time = "2026-09-15T19:31:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/a4cf1cf372313734b17996d4007f9f73596e7a178b9485802e5494ecf484/yarl-1.25.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af4ea5b37403ef4e30f3927eaed540db942bde01d8d3ff083527c0704d1c9c68", size = 120565, upload-time = "2026-09-15T19:31:29.47Z" }, + { url = "https://files.pythonhosted.org/packages/05/79/ad94f93ca731bc9e44d321833ab96b82a4f9f5f63cf773f81a4aeea5ecc1/yarl-1.25.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:68782fdb4027b8d1eee25ec35e9a6db05e863b899eb0310b3a33b6c3fef55707", size = 117060, upload-time = "2026-09-15T19:31:31.367Z" }, + { url = "https://files.pythonhosted.org/packages/bb/cc/51a7b4abf4ac593b8e7eb3794b28e5a35ae26eed8bc04787628d215af82f/yarl-1.25.1-cp312-cp312-win_amd64.whl", hash = "sha256:7d575b54cb3863ef9bc290ea4b009999d55dc237326131e4853cf33e888fee03", size = 102593, upload-time = "2026-09-15T19:31:33.329Z" }, + { url = "https://files.pythonhosted.org/packages/9d/21/0941a6b93a58b59a1ec75e5333bf06929b671309c43c0cd201c172d9c39f/yarl-1.25.1-cp312-cp312-win_arm64.whl", hash = "sha256:bc3ac7bf569f6b64dad04dd7808c7872dae8a97df657856eac05e9b7e3614a85", size = 97697, upload-time = "2026-09-15T19:31:35.855Z" }, + { url = "https://files.pythonhosted.org/packages/54/22/318c7980066769c6bcd9221ed2248294f5698811da099013098c670565ed/yarl-1.25.1-py3-none-any.whl", hash = "sha256:681c758b0490f9e96b78e5fa8e8dc6e648e9185bb6eaebe73183c33ea0c445f3", size = 63617, upload-time = "2026-09-15T19:34:59.616Z" }, ] From 6e7c9f8af5497f2a342405a4f356b84354d1775b Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 14:12:39 +1300 Subject: [PATCH 09/10] fix(tts): admit checked Japanese dictionary readings and default context experiment off --- .env.example | 4 +- LICENSES/JAPANESE-DICTIONARY.md | 52 ++- LICENSES/provenance.json | 2 +- .../catalog/voice/tts_japanese_reading.json | 29 +- config/japanese_dictionary.py | 11 + docs/japanese_context_reading.md | 73 +++- docs/japanese_pronunciation_dictionary.md | 159 +++---- .../src/shared/configCatalog.generated.ts | 14 +- .../tests/voiceConfigurationCatalog.test.mjs | 4 +- tests/test_japanese_dictionary.py | 36 +- tests/test_japanese_dictionary_boundaries.py | 10 +- tests/test_japanese_dictionary_data_policy.py | 24 ++ tests/test_japanese_reading.py | 10 + tools/audit_japanese_dictionary.py | 30 +- tools/benchmark_japanese_reading.py | 78 ++++ .../japanese_dictionary_common_words.json | 57 ++- ...japanese_dictionary_reviewed_readings.json | 399 ++++++++++++++++++ tools/data/japanese_dictionary_source.json | 11 +- tools/data/japanese_reading_cases.json | 38 +- tools/prepare_japanese_dictionary.py | 22 +- tools/validate_japanese_dictionary.py | 67 +-- 21 files changed, 925 insertions(+), 205 deletions(-) create mode 100644 tools/benchmark_japanese_reading.py create mode 100644 tools/data/japanese_dictionary_reviewed_readings.json diff --git a/.env.example b/.env.example index 7adbee63..198d2dbe 100644 --- a/.env.example +++ b/.env.example @@ -577,6 +577,6 @@ ENABLE_JAPANESE_USER_DICTIONARY=true # END GENERATED CONFIG: tts_japanese_dictionary # BEGIN GENERATED CONFIG: tts_japanese_reading -# Japanese contextual readings -ENABLE_JAPANESE_CONTEXT_READING=true +# Experimental Japanese contextual readings +ENABLE_JAPANESE_CONTEXT_READING=false # END GENERATED CONFIG: tts_japanese_reading diff --git a/LICENSES/JAPANESE-DICTIONARY.md b/LICENSES/JAPANESE-DICTIONARY.md index 79252c26..6faacd4c 100644 --- a/LICENSES/JAPANESE-DICTIONARY.md +++ b/LICENSES/JAPANESE-DICTIONARY.md @@ -48,32 +48,38 @@ then removes only identical full rows. Different lexical readings remain. The generated `provenance.json` additionally identifies the derivative's version, hash, entry count, normalization rules and repair counters. -Version 2026.10.20.2 retains the previous correction history and adds a -source-backed `let's → レッツ` repair: the existing `lets` entry has the same -CMU phones. Its accent annotation is reused, not validated by listening. +Version 2026.10.20.3 admits only exact word/reading pairs in the hash-pinned +`tools/data/japanese_dictionary_reviewed_readings.json`, after base ownership +and CMU quarantine. The smaller candidate has 129 rows / 127 distinct words. +The ledger is an individual model-assisted lexical candidate check, not +independent human/native-speaker or listening sign-off. Unreviewed source rows +and historical corrections are research evidence, not release approval. + The historical 317-row review comprises 94 lexical/script/annotation repairs, -65 representation repairs and 158 unconfirmed readings. The release filter -yields known whole words and redundant letter readings to the pinned base -and quarantines every variant of CMU final-phone risk surfaces. CMU/base -hashes, rules and complete exclusion records are in provenance. The direct -flags reproduce the external 1,557-row TSV; quarantine of their other variants -excludes another 112 rows. This screen admits false positives and can miss -other errors; it does not establish that each excluded row is wrong. +65 representation repairs and 158 unconfirmed readings. Those unconfirmed +rows remain in research inputs; release admission now requires the separate +checked ledger. The source-backed `let's → レッツ` repair reuses the existing +`lets` annotation and CMU phones. `proposer` has a provisional flat accent. +Neither native compilation nor these lexical checks establish accent quality. + +Base/CMU hashes, ledger identity, accepted readings, exclusion counts and an +excluded-row-set hash are in provenance; rejected readings are not shipped in +that metadata. Source, correction and ledger hashes reproduce the selection. +The CMU screen reproduces the 1,557 direct flags plus 112 variants, including +false positives. It can miss interior/vowel shifts and is not a correctness +oracle. Every shipped pair must pass ledger membership independently of it. The prepared CSV and frontend share full-token boundaries on retained Latin -entries. Only complete registered official tokens are marked, preventing -prefix and suffix intrusion while leaving unlisted/base/legacy/personal-only -inputs unchanged. All original source costs are preserved; historical cost -calibration and the common floor are retired. This CSV requires the matching -frontend integration, not only an OpenJTalk user-dictionary registration. - -From 127,808 normalized entries, 2,502 are excluded (849 base-owned rows, -1,669 CMU-risk rows, 16 overlap), leaving 125,306. Three-context validation -covers all 814 base-owned surfaces, and 290 common English words extend the -former 52-control sample. Native selection across 122,978 retained distinct -surfaces in three contexts loses no previously selected whole-word match. -These checks do not establish exhaustive lexical accuracy. `proposer` still -uses a provisional flat accent; waveform/listening acceptance is outstanding. +entries. Only complete registered official tokens are marked. Original costs +are preserved; there are no runtime per-word reading exceptions. This CSV +requires the matching frontend integration, not just native registration. +Old full-data candidates without a checked ledger are retired and rejected. + +The production gate checks every retained word in three contexts (381), all +129 row memberships, 303 common English controls (190 unlisted fallbacks), 52 +baseline controls, 32 extra units and 814 base-owned words in three contexts. +These checks do not establish exhaustive lexical accuracy. Independent review +and human listening acceptance remain outstanding; this candidate is local. The upstream default branch was rechecked on 2026-10-20 at `b4570b876f5c086819ba84e3215472733c6a7b70`: the CSV still has the same Git blob diff --git a/LICENSES/provenance.json b/LICENSES/provenance.json index 70dd293c..119bca5a 100644 --- a/LICENSES/provenance.json +++ b/LICENSES/provenance.json @@ -92,7 +92,7 @@ "modified": true, "release_action": "exclude", "gate_status": "ready", - "notes": "Data is built as an optional external asset bundle, not included in source archives. Its separate pack carries complete notices and generated source/derivative hashes. Immediate upstream MIT reliance and the unrecorded exact CMUdict input revision are explicit; no exhaustive pronunciation or listening validation is claimed. This record does not publish any bundle." + "notes": "Data is built as an optional external asset bundle, not included in source archives. Its separate pack carries complete notices and generated source/derivative hashes. Immediate upstream MIT reliance and the unrecorded exact CMUdict input revision are explicit; no exhaustive pronunciation or listening validation is claimed. The 2026.10.20.3 candidate admits only exact checked lexical pairs; unreviewed upstream rows are research inputs and excluded from the official CSV. Independent lexical/accent/listening review is pending. This record does not publish any bundle." }, { "id": "gpt-sovits-uvr5-tools", diff --git a/config/catalog/voice/tts_japanese_reading.json b/config/catalog/voice/tts_japanese_reading.json index a8b81515..9101e2ea 100644 --- a/config/catalog/voice/tts_japanese_reading.json +++ b/config/catalog/voice/tts_japanese_reading.json @@ -1,22 +1,35 @@ { "id": "tts_japanese_reading", - "title": {"en-US": "Japanese contextual readings", "zh-CN": "日语语境读音"}, + "title": { + "en-US": "Experimental Japanese contextual readings", + "zh-CN": "实验性日语语境读音" + }, "description": { - "en-US": "Use an optionally installed model to distinguish Japanese readings from context.", - "zh-CN": "使用可选安装的模型,根据上下文区分日语汉字读音。" + "en-US": "Optional experiment with known reading regressions. Installing the model does not enable it.", + "zh-CN": "存在已知读音回归的可选实验;安装模型不会自动启用。" }, "desktop": true, "apply": "backend_restart", "config": { "ENABLE_JAPANESE_CONTEXT_READING": { "type": "boolean", - "title": {"en-US": "Use installed context-reading model", "zh-CN": "使用已安装的语境读音模型"}, + "title": { + "en-US": "Use installed context-reading model", + "zh-CN": "使用已安装的语境读音模型" + }, "description": { - "en-US": "Install japanese-context-reading separately. No automatic download. Restart the backend after changes.", - "zh-CN": "需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory (external audit about 650–900 MiB); runtime and workload affect usage. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", + "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存(外审约 650–900 MiB),随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" }, - "default": true, - "accepted_values": ["true", "false", "1", "0", "yes", "no"], + "default": false, + "accepted_values": [ + "true", + "false", + "1", + "0", + "yes", + "no" + ], "example_active": true } } diff --git a/config/japanese_dictionary.py b/config/japanese_dictionary.py index 115e93b6..ca68d51d 100644 --- a/config/japanese_dictionary.py +++ b/config/japanese_dictionary.py @@ -81,6 +81,17 @@ def _validate_dictionary_pack(root: Path) -> JapaneseDictionaryPack: count = sum(1 for _ in dictionary_rows(data)) if raw.get("entry_count") != count: raise JapaneseDictionaryError("Dictionary content does not match its provenance entry count") + release_review = raw.get("release_review") + review = release_review.get("accepted_readings") if isinstance(release_review, dict) else None + if not isinstance(review, dict) or not review: + raise JapaneseDictionaryError("Official dictionary lacks its checked reading ledger; old full-data candidates are retired") + if any(not isinstance(values, list) or not values or + any(not isinstance(value, str) or not value for value in values) for values in review.values()): + raise JapaneseDictionaryError("Checked readings must be nonempty lists of exact readings") + for row in dictionary_rows(data): + approved = review.get(latin_word_key(row[0]), ()) + if row[11] not in approved or row[12] not in approved: + raise JapaneseDictionaryError(f"Official dictionary contains an unchecked word or reading: {row[0].strip()}") source = raw.get("source") if not isinstance(source, dict) or any(not source.get(key) for key in ("repository", "revision", "path", "sha256")): raise JapaneseDictionaryError("Dictionary provenance requires a pinned source and source hash") diff --git a/docs/japanese_context_reading.md b/docs/japanese_context_reading.md index f4dbf9f4..cfccfeb9 100644 --- a/docs/japanese_context_reading.md +++ b/docs/japanese_context_reading.md @@ -1,4 +1,4 @@ -# Optional Japanese contextual readings / 可选日语语境读音 +# Experimental Japanese contextual readings / 实验性日语语境读音 `japanese-context-reading` adds upstream tsqyomi v5 reading selection to the existing Japanese OpenJTalk frontend. It chooses among native reading candidates @@ -23,15 +23,19 @@ python tools/external_assets.py install output/amadeus-japanese-context-reading- ``` The Voice setting **Use installed context-reading model** maps to -`ENABLE_JAPANESE_CONTEXT_READING`. It defaults to allowing an installed model, -but never installs/downloads one. Missing optional data is normal and inactive. +`ENABLE_JAPANESE_CONTEXT_READING`. It **defaults off**, even when the model is installed. It never installs/downloads +one. Known reading regressions remain, so enabling it is an explicit experiment. +CPU inference adds hundreds of MiB of process memory; the external corpus audit +reported roughly 650–900 MiB extra. Runtime and workload change the actual cost. +Missing optional data is normal and inactive. Restart the backend after installing, updating or changing this setting. Turning it off uses the existing native reading selection; the English dictionary has its own independent switch. Invalid/incomplete data or unsupported native APIs are reported and leave the existing frontend usable. -安装资源或更改“语音 → 日语语境读音”后须重启后端。未安装时不会自动下载, -仍使用原前端。语境读音模型和英文词典可以独立安装、独立关闭。 +默认关闭,安装后也不自动启用;仍有已知误读。CPU 推理会增加数百 MiB 进程内存 +(外部语料审查约 650–900 MiB,实际取决于运行时和负载)。更改设置后须重启 +后端。两个资源独立安装、独立关闭,均不自动下载。 Startup verifies all three source hashes and loads only local files, explicitly on CPU. It does not contact Hugging Face or choose a GPU provider. The loaded @@ -60,13 +64,13 @@ manifest and installs those files. Native activation additionally checks the fixed upstream hashes. Preparation refuses a different model or a differing existing destination unless `--overwrite` is explicit. -`tools/data/japanese_reading_cases.json` provides 88 synthetic, contrastive reading -targets, including ordinary readings that must not regress. The production-path +`tools/data/japanese_reading_cases.json` provides 92 contrastive reading +targets (the original 88 plus four full external-audit sentences), including ordinary readings that must not regress. The production-path gate compares complete native nodes, checks mora counts and symbol compatibility, and compares plain/prosody paths. It tests correctness against expected readings, not merely equality before/after installation. -## Experiment, 2026-10-20 +## Initial experiment, 2026-10-20 Windows AMD64, Python 3.12.10, pyopenjtalk-plus 0.4.1.post9, ONNX Runtime 1.26.0 and tokenizers 0.22.1 were used for the local comparison. The same 88 lexical @@ -79,10 +83,11 @@ independent estimate of general Japanese accuracy. | Existing frontend (Sudachi / nani defaults) | 69 / 88 | Retain as the base path | | tsqyomi v4 (published native default model) | 82 / 88 | Reject: regresses ordinary 上手 / 下手 / 生 readings | | Haqumei 0.12.2 | 69 / 88 | Reject replacement: no net gain on these targets | -| Pinned tsqyomi v5 | 88 / 88 | Optional installed model | +| Pinned tsqyomi v5 | 88 / 88 | Initial result; superseded by broader failing gate below | The v4 snapshot was `680596dd2ad2bad59ee5db3741e197cfce79f9b4`; v5 is pinned -above. The checked frontend upstream commit was +above. The checked maintained frontend upstream was +`tsukumijima/pyopenjtalk-plus`, commit `b17704eed5f8a7eef778310f9eb9a7f71fb5321b`; it also selects v5. Haqumei's repository was checked at `e70fa6279a225c26f8956ce393730ca58f86db7e`. The additional 32 contexts were frozen before the extended v5 measurement. @@ -109,3 +114,51 @@ names and readings outside the model/candidate vocabulary remain limited. Waveform comparisons use the same voice, reference and sampling seed. Their generation/integrity does not replace listening acceptance. The optional resource ZIP is kept local until separately approved for publication. + +## External audit follow-up, 2026-10-20 + +The original curated 88/88 result did not generalize to independent material. +Four complete source sentences now extend the strict production gate to 92 +lexical targets. The base passes **72/92**, v5 **89/92**, with **three failing +targets and two lost base-correct readings**. The command exits nonzero. These +failures are neither allowlisted nor hidden by the new default-off setting: + +| Complete target/context | Expected | Base | v5 | +| --- | --- | --- | --- | +| 査読を通った | トオッ | トオッ | カヨッ | +| 中立派 in the complete neutral-faction sentence | チュウリツハ | チュウリッパ | ナカリッパ | +| この例での箱は青、玉は5個として続けるわ。 / 玉 | タマ | タマ | タマ | +| 仕事だけ止めるわ。 / 止める | トメル | トメル | ヤメル | + +The compound 中立派 is already missegmented by the base; checking only its 中 +node would falsely certify the compound as correct. The intact number 5 in the +玉 sentence matters: a masked corpus version removed it and changed native +selection. ヤメル is a valid Japanese reading generally; this acceptance target +represents stopping a running Work task, rather than quitting employment. +The source examples are retained without numeric masking. No four-word runtime +blacklist, guessed confidence threshold or generated kana rewrite was added. +The weights remain unchanged. Model quality acceptance is **blocked**. + +A separate fresh-process benchmark over the supplied 12,027-sentence local JSON +corpus measures process RSS and Windows private memory before/after model load +and corpus execution. In this run, model loading added about **312 MiB RSS / +314 MiB private**, and the final increment was about **323 / 318 MiB** over the +warmed base. Absolute final process memory was about **544 MiB RSS / 897 MiB +private**. These are separate metrics, not interchangeable memory limits. The +external reviewer observed a larger increase; retain a conservative warning +rather than treating this one run as a guaranteed maximum. No memory reduction +is claimed. Warm median base/model time was **0.595 / 1.125 ms**, p95 +**1.23 / 8.91 ms**; loading took **0.82 s**. Corpus/model-candidate coverage +changes timing substantially. The 203 changed phone sequences are observations, +not 203 proven improvements, and are a different metric from changed readings. + +Reproduce on an explicit local corpus, without exporting its sentences: + +```powershell +$env:ENABLE_JAPANESE_CONTEXT_READING='false' +python tools/benchmark_japanese_reading.py --corpus path/to/sentences.json --output output/reading_benchmark.json +``` + +The resource remains local and unpublished. Human lexical review, listening +acceptance and independent maintainer approval are outstanding. Keeping the +integration as a default-off experiment does not establish model release quality. diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index 4058f4bc..eb8fb35b 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -15,8 +15,8 @@ loader before installation. Older applications reject the unknown pack ID. The first compatible application release has not yet been published. ```powershell -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.2.zip -python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.20.2.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.3.zip +python tools/external_assets.py install output/amadeus-japanese-pronunciation-2026.10.20.3.zip python tools/external_assets.py status ``` @@ -91,8 +91,8 @@ is explicit. ```powershell python tools/prepare_japanese_dictionary.py --source GPT_SoVITS/text/ja_userdic/userdict.csv python tools/validate_japanese_dictionary.py --output output/japanese_dictionary_validation.json -python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.20.2.zip -python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.2.zip +python tools/external_assets.py build japanese-pronunciation --output output/amadeus-japanese-pronunciation-2026.10.20.3.zip +python tools/external_assets.py verify output/amadeus-japanese-pronunciation-2026.10.20.3.zip python tools/audit_japanese_dictionary.py --source assets/text/ja/english/userdict.csv --output output/japanese_dictionary_audit.json ``` @@ -108,95 +108,74 @@ see [Japanese contextual readings](japanese_context_reading.md). Its model and switch are independent of this English loanword pack. Disabling this dictionary does not disable an installed context-reading model. -Version 2026.10.20.2 retains the historical review of 317 entries: 94 -lexical/script/annotation repairs, 65 separator/format repairs, and 158 -unconfirmed readings. Those decisions are evidence, not mandatory release -inclusions. The correction CSV has 172 rows after retiring the cost-only -`components` entry and adding `let's → レッツ`. The latter reuses the existing -`lets` reading and accent annotation: both have CMU `L EH1 T S`. +Version **2026.10.20.3** admits only individually checked, exact word/reading +pairs from `tools/data/japanese_dictionary_reviewed_readings.json`. The user +explicitly selected smaller coverage over distributing unchecked upstream data. +The ledger is a model-assisted lexical candidate review against CMU phones and +Japanese loanword usage, **not independent human or native-speaker sign-off**. +Independent maintainer review, accent and listening acceptance remain pending. + +The candidate contains **129 rows / 127 distinct words**, selected from 129 +ledger words. Base ownership and conservative screening can still exclude a +checked word. A checked alternative does not authorize other readings of the +same word. The loader rejects old full-data candidates lacking this ledger; +update that local pack or disable it. Historical upstream CSVs and the 317-row +review remain research inputs; they are not automatically admitted into the +formal pack. Personal/legacy sources remain an explicit, separate user choice. Preparation applies these rules offline, after representation repairs: -- Yield every surface recognized as one known whole word by an independent - base MeCab instance to that base. Also remove redundant letter/initialism - readings identical to the base result, such as `rpm`. Other unknown words - remain eligible for improved readings. Pin the runtime and base dictionary. -- Quarantine every variant of a surface if any reading's final Katakana - consonant conflicts with every CMU pronunciation variant: ズ requires S/Z, - ド requires D/T, and ング requires NG. Removing only one bad variant could - promote another shifted reading (`directly → director`). -- Surround retained Latin surfaces with native fullwidth spaces. The frontend - applies both boundaries only to complete tokens present in the successfully - registered official CSV, after sentence/punctuation splitting. Thus `mode` - cannot intrude into `model`, nor `con` into `icon`. Unlisted words and units - keep their input unchanged; base, legacy and personal-only behavior is kept. - Both prosody and plain-phoneme paths use this contract. Raw pyopenjtalk callers - of this prepared CSV must use the same helper. This requires the matching - frontend code, in addition to the installer/catalog entry. - -All original source costs are preserved. Whole-token boundaries supersede -both the historical 109-entry calibration and the rejected common cost floor; -changing costs traded correct short-word readings against longer-word splits. -Historical cost evidence is explicitly marked retired. There are no runtime -per-word reading exceptions; the runtime vocabulary comes from the exact -successfully loaded official bytes and is cleared on disable/load failure. -The audio-cache identity includes this frontend contract. - -From 127,808 normalized rows, the policy excludes 2,502 rows: 849 base-owned -rows and 1,669 rows associated with CMU-risk surfaces (16 overlap). The resulting -pack has 125,306 rows; 125,303 Latin rows receive word boundaries. Provenance -records every exclusion's normalized input line, surface, reading and reason. -CMU data and native base hashes are pinned. - -The direct flags exactly reproduce all 1,557 external TSV records (ズ 807, -ド 523, ング 227); none of those original word/reading pairs remain. Another -112 same-surface variants are quarantined to avoid promoting shifted rows. -These are conservative exclusions, not 1,669 proven errors: `with → ウィズ` -and `condo → コンド` can be legitimate loanword adaptations. The rule can miss -interior shifts and vowel-ending errors. The older singular/inflection collision -list remains review-only (`path` and `paths` can both be `パス`). - -构建时优先使用基础词典,隔离词尾音可疑的整个词形;正式包只在完整词边界命中, -避免短词串入其他单词。原有词条代价全部保留,代价补丁已撤掉。未收录的词、 -单位及仅使用个人或旧词典的路径保持原输入。隔离清单含误报,不等于全部确认有错; -可确认的 `let's` 错行使用已有 `lets` 的读音修复。 - -Whole-token abbreviation preparation handles `MLP` next to Japanese, fullwidth -Latin text and case variants of existing names, and avoids replacing `API` -inside `CAPITAL`. Known names/initialisms followed by plural or possessive -suffixes convert only their stem and preserve the suffix. It applies to Japanese synthesis text; English output, -subtitles and conversation history retain their original text. - -The upstream translation is not exhaustively human-verified. This is an English -word/name extension, not a general context-sensitive Japanese homograph model -or a voice/timbre replacement. Native pronunciation/prosody comparisons do not -replace end-to-end synthesis and human listening acceptance. - -The native release gate compares the original 52 controls and 32 additional -unit samples against the production `japanese.g2p` path. It also compares all -814 base-owned imported words in three contexts (2,442 controls), exercises -290 frozen common English words, including all 48 distinct words in the -external 49-row frequent-document subset, and validates the 11 reported regressions -with reviewed readings and five unchanged base fallbacks. The broader -vocabulary is selected from frequent tracked Markdown words and flagged -surfaces; inclusion alone is not a pronunciation correctness oracle. Changes -for other unknown words are recorded for review. - -Retained corrected rows and contractions pass 516 native contexts and 172 -production frontend/prosody comparisons. There are no failures or unit-prosody -changes in this candidate. These results apply to these controls and the -pinned native environment; they do not establish zero regressions across all -English vocabulary or replace waveform/listening acceptance. - -Across all 122,978 retained distinct surfaces in three contexts, the new native -selection preserves every complete-word match from the previous pack and -selects a complete word for previously split inputs. This checks selection, -not the correctness of every imported reading. - -The gate runs offline with a prepared full pack. Core tests use synthetic data -and require no dictionary/model downloads. Pack validation checks hash before -parsing CSV and memoizes success/failure by member stats. Audio cache keys -perform no dictionary-file validation. +- Yield words recognized by an independent pinned base MeCab instance, and + redundant letter/initialism readings, to the base dictionary. +- Quarantine every variant of a surface if its final Katakana consonant + conflicts with all CMU variants: ズ requires S/Z, ド requires D/T, ング requires + NG. This screen can both miss errors and reject valid loanwords. It is a + secondary screen, not permission to release all other upstream rows. +- Admit a remaining row only when **both** reading fields match an exact + checked pair in the hash-pinned ledger. No automatic rough transliterator or + runtime pronunciation exception decides lexical admission. +- Bound retained Latin surfaces with native fullwidth spaces; the frontend + marks only complete tokens present in the registered official pack. `mode` + cannot intrude into `model`, nor `con` into `icon`. Both frontend paths use + the same contract. Unlisted words/units retain their input. Original costs + are preserved; historical cost calibration and the common floor are retired. + +Provenance contains the ledger identity and accepted pairs, source/CMU/base +hashes, aggregate exclusions and a reproducible hash of excluded audit rows. +The rejected source readings are not copied into the resource pack. Detailed +research evidence can be generated separately by the offline audit command. +The original correction history remains tracked; only approved corrections +are described in the pack. The source's 1,557 terminal-phone flags plus 112 +variants remain excluded, but this is not a claim that all 1,669 were errors. + +正式候选包仅收录逐条检查过的“词形+读音”,接受覆盖缩小。同词形的未审核读音 +也不收录。清单目前属于模型辅助检查,仍需独立维护者复核和人工试听;不再把 +“未被自动规则标出”当作质量合格。没有新增运行时逐词特判。 + +The production `clean_text` gate independently loads the checked-in ledger and +asserts every released word in three contexts (**381 checks**) plus 127 +frontend/prosody comparisons. All 129 stored rows must match approved readings, +including alternatives that native cost selection does not choose. The gate +also checks 52 baseline controls, 32 extra units, all 814 base-owned words in +three contexts, and **303 common English controls**. All **190 unlisted common +words** must preserve baseline phones and remain unmarked by token boundaries. +There is no coverage-only correctness claim for released words. The 26 reported +sentence contexts include the new interior/vowel-shift examples; those omitted +words fall back to the base, rather than receiving guessed replacements. + +The candidate passes those lexical/integration gates with no unit-prosody +changes. `model`, `welcome`, `directly`, `reset`, `deadline` and the newly +reported unreviewed words may still be spelled as letters. This is a known +coverage limit. Existing correct help/learn/two/with/production remain owned by +the base; `let’s` uses the checked レッツ repair. Proper-name ambiguity and the +provisional `proposer` accent still require review. These checks establish +these targets only, not universal accuracy or natural emotional delivery. + +The gate runs offline with a prepared candidate. Core tests use synthetic data +and require no dictionary/model downloads. Pack validation checks content hash +and exact ledger membership, and memoizes results by member stats. Audio cache +keys perform no dictionary-file validation. Abbreviation preparation, subtitles, +conversation history and the character checkpoint contract are unchanged. ## Publication / 发布 diff --git a/electron/src/shared/configCatalog.generated.ts b/electron/src/shared/configCatalog.generated.ts index 1f3552d7..c49f3d48 100644 --- a/electron/src/shared/configCatalog.generated.ts +++ b/electron/src/shared/configCatalog.generated.ts @@ -3146,12 +3146,12 @@ export const catalogGroups: CatalogGroup[] = [ { "id": "tts_japanese_reading", "title": { - "en-US": "Japanese contextual readings", - "zh-CN": "日语语境读音" + "en-US": "Experimental Japanese contextual readings", + "zh-CN": "实验性日语语境读音" }, "description": { - "en-US": "Use an optionally installed model to distinguish Japanese readings from context.", - "zh-CN": "使用可选安装的模型,根据上下文区分日语汉字读音。" + "en-US": "Optional experiment with known reading regressions. Installing the model does not enable it.", + "zh-CN": "存在已知读音回归的可选实验;安装模型不会自动启用。" }, "desktop": true, "apply": "backend_restart", @@ -3163,10 +3163,10 @@ export const catalogGroups: CatalogGroup[] = [ "zh-CN": "使用已安装的语境读音模型" }, "description": { - "en-US": "Install japanese-context-reading separately. No automatic download. Restart the backend after changes.", - "zh-CN": "需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory (external audit about 650–900 MiB); runtime and workload affect usage. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", + "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存(外审约 650–900 MiB),随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" }, - "default": true, + "default": false, "accepted_values": [ "true", "false", diff --git a/electron/tests/voiceConfigurationCatalog.test.mjs b/electron/tests/voiceConfigurationCatalog.test.mjs index 2838de26..5e11c33b 100644 --- a/electron/tests/voiceConfigurationCatalog.test.mjs +++ b/electron/tests/voiceConfigurationCatalog.test.mjs @@ -109,7 +109,9 @@ test('Japanese dictionaries are optional and can be disabled with a restart', () test('Japanese context reading follows the saved switch and output language', () => { const selection = { asrBackend: 'qwen3_asr', ttsBackend: 'gpt_sovits', wakeEnabled: false, aecEnabled: false, ttsOutputLanguage: 'ja' } const group = (selected, values = {}) => exports.buildVoiceConfigurationCatalog(selected, { values }).find(item => item.id === 'tts_japanese_reading') - assert.equal(group(selection).fields[0].value, true) + assert.equal(group(selection).fields[0].value, false) + assert.equal(group(selection).active, false) + assert.equal(group(selection, { ENABLE_JAPANESE_CONTEXT_READING: 'true' }).active, true) assert.equal(group(selection, { ENABLE_JAPANESE_CONTEXT_READING: 'false' }).active, false) assert.equal(group({ ...selection, ttsOutputLanguage: 'en' }).active, false) assert.equal(group({ ...selection, ttsBackend: 'remote' }).active, false) diff --git a/tests/test_japanese_dictionary.py b/tests/test_japanese_dictionary.py index 5b57555f..dd809b6b 100644 --- a/tests/test_japanese_dictionary.py +++ b/tests/test_japanese_dictionary.py @@ -49,8 +49,24 @@ def _prepare(root: Path, *, native: bool = False) -> Path: recipe = json.loads((ROOT / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(list(dictionary_pack.dictionary_rows(data)))) recipe_path = root / "recipe.json" + reviewed_path = ROOT / "tools/data/japanese_dictionary_reviewed_readings.json" + if native: + ledger = json.loads(reviewed_path.read_text(encoding="utf-8")) + ledger["readings"].update(comp=["コンプ"], ent=["エント"]) + # These fixtures exercise native compounds and historical repair + # contracts independently of the production release subset. + ledger["readings"].update({word: [reading] for word, reading in { + "dentist": "デンティスト", "balloonist": "バルーニスト", "ninth": "ナインス", + "intruder": "イントルーダー", "lawrence": "ローレンス", "ballet's": "バレエズ", + "coca-cola": "コカコーラ", "t-shirt": "ティーシャツ", "economists'": "エコノミスツ", + }.items()}) + reviewed_path = root / "reviewed.json" + review_data = (json.dumps(ledger, ensure_ascii=False) + "\n").encode() + reviewed_path.write_bytes(review_data) + recipe["release_review"]["sha256"] = hashlib.sha256(review_data).hexdigest() recipe_path.write_text(json.dumps(recipe), encoding="utf-8") return prepare_dictionary(source, project_root=root, recipe_path=recipe_path, + reviewed_path=reviewed_path, count_mora=None if native else lambda _: None, base_word=None if native else lambda *_: False) @@ -85,20 +101,21 @@ def test_preparation_records_derivative_and_does_not_edit_source(tmp_path): pack_root = _prepare(tmp_path) assert (tmp_path / "upstream.csv").read_text(encoding="utf-8") == FOLLOWER + LEARNER pack = dictionary_pack.load_dictionary_pack(pack_root) - corrections = list(dictionary_pack.dictionary_rows((ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes())) - # Conservative final-phone screening also quarantines legitimate loanword - # adaptations; condo -> コンド is a documented false positive (CMU OW). - accepted = [row for row in corrections if row[0] != "condo"] - assert pack.entry_count == len(accepted) readings = {row[0].strip(dictionary_pack.LATIN_WORD_BOUNDARY): row[11] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes())} - assert readings == {row[0]: row[11] for row in accepted} + from tools.audit_japanese_dictionary import reviewed_readings, word_key + reviewed, _ = reviewed_readings() + assert pack.entry_count == len(readings) + assert all(reading in reviewed[word_key(word)] for word, reading in readings.items()) + assert "wurtzel" not in readings, "historical repairs are not automatic release approval" assert readings["follower"] == "フォロワー" assert readings["learner"] == "ラーナー" provenance = json.loads((pack_root / "provenance.json").read_text(encoding="utf-8")) assert provenance["normalization"]["rules"] assert provenance["normalization"]["results"]["output_entries"] == provenance["extension_filter"]["results"]["input_entries"] assert provenance["extension_filter"]["results"]["output_entries"] == pack.entry_count + assert "excluded" not in provenance["extension_filter"]["results"], "unreviewed source rows stay in local audit output" + assert all(item["after"] in reviewed.get(word_key(item["word"]), ()) for item in provenance["corrections"]) assert all(path.is_file() for path in pack.files()) # A maintainer cannot accidentally publish a customized installation as the # pinned upstream base, even if it has the expected CSV shape. @@ -220,8 +237,15 @@ def test_reviewed_bad_reading_repair_preserves_other_readings_of_the_same_surfac source.write_bytes(data) recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=2) recipe_path = tmp_path / "recipe.json" + ledger = json.loads((ROOT / "tools/data/japanese_dictionary_reviewed_readings.json").read_text(encoding="utf-8")) + ledger["readings"]["corps"] = ["コア", "コアズ"] + reviewed_path = tmp_path / "reviewed.json" + review_data = (json.dumps(ledger, ensure_ascii=False) + "\n").encode() + reviewed_path.write_bytes(review_data) + recipe["release_review"]["sha256"] = hashlib.sha256(review_data).hexdigest() recipe_path.write_text(json.dumps(recipe), encoding="utf-8") pack_root = prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, + reviewed_path=reviewed_path, count_mora=lambda _: None, base_word=lambda *_: False) readings = [row[12] for row in dictionary_pack.dictionary_rows((pack_root / "userdict.csv").read_bytes()) if row[0].strip(dictionary_pack.LATIN_WORD_BOUNDARY) == "corps"] diff --git a/tests/test_japanese_dictionary_boundaries.py b/tests/test_japanese_dictionary_boundaries.py index 492e5ec7..18396ca4 100644 --- a/tests/test_japanese_dictionary_boundaries.py +++ b/tests/test_japanese_dictionary_boundaries.py @@ -138,9 +138,17 @@ def test_native_units_and_contractions_against_base_frontend(tmp_path): source.write_bytes(data) recipe = json.loads((root / "tools/data/japanese_dictionary_source.json").read_text(encoding="utf-8")) recipe["source"].update(sha256=hashlib.sha256(data).hexdigest(), entry_count=len(rows)) + # Explicit fixture approval tests contraction mechanics without enlarging + # the production subset merely to satisfy an integration test. + ledger = json.loads((root / "tools/data/japanese_dictionary_reviewed_readings.json").read_text(encoding="utf-8")) + ledger["readings"].update({"don't": ["ドント"], "i'm": ["アイム"]}) + reviewed_path = tmp_path / "reviewed.json" + review_data = (json.dumps(ledger, ensure_ascii=False) + "\n").encode() + reviewed_path.write_bytes(review_data) + recipe["release_review"]["sha256"] = hashlib.sha256(review_data).hexdigest() recipe_path = tmp_path / "recipe.json" recipe_path.write_text(json.dumps(recipe), encoding="utf-8") - prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path) + prepare_dictionary(source, project_root=tmp_path, recipe_path=recipe_path, reviewed_path=reviewed_path) texts = ("身長は170cmです", "50kgです", "3kmです", "10mmです", "100mlです", "60kWです", "60MWです", "APIsを使う", "OpenAI's API") try: jtalk.unset_user_dict() diff --git a/tests/test_japanese_dictionary_data_policy.py b/tests/test_japanese_dictionary_data_policy.py index 0a9519a6..4c00d186 100644 --- a/tests/test_japanese_dictionary_data_policy.py +++ b/tests/test_japanese_dictionary_data_policy.py @@ -71,6 +71,30 @@ def test_native_base_lookup_does_not_treat_letter_spelling_as_a_word(): assert not known("model", "モデル") +def test_official_admission_requires_each_exact_checked_reading(): + rows = [_row("true", "トゥルービュー"), _row("follower", "フォロワー"), + _row("follower", "フォロワーズ"), _row("unreviewed", "アンレビュード")] + before = [list(row) for row in rows] + kept, report = filter_extension_rows(rows, {}, lambda *_: False, {"follower": {"フォロワー"}}) + assert [(word_key(row[0]), row[12]) for row in kept] == [("follower", "フォロワー")] + assert report["reason_counts"] == {"unreviewed_word_or_reading": 3} + assert rows == before + + +@pytest.mark.parametrize("review", [None, {"accepted_readings": {"follower": "フォロワー"}}, + {"accepted_readings": {"follower": ["別の読音"]}}]) +def test_official_pack_rejects_retired_or_inconsistent_checked_ledger(tmp_path, review): + from test_japanese_dictionary import _prepare + from config.japanese_dictionary import JapaneseDictionaryError, load_dictionary_pack + pack = _prepare(tmp_path) + provenance = pack / "provenance.json" + data = json.loads(provenance.read_text(encoding="utf-8")) + data["release_review"] = review + provenance.write_text(json.dumps(data), encoding="utf-8") + with pytest.raises(JapaneseDictionaryError, match="ledger|readings|unchecked"): + load_dictionary_pack(pack) + + def test_boundaries_require_a_complete_registered_token_and_preserve_numeric_units(): words = frozenset({"mode", "con", "ion", "don't"}) text = "model icon production 100rpm 100L 100人" diff --git a/tests/test_japanese_reading.py b/tests/test_japanese_reading.py index 9813ab7c..75fb39e6 100644 --- a/tests/test_japanese_reading.py +++ b/tests/test_japanese_reading.py @@ -65,6 +65,16 @@ def test_ready_model_uses_only_pinned_local_files_and_cpu(tmp_path, monkeypatch) assert calls[-1] == ("舞台の上手", {}) +def test_installed_model_is_not_enabled_by_default(tmp_path, monkeypatch): + _prepare(tmp_path, monkeypatch) + from config import settings + monkeypatch.setattr(settings, "ENABLE_JAPANESE_CONTEXT_READING", False) + native, calls = _native() + assert runtime.configure_japanese_reading(native, project_root=tmp_path)["state"] == "disabled" + runtime.run_frontend(native, "査読を通った") + assert calls == [("査読を通った", {})] + + @pytest.mark.parametrize("problem", ["corrupt", "missing", "native_failure", "unsupported"]) def test_invalid_optional_model_is_reported_without_breaking_base_frontend(tmp_path, monkeypatch, problem): directory = _prepare(tmp_path, monkeypatch) diff --git a/tools/audit_japanese_dictionary.py b/tools/audit_japanese_dictionary.py index 13df1c56..395dec15 100644 --- a/tools/audit_japanese_dictionary.py +++ b/tools/audit_japanese_dictionary.py @@ -28,6 +28,20 @@ _FINAL_CONSONANTS = { "ズ": {"Z", "S"}, "ド": {"D", "T"}, "ング": {"NG"}, } +REVIEWED_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_reviewed_readings.json" + + +def reviewed_readings(path: Path = REVIEWED_PATH) -> tuple[dict[str, set[str]], bytes]: + data = Path(path).read_bytes().replace(b"\r\n", b"\n") + document = json.loads(data) + readings = document["readings"] + if not isinstance(readings, dict) or not readings or any( + not isinstance(word, str) or not word or not isinstance(values, list) or not values or + any(not isinstance(value, str) or not _KANA.fullmatch(value) for value in values) + for word, values in readings.items() + ): + raise ValueError("Reviewed ledger requires words with nonempty lists of exact Katakana readings") + return {word_key(word): set(values) for word, values in readings.items()}, data def word_key(value: str) -> str: @@ -91,7 +105,8 @@ def native_base_hashes() -> dict[str, str]: def filter_extension_rows(rows: list[list[str]], phones: dict[str, set[tuple[str, ...]]], - base_word: Callable[..., bool]) -> tuple[list[list[str]], dict]: + base_word: Callable[..., bool], + reviewed: dict[str, set[str]] | None = None) -> tuple[list[list[str]], dict]: """Yield known words to the base and quarantine suspect readings offline.""" kept, excluded = [], [] counts = Counter() @@ -108,6 +123,8 @@ def filter_extension_rows(rows: list[list[str]], phones: dict[str, set[tuple[str # merely promote the next unverified variant (e.g. directly -> director). if word in suspect: reasons.append("cmu_final_phone_risk") + if reviewed is not None and (row[11] not in reviewed.get(word, ()) or row[12] not in reviewed.get(word, ())): + reasons.append("unreviewed_word_or_reading") if reasons: counts.update(reasons) excluded.append({"normalized_line": number, "surface": word, "reading": row[12], @@ -229,7 +246,8 @@ def normalize_rows(rows: list[list[str]], count_mora: Callable[[str], int | None def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], int | None], normalize_surface: Callable[[str], str] | None = None, - base_word: Callable[..., bool] | None = None) -> dict: + base_word: Callable[..., bool] | None = None, + reviewed: dict[str, set[str]] | None = None) -> dict: words = defaultdict(list) for number, row in enumerate(rows, 1): key = word_key(row[0]) @@ -256,7 +274,7 @@ def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], "disposition": "review_only; Japanese loanword forms may legitimately coincide"}) _, normalization = normalize_rows(rows, count_mora, normalize_surface) duplicates = Counter(tuple(row) for row in rows) - _, extension = filter_extension_rows(rows, phones, base_word or (lambda *_: False)) + _, extension = filter_extension_rows(rows, phones, base_word or (lambda *_: False), reviewed) return {"entries": len(rows), "distinct_surfaces": len(words), "cmudict_sha256": hashlib.sha256(cmu_data).hexdigest(), "exact_duplicate_rows": sum(count - 1 for count in duplicates.values()), @@ -264,7 +282,7 @@ def audit_rows(rows: list[list[str]], cmudict: Path, count_mora: Callable[[str], "inflection_candidate_count": len(candidates), "inflection_candidates": candidates, "extension_filter": extension, "normalization": normalization, - "limitations": "Candidates do not prove lexical errors. Legitimate heteronyms and loanword inflections are retained. No LLM, network, waveform synthesis or semantic classifier is used."} + "limitations": "Screening is not a correctness oracle. Official release admission additionally requires an exact checked word/reading pair. Research rows and heteronyms are not rewritten. No network or waveform synthesis is used."} def main() -> int: @@ -275,8 +293,10 @@ def main() -> int: args = parser.parse_args() count, version = native_mora_counter() source_data = args.source.read_bytes() + reviewed, review_data = reviewed_readings() report = audit_rows(list(dictionary_rows(source_data)), args.cmudict, count, native_surface_normalizer(), - native_base_word_lookup()) + native_base_word_lookup(), reviewed) + report["reviewed_ledger_sha256"] = hashlib.sha256(review_data).hexdigest() report["base_dictionary_sha256"] = native_base_hashes() report["source_sha256"] = hashlib.sha256(source_data).hexdigest() report["native_runtime"] = version diff --git a/tools/benchmark_japanese_reading.py b/tools/benchmark_japanese_reading.py new file mode 100644 index 00000000..dcc09780 --- /dev/null +++ b/tools/benchmark_japanese_reading.py @@ -0,0 +1,78 @@ +"""Measure installed context reading on an explicit local JSON sentence list, offline.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path +import statistics +import sys +import time + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT)) + + +def benchmark(corpus: Path, project_root: Path) -> dict: + import psutil + import pyopenjtalk + from tts.japanese_reading import configure_japanese_reading + from tools.tts_text_processor import correct_pronunciation_for_tts + + # Establish the base state before importing the production frontend too. + # Set ENABLE_JAPANESE_CONTEXT_READING=false in this benchmark's environment + # so its normal import cannot load an installed model before measurement. + from config.settings import ENABLE_JAPANESE_CONTEXT_READING + if ENABLE_JAPANESE_CONTEXT_READING: + raise ValueError("Run this fresh process with ENABLE_JAPANESE_CONTEXT_READING=false") + sys.path.insert(0, str(ROOT / "GPT_SoVITS")) + from text.cleaner import clean_text + + sentences = json.loads(corpus.read_text(encoding="utf-8")) + if not isinstance(sentences, list) or not sentences or any(not isinstance(s, str) for s in sentences): + raise ValueError("Corpus must be a nonempty JSON list of sentences") + process = psutil.Process() + + def memory(): + info = process.memory_info() + return {name: round(getattr(info, name) / 2**20, 2) for name in ("rss", "private") if hasattr(info, name)} + + before, summaries, memories = [], {}, {} + changed = 0 + try: + for name, enabled in (("base", False), ("context", True)): + memories[name + "_before_load_mib"] = memory() + start = time.perf_counter() + status = configure_japanese_reading(pyopenjtalk, project_root=project_root, enabled=enabled) + load_seconds = time.perf_counter() - start + if enabled and not status["ready"]: + raise RuntimeError(status["detail"]) + memories[name + "_after_load_mib"] = memory() + timings = [] + for index, sentence in enumerate(sentences): + start = time.perf_counter() + phones = clean_text(correct_pronunciation_for_tts(sentence), "ja", "v2")[0] + timings.append(1000 * (time.perf_counter() - start)) + if enabled: + changed += phones != before[index] + else: + before.append(phones) + memories[name + "_after_corpus_mib"] = memory() + summaries[name] = {"load_seconds": load_seconds, "median_ms": statistics.median(timings), + "p95_ms": sorted(timings)[int(.95 * (len(timings) - 1))]} + return {"sentences": len(sentences), "changed_phone_sequences": changed, + "timing": summaries, "process_memory": memories, + "limitations": "Single fresh process; process memory includes native caches/arenas and base outputs. Corpus changes are observations, not correctness judgments. No sentences or audio are exported."} + finally: + configure_japanese_reading(pyopenjtalk, project_root=project_root, enabled=False) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--corpus", type=Path, required=True) + parser.add_argument("--project-root", type=Path, default=ROOT) + parser.add_argument("--output", type=Path, required=True) + args = parser.parse_args() + report = benchmark(args.corpus, args.project_root) + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_text(json.dumps(report, indent=2) + "\n", encoding="utf-8") + print(json.dumps(report)) diff --git a/tools/data/japanese_dictionary_common_words.json b/tools/data/japanese_dictionary_common_words.json index 76cc7be8..18e061bb 100644 --- a/tools/data/japanese_dictionary_common_words.json +++ b/tools/data/japanese_dictionary_common_words.json @@ -1,5 +1,5 @@ { - "description": "Frozen regression vocabulary: frequent CMU English tokens in tracked Markdown, independent final-phone risk surfaces occurring at least three times there, the 11 externally reported regressions, and all 48 distinct words from the external 49-row frequent-doc subset. Counts select coverage, not correct pronunciations.", + "description": "Frozen regression vocabulary: frequent CMU English tokens in tracked Markdown, independent final-phone risk surfaces occurring at least three times there, the 11 externally reported regressions, and all 48 distinct words from the external 49-row frequent-doc subset. Counts select coverage, not correct pronunciations. Every released word is checked against the separate reviewed_readings ledger; all absent common words must preserve baseline phones. Coverage counts alone are not correctness.", "selected_on": "2026-10-20", "words": [ "ablation", @@ -31,6 +31,7 @@ "backend", "before", "begin", + "behavior", "blackwell", "borderline", "both", @@ -47,6 +48,7 @@ "cannot", "change", "changes", + "changing", "character", "chat", "check", @@ -68,11 +70,13 @@ "core", "could", "cpu", + "credentials", "current", "deadline", "default", "desktop", "device", + "different", "direct", "directed", "directly", @@ -83,7 +87,9 @@ "drive", "driver", "duplex", + "during", "each", + "earlier", "edu", "electron", "embedded", @@ -136,6 +142,7 @@ "just", "keep", "key", + "later", "learn", "let’s", "lifecycle", @@ -175,6 +182,7 @@ "output", "pack", "package", + "pass", "passed", "path", "per", @@ -183,10 +191,11 @@ "playing", "policy", "preference", + "private", "probe", "process", - "production", "Production", + "production", "profile", "profiles", "progressive", @@ -197,6 +206,7 @@ "python", "quieter", "rag", + "raw", "real", "reference", "refusal", @@ -235,12 +245,14 @@ "snapshot", "source", "spacing", + "specific", "speckle", "speech", "start", "startup", "state", "status", + "stay", "still", "strategy", "subtitle", @@ -263,8 +275,8 @@ "transient", "trigger", "true", - "two", "Two", + "two", "unchanged", "use", "used", @@ -277,6 +289,7 @@ "voice", "wake", "wallpaper", + "want", "was", "waveform", "welcome", @@ -293,22 +306,27 @@ "writing", "zip" ], - "reviewed_readings": { - "Help": "ヘルプ", - "learn": "ラーン", - "Two": "ツー", - "with": "ウィズ", - "Production": "プロダクション", - "let’s": "レッツ", - "mode": "モード", - "direct": "ダイレクト" - }, "base_fallback_words": [ - "model", - "welcome", + "behavior", + "changing", + "credentials", + "deadline", + "different", "directly", + "during", + "earlier", + "later", + "model", + "pass", + "private", + "raw", "reset", - "deadline" + "revision", + "specific", + "stay", + "true", + "want", + "welcome" ], "external_audit": { "input_pack_version": "2026.10.18.1", @@ -317,5 +335,12 @@ "tsv_sha256": "b8446675edbd1ff0796b76ad6167e8e20266b198a9829d8f60bc0e37e0f73e2a", "frequent_doc_rows": 49, "frequent_doc_distinct_words": 48 + }, + "base_readings": { + "Help": "ヘルプ", + "learn": "ラーン", + "Two": "ツー", + "with": "ウィズ", + "Production": "プロダクション" } } diff --git a/tools/data/japanese_dictionary_reviewed_readings.json b/tools/data/japanese_dictionary_reviewed_readings.json new file mode 100644 index 00000000..772f1845 --- /dev/null +++ b/tools/data/japanese_dictionary_reviewed_readings.json @@ -0,0 +1,399 @@ +{ + "version": "2026.10.20.3", + "checked_on": "2026-10-20", + "review_method": "Individually selected lexical candidate readings; model-assisted comparison with CMU phones and Japanese loanword usage. Not independent human sign-off.", + "limitations": "Lexical admission only. Independent maintainer/lexical review and listening/accent acceptance remain pending. Absent words/readings are not authorized for the official candidate; preserved source/corrections are research evidence, not automatic approval.", + "cmudict_path": "GPT_SoVITS/text/cmudict.rep", + "cmudict_sha256": "0e601d017d6e6f958443d41cd8922b4cd7598b3ba2056253a33f3e5a35f38494", + "readings": { + "abstraction": [ + "アブストラクション" + ], + "acceptance": [ + "アクセプタンス" + ], + "accepted": [ + "アクセプテッド" + ], + "acronym": [ + "アクロニム" + ], + "actual": [ + "アクチュアル" + ], + "adapter": [ + "アダプター" + ], + "agent": [ + "エージェント" + ], + "airline": [ + "エアライン" + ], + "alpha": [ + "アルファ" + ], + "amadeus": [ + "アマデウス" + ], + "application": [ + "アプリケーション" + ], + "applicator": [ + "アプリケーター" + ], + "auto": [ + "オート" + ], + "backend": [ + "バックエンド" + ], + "bank": [ + "バンク" + ], + "both": [ + "ボース" + ], + "branch": [ + "ブランチ" + ], + "browser": [ + "ブラウザー" + ], + "build": [ + "ビルド" + ], + "cache": [ + "キャッシュ" + ], + "cannot": [ + "キャノット" + ], + "changes": [ + "チェンジズ" + ], + "chat": [ + "チャット" + ], + "checks": [ + "チェックス" + ], + "code": [ + "コード" + ], + "companion": [ + "コンパニオン" + ], + "complete": [ + "コンプリート" + ], + "component": [ + "コンポーネント" + ], + "components": [ + "コンポーネンツ" + ], + "con": [ + "コン" + ], + "configuration": [ + "コンフィギュレーション" + ], + "contract": [ + "コントラクト" + ], + "conversation": [ + "コンバーセーション" + ], + "core": [ + "コア" + ], + "desktop": [ + "デスクトップ" + ], + "developer": [ + "デベロッパー" + ], + "direct": [ + "ダイレクト" + ], + "docs": [ + "ドックス" + ], + "does": [ + "ダズ" + ], + "each": [ + "イーチ" + ], + "exact": [ + "エグザクト" + ], + "execution": [ + "エグゼキューション" + ], + "existing": [ + "エグジスティング" + ], + "explicit": [ + "エクスプリシット" + ], + "extension": [ + "エクステンション" + ], + "external": [ + "エクスターナル" + ], + "extra": [ + "エクストラ" + ], + "files": [ + "ファイルズ" + ], + "first": [ + "ファースト" + ], + "flash": [ + "フラッシュ" + ], + "follower": [ + "フォロワー" + ], + "game": [ + "ゲーム" + ], + "graph": [ + "グラフ" + ], + "handler": [ + "ハンドラー" + ], + "has": [ + "ハズ" + ], + "host": [ + "ホスト" + ], + "icon": [ + "アイコン" + ], + "identity": [ + "アイデンティティ" + ], + "including": [ + "インクルーディング" + ], + "inference": [ + "インファレンス" + ], + "install": [ + "インストール" + ], + "into": [ + "イントゥ" + ], + "ion": [ + "イオン" + ], + "its": [ + "イッツ" + ], + "japanese": [ + "ジャパニーズ" + ], + "keep": [ + "キープ" + ], + "key": [ + "キー" + ], + "learner": [ + "ラーナー" + ], + "let's": [ + "レッツ" + ], + "locked": [ + "ロックト" + ], + "main": [ + "メイン" + ], + "manifest": [ + "マニフェスト" + ], + "may": [ + "メイ" + ], + "mode": [ + "モード" + ], + "native": [ + "ネイティブ" + ], + "official": [ + "オフィシャル" + ], + "one": [ + "ワン" + ], + "optional": [ + "オプショナル" + ], + "original": [ + "オリジナル" + ], + "other": [ + "アザー" + ], + "output": [ + "アウトプット" + ], + "pack": [ + "パック" + ], + "package": [ + "パッケージ" + ], + "passed": [ + "パスト" + ], + "per": [ + "パー" + ], + "playback": [ + "プレイバック" + ], + "policy": [ + "ポリシー" + ], + "probe": [ + "プローブ" + ], + "profiles": [ + "プロファイルズ" + ], + "prompt": [ + "プロンプト" + ], + "proposer": [ + "プロポーザー" + ], + "provider": [ + "プロバイダー" + ], + "python": [ + "パイソン" + ], + "rag": [ + "ラグ" + ], + "real": [ + "リアル" + ], + "reliably": [ + "リライアブリー" + ], + "remain": [ + "リメイン" + ], + "remains": [ + "リメインズ" + ], + "render": [ + "レンダー" + ], + "request": [ + "リクエスト" + ], + "restart": [ + "リスタート" + ], + "retain": [ + "リテイン" + ], + "selected": [ + "セレクテッド" + ], + "selection": [ + "セレクション" + ], + "semantic": [ + "セマンティック" + ], + "settings": [ + "セッティングス" + ], + "shared": [ + "シェアード" + ], + "smoke": [ + "スモーク" + ], + "snapshot": [ + "スナップショット" + ], + "source": [ + "ソース" + ], + "speech": [ + "スピーチ" + ], + "startup": [ + "スタートアップ" + ], + "status": [ + "ステータス" + ], + "still": [ + "スティル" + ], + "sync": [ + "シンク" + ], + "tests": [ + "テスツ" + ], + "their": [ + "ゼア" + ], + "then": [ + "ゼン" + ], + "through": [ + "スルー" + ], + "tools": [ + "ツールズ" + ], + "torch": [ + "トーチ" + ], + "unchanged": [ + "アンチェンジド" + ], + "user": [ + "ユーザー" + ], + "wake": [ + "ウェイク" + ], + "wallpaper": [ + "ウォールペーパー" + ], + "was": [ + "ワズ" + ], + "when": [ + "ウェン", + "ホエン" + ], + "window": [ + "ウィンドウ" + ], + "without": [ + "ウィザウト", + "ウィズアウト" + ] + } +} diff --git a/tools/data/japanese_dictionary_source.json b/tools/data/japanese_dictionary_source.json index ba85509f..5abe2e26 100644 --- a/tools/data/japanese_dictionary_source.json +++ b/tools/data/japanese_dictionary_source.json @@ -1,5 +1,5 @@ { - "version": "2026.10.20.2", + "version": "2026.10.20.3", "language": "ja", "source": { "repository": "https://github.com/RVC-Boss/GPT-SoVITS", @@ -33,7 +33,7 @@ "yield all one- or two-letter Latin surfaces to the base Japanese dictionary", "canonicalize every surface using native MeCab input spelling, including unchanged contractions and compounds" ], - "unresolved_policy": "Flag non-kana remnants and out-of-range accent candidates for review; retain them without guessing a replacement. Preserve different legitimate readings of the same word." + "unresolved_policy": "Retain uncertain research rows without guessing replacements. Official pack admission subsequently requires both reading fields to match the separate checked ledger." }, "corrections": [ { @@ -10229,7 +10229,7 @@ "baseline_version": "2026.10.16.2", "baseline_sha256": "91b3a3c1aa37b7eea96b5815b40e48c0f6a15d02136f956d9deb340df35131f2", "baseline_review_entries": 317, - "policy": "Exact bad-reading repairs preserve other lexical alternatives. Unconfirmed lexical transcriptions or accent nuclei are retained. Native compilation and labels do not replace human listening.", + "policy": "Historical research disposition only: exact bad-reading repairs preserve alternatives and unconfirmed readings in research inputs. Official release admission separately requires an exact checked pair in release_review; no automatic inclusion. Native labels do not replace human listening.", "lexical_repairs": 94, "representation_repairs": 65, "retained_entries": 158, @@ -10281,5 +10281,10 @@ "flag_list_sha256": "b8446675edbd1ff0796b76ad6167e8e20266b198a9829d8f60bc0e37e0f73e2a" }, "latin_word_boundary": "U+3000 at both edges" + }, + "release_review": { + "path": "tools/data/japanese_dictionary_reviewed_readings.json", + "sha256": "ebd8d61559add6efabbbe63cbc758be887982593e15c6e214f1769e78ca68a59", + "policy": "Admit only the exact individually checked word/reading pairs, after existing base ownership and CMU quarantine. Unreviewed source/correction rows remain local research data; never promote another unchecked variant." } } diff --git a/tools/data/japanese_reading_cases.json b/tools/data/japanese_reading_cases.json index 97e423e2..f1e153d5 100644 --- a/tools/data/japanese_reading_cases.json +++ b/tools/data/japanese_reading_cases.json @@ -1,5 +1,5 @@ { - "description": "Synthetic contrastive Japanese reading cases. Expected lexical readings are acceptance targets, not a claim of native-speaker listening acceptance. Multiple accepted forms record real ambiguity or orthographic conventions.", + "description": "Contrastive Japanese reading cases: original synthetic targets plus full external-audit sentences. Expected lexical readings are acceptance targets, not native-speaker/listening sign-off. Multiple accepted forms record ambiguity or orthographic conventions.", "cases": [ { "text": "彼は上手に歌う。", @@ -743,6 +743,42 @@ ], "sense": "calendar day", "partition": "additional_contexts" + }, + { + "text": "でも査読を通ったかどうかで価値はまるで違う。", + "surface": "通っ", + "readings": [ + "トオッ" + ], + "sense": "a theorem passed peer review, not commuting; full production-path external audit guard", + "partition": "external_audit_full_sentences" + }, + { + "text": "中立派は「証明の検証負荷が減るから、人間はより本質的な問いに集中できる」と見ているわ。", + "surface": "中立派", + "readings": [ + "チュウリツハ" + ], + "sense": "neutral faction; whole compound target: the base frontend also missegments 中 + 立派", + "partition": "external_audit_full_sentences" + }, + { + "text": "この例での箱は青、玉は5個として続けるわ。", + "surface": "玉", + "readings": [ + "タマ" + ], + "sense": "five balls; retain the digit and the production number conversion, unlike the masked corpus", + "partition": "external_audit_full_sentences" + }, + { + "text": "仕事だけ止めるわ。", + "surface": "止める", + "readings": [ + "トメル" + ], + "sense": "stop the running Work task; context-specific acceptance, not a universal ban on the valid ヤメル reading", + "partition": "external_audit_full_sentences" } ] } diff --git a/tools/prepare_japanese_dictionary.py b/tools/prepare_japanese_dictionary.py index 868980e0..2de5e863 100644 --- a/tools/prepare_japanese_dictionary.py +++ b/tools/prepare_japanese_dictionary.py @@ -21,6 +21,7 @@ from tools.audit_japanese_dictionary import ( # noqa: E402 cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, native_mora_counter, native_surface_normalizer, normalize_rows, + reviewed_readings, REVIEWED_PATH, ) RECIPE_PATH = PROJECT_ROOT / "tools/data/japanese_dictionary_source.json" @@ -29,6 +30,7 @@ def prepare_dictionary(source: Path, *, project_root: Path = PROJECT_ROOT, recipe_path: Path = RECIPE_PATH, corrections_path: Path = CORRECTIONS_PATH, + reviewed_path: Path = REVIEWED_PATH, overwrite: bool = False, count_mora: Callable[[str], int | None] | None = None, normalize_surface: Callable[[str], str] | None = None, base_word: Callable[..., bool] | None = None) -> Path: @@ -45,6 +47,9 @@ def normalize(value: str) -> str: corrections = {normalize(row[0]): row for row in dictionary_rows(correction_data)} before = {normalize(item["word"]): item["before"] for item in recipe["corrections"]} policy = recipe["extension_filter"] + reviewed, review_data = reviewed_readings(reviewed_path) + if hashlib.sha256(review_data).hexdigest() != recipe["release_review"]["sha256"]: + raise JapaneseDictionaryError("Reviewed reading ledger differs from the pinned recipe") cmu_data = (PROJECT_ROOT / policy["cmudict_path"]).read_bytes().replace(b"\r\n", b"\n") if hashlib.sha256(cmu_data).hexdigest() != policy["cmudict_sha256"]: raise JapaneseDictionaryError("CMU screening data differs from the pinned recipe") @@ -78,12 +83,25 @@ def normalize(value: str) -> str: row = list(correction) corrected.append(row) corrected, normalization = normalize_rows(corrected, count_mora, normalize_surface) - corrected, filtering = filter_extension_rows(corrected, cmu_pronunciations(cmu_data), base_word) + corrected, filtering = filter_extension_rows(corrected, cmu_pronunciations(cmu_data), base_word, reviewed) + # Rejected readings are local audit evidence, not shipped data. Keep enough + # provenance to reproduce the exclusion set from pinned sources and ledger. + excluded = filtering.pop("excluded") + filtering["base_owned_surfaces"] = sorted({item["surface"] for item in excluded + if "base_dictionary_word" in item["reasons"]}) + filtering["excluded_rows_sha256"] = hashlib.sha256( + json.dumps(excluded, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode()).hexdigest() buffer = io.StringIO(newline="") csv.writer(buffer, lineterminator="\n").writerows(corrected) derivative = buffer.getvalue().encode("utf-8") - metadata = {**recipe, "sha256": hashlib.sha256(derivative).hexdigest(), "entry_count": len(corrected), + metadata = {**recipe, + "corrections": [item for item in recipe["corrections"] + if item["after"] in reviewed.get(normalize(item["word"]), ())], + "sha256": hashlib.sha256(derivative).hexdigest(), "entry_count": len(corrected), "corrections_sha256": hashlib.sha256(correction_data).hexdigest(), + "release_review": {**recipe["release_review"], + "review_method": json.loads(review_data)["review_method"], + "accepted_readings": {word: sorted(values) for word, values in reviewed.items()}}, "normalization": {**recipe["normalization"], "results": {key: value for key, value in normalization.items() if key != "unresolved"}}, "extension_filter": {**policy, "results": filtering}, diff --git a/tools/validate_japanese_dictionary.py b/tools/validate_japanese_dictionary.py index 72f796c5..ed124fa5 100644 --- a/tools/validate_japanese_dictionary.py +++ b/tools/validate_japanese_dictionary.py @@ -2,7 +2,6 @@ from __future__ import annotations import argparse -from collections import defaultdict import hashlib import json from pathlib import Path @@ -13,7 +12,8 @@ from config.japanese_dictionary import LATIN_WORD_BOUNDARY, PACK_TREE, dictionary_rows, load_dictionary_pack # noqa: E402 from tools.audit_japanese_dictionary import ( # noqa: E402 - cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, word_key, + cmu_pronunciations, filter_extension_rows, native_base_hashes, native_base_word_lookup, + reviewed_readings, word_key, ) from tools.tts_text_processor import correct_pronunciation_for_tts # noqa: E402 from tts.japanese_dictionary import configure_japanese_dictionary, with_latin_word_boundaries # noqa: E402 @@ -36,7 +36,6 @@ "花が咲き、鼻がむずむずする。", "新しい本を読む。", "研究結果を発表します。", "音声合成の品質を確認する。", "こんにちは、元気ですか。", "猫-犬。", ) -CONTRACTIONS = ("don't", "won't", "I'm", "I've", "we're", "they're", "isn't", "can't", "wouldn't") UNIT_SAMPLES = tuple("100" + unit + "です" for unit in ( "g", "mg", "kg", "t", "m", "cm", "mm", "km", "ml", "mL", "L", "kW", "MW", "W", "V", "mV", "A", "mA", "Hz", "kHz", "MHz", "GHz", "s", "ms", "ns", "kB", "MB", "GB", "TB", "dB", "rpm", "℃", @@ -45,6 +44,9 @@ "Helpを押して。": "help", "このbugからlearnしましょう。": "learn", "Twoつ目の方法。": "two", "Python with Docker。": "with", "Productionに出す。": "production", "モデルのresetをする。": "reset", **{f"この{word}を確認して。": word for word in ("model", "welcome", "directly", "reset", "deadline")}, + **{f"この{word}を確認して。": word for word in ( + "true", "revision", "during", "behavior", "different", "specific", "later", "earlier", + "private", "credentials", "pass", "changing", "raw", "stay", "want")}, } @@ -68,15 +70,15 @@ def production_phones(text: str) -> list[str]: if hashlib.sha256(cmu_data).hexdigest() != policy["cmudict_sha256"]: raise RuntimeError("Release gate requires the recipe's CMU screening data") cmu = cmu_pronunciations(cmu_data) - correction_data = (ROOT / "tools/data/japanese_dictionary_corrections.csv").read_bytes().replace(b"\r\n", b"\n") - if hashlib.sha256(correction_data).hexdigest() != metadata["corrections_sha256"]: - raise RuntimeError("Release gate requires the pack's correction recipe") - corrected = list(dictionary_rows(correction_data)) - excluded = policy["results"]["excluded"] - _, screening = filter_extension_rows(rows, cmu, base_word) + reviewed, review_data = reviewed_readings() + if hashlib.sha256(review_data).hexdigest() != metadata["release_review"]["sha256"]: + raise RuntimeError("Release gate requires the independently tracked checked-reading ledger") + if metadata["release_review"]["accepted_readings"] != {word: sorted(values) for word, values in reviewed.items()}: + raise RuntimeError("Pack's checked-reading declaration differs from the source ledger") + _, screening = filter_extension_rows(rows, cmu, base_word, reviewed) vocabulary_data = json.loads((ROOT / "tools/data/japanese_dictionary_common_words.json").read_text(encoding="utf-8")) common = vocabulary_data["words"] - base_owned = sorted({item["surface"] for item in excluded if "base_dictionary_word" in item["reasons"]}) + base_owned = policy["results"]["base_owned_surfaces"] jtalk = pyopenjtalk.OpenJTalk(pyopenjtalk.OPEN_JTALK_DICT_DIR) surface = jtalk.normalize_for_mecab @@ -89,15 +91,22 @@ def lexical_surface(value: str) -> str: def target_readings(text: str, word: str) -> list[str]: return [f["read"] for f in pyopenjtalk.run_frontend(native_text(text)) if lexical_surface(f["string"]) == surface(word)] - expected = defaultdict(set) - for row in rows: - expected[lexical_surface(row[0])].add(row[11]) + released = {word_key(row[0]) for row in rows} + # Observe nodes selected by clean_text itself: capitalisation, abbreviation + # conversion, punctuation and number handling must match production. + original_frontend, observed_nodes = japanese.run_frontend, [] + + def observe(jtalk, text): + features = original_frontend(jtalk, text) + observed_nodes.extend(features) + return features failures, contexts, frontend_words = [], 0, 0 if screening["excluded_entries"]: failures.append({"kind": "unscreened_entries", "entries": screening["excluded"]}) if screening["latin_word_boundaries_added"]: failures.append({"kind": "missing_word_boundaries", "count": screening["latin_word_boundaries_added"]}) try: + japanese.run_frontend = observe configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=False) baseline = {text: production_phones(text) for text in BASE_SAMPLES} unit_baseline = {text: production_phones(text) for text in UNIT_SAMPLES} @@ -106,7 +115,8 @@ def target_readings(text: str, word: str) -> list[str]: base_controls = {prefix + word + suffix for word in base_owned for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。"))} english_controls = base_controls | set(common) | set(REPORTED_CONTEXTS) - space_controls = base_controls | set(BASE_SAMPLES) | set(UNIT_SAMPLES) | set(vocabulary_data["base_fallback_words"]) + fallback_words = {word for word in common if word_key(word) not in released} + space_controls = base_controls | set(BASE_SAMPLES) | set(UNIT_SAMPLES) | fallback_words english_baseline = {text: production_phones(text) for text in sorted(english_controls)} context_baseline = {text: target_readings(text, word) for text, word in REPORTED_CONTEXTS.items()} status = configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=True) @@ -135,20 +145,19 @@ def target_readings(text: str, word: str) -> list[str]: # accent correction or a per-unit exclusion. unit_prosody_changes.append({"text": text, "before": before, "after": after}) vocabulary = [] - quarantined = {item["surface"] for item in excluded} - {word_key(row[0]) for row in rows} for text, before in english_baseline.items(): after = production_phones(text) - must_preserve = text in base_controls or text in vocabulary_data["base_fallback_words"] + must_preserve = text in base_controls or text in fallback_words if must_preserve and after != before: failures.append({"kind": "english_base_regression", "text": text, "before": before, "after": after}) if text in common: observed = pyopenjtalk.run_frontend(native_text(text)) - reading = vocabulary_data["reviewed_readings"].get(text) + reading = vocabulary_data["base_readings"].get(text) if reading and [f["read"] for f in observed] != [reading]: failures.append({"kind": "reviewed_english_reading", "text": text, "expected": reading, "observed": observed}) vocabulary.append({"word": text, "base_owned": word_key(text) in base_owned, - "quarantined": word_key(text) in quarantined, + "released": word_key(text) in released, "changed": after != before, "readings": [f["read"] for f in observed]}) sentence_results = [] for text, word in REPORTED_CONTEXTS.items(): @@ -160,19 +169,16 @@ def target_readings(text: str, word: str) -> list[str]: # with sentence). Keep the full production-path observation visible. sentence_results.append({"text": text, "word": word, "before": before, "after": after, "sentence_phones_changed": production_phones(text) != english_baseline[text]}) - words = {row[0]: row[0] for row in corrected} - words.update({surface(word): word for word in CONTRACTIONS}) - for key, word in words.items(): - key = surface(key.lower()) - if key not in expected: - # Corrections are historical evidence, not mandatory overrides - # of the base or of conservative release-data exclusions. - continue + # Every released word has a lexical oracle independent of its CSV. There + # is no coverage-only branch for words admitted into the official pack. + for word in sorted(released): + key = surface(word) for prefix, suffix in (("", ""), ("", "を確認する。"), ("これは", "です。")): - whole = [f for f in pyopenjtalk.run_frontend(with_latin_word_boundaries(prefix + word.lower() + suffix)) - if lexical_surface(f["string"]) == key] + observed_nodes.clear() + phones = production_phones(prefix + word + suffix) + whole = [f for f in observed_nodes if lexical_surface(f["string"]) == key] contexts += 1 - if len(whole) != 1 or whole[0]["read"] not in expected[key]: + if len(whole) != 1 or whole[0]["read"] not in reviewed[word] or not phones: failures.append({"kind": "whole_word", "word": word, "prefix": prefix, "suffix": suffix, "observed": whole}) # Use the actual production prosody path, not only native row lookup. phones = japanese.g2p(word) @@ -186,12 +192,15 @@ def target_readings(text: str, word: str) -> list[str]: "additional_unit_phoneme_checks": len(UNIT_SAMPLES), "base_owned_words": len(base_owned), "english_base_contexts": len(base_controls), "common_english_words": len(common), "common_english_results": vocabulary, + "released_checked_words": len(released), "unchanged_common_fallbacks": len(fallback_words), + "checked_ledger_words": len(reviewed), "external_sentence_contexts": len(REPORTED_CONTEXTS), "external_sentence_results": sentence_results, "unit_prosody_changes_for_review": unit_prosody_changes, "production_frontend_words": frontend_words, "failures": failures, "limitations": "Native phoneme/prosody and token-selection checks; no waveform or listening acceptance."} finally: + japanese.run_frontend = original_frontend configure_japanese_dictionary(pyopenjtalk, project_root=project_root, enabled=False) From 0d223162b0476b9c455ead69b9058310133f9dc8 Mon Sep 17 00:00:00 2001 From: Lucas1479 Date: Tue, 20 Oct 2026 19:54:11 +1300 Subject: [PATCH 10/10] docs(tts): clarify context memory costs and unchecked legacy dictionaries --- config/catalog/voice/tts_japanese_reading.json | 4 ++-- docs/japanese_context_reading.md | 16 +++++++++------- docs/japanese_pronunciation_dictionary.md | 8 ++++++++ electron/src/shared/configCatalog.generated.ts | 4 ++-- 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/config/catalog/voice/tts_japanese_reading.json b/config/catalog/voice/tts_japanese_reading.json index 9101e2ea..df679e4d 100644 --- a/config/catalog/voice/tts_japanese_reading.json +++ b/config/catalog/voice/tts_japanese_reading.json @@ -18,8 +18,8 @@ "zh-CN": "使用已安装的语境读音模型" }, "description": { - "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory (external audit about 650–900 MiB); runtime and workload affect usage. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", - "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存(外审约 650–900 MiB),随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory; usage varies with runtime and workload. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", + "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存,用量随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" }, "default": false, "accepted_values": [ diff --git a/docs/japanese_context_reading.md b/docs/japanese_context_reading.md index cfccfeb9..25dda8fd 100644 --- a/docs/japanese_context_reading.md +++ b/docs/japanese_context_reading.md @@ -25,16 +25,16 @@ python tools/external_assets.py install output/amadeus-japanese-context-reading- The Voice setting **Use installed context-reading model** maps to `ENABLE_JAPANESE_CONTEXT_READING`. It **defaults off**, even when the model is installed. It never installs/downloads one. Known reading regressions remain, so enabling it is an explicit experiment. -CPU inference adds hundreds of MiB of process memory; the external corpus audit -reported roughly 650–900 MiB extra. Runtime and workload change the actual cost. +CPU inference adds hundreds of MiB of process memory. Usage varies with runtime, +workload and the memory metric being measured. Missing optional data is normal and inactive. Restart the backend after installing, updating or changing this setting. Turning it off uses the existing native reading selection; the English dictionary has its own independent switch. Invalid/incomplete data or unsupported native APIs are reported and leave the existing frontend usable. -默认关闭,安装后也不自动启用;仍有已知误读。CPU 推理会增加数百 MiB 进程内存 -(外部语料审查约 650–900 MiB,实际取决于运行时和负载)。更改设置后须重启 +默认关闭,安装后也不自动启用;仍有已知误读。CPU 推理会增加数百 MiB 进程内存, +实际取决于运行时、负载与统计口径。更改设置后须重启 后端。两个资源独立安装、独立关闭,均不自动下载。 Startup verifies all three source hashes and loads only local files, explicitly @@ -145,9 +145,11 @@ and corpus execution. In this run, model loading added about **312 MiB RSS / 314 MiB private**, and the final increment was about **323 / 318 MiB** over the warmed base. Absolute final process memory was about **544 MiB RSS / 897 MiB private**. These are separate metrics, not interchangeable memory limits. The -external reviewer observed a larger increase; retain a conservative warning -rather than treating this one run as a guaranteed maximum. No memory reduction -is claimed. Warm median base/model time was **0.595 / 1.125 ms**, p95 +external reviewer confirmed that their measurement used a different baseline; +its earlier range is not directly comparable to this increment. Describe the +cost as hundreds of MiB, varying with runtime/workload, rather than a fixed +range or guaranteed maximum. No memory reduction is claimed. +Warm median base/model time was **0.595 / 1.125 ms**, p95 **1.23 / 8.91 ms**; loading took **0.82 s**. Corpus/model-candidate coverage changes timing substantially. The 203 changed phone sequences are observations, not 203 proven improvements, and are a different metric from changed readings. diff --git a/docs/japanese_pronunciation_dictionary.md b/docs/japanese_pronunciation_dictionary.md index eb8fb35b..7f5538f9 100644 --- a/docs/japanese_pronunciation_dictionary.md +++ b/docs/japanese_pronunciation_dictionary.md @@ -54,6 +54,11 @@ messages; installation status is not evidence of native activation. `GPT_SoVITS/text/ja_userdic/userdict.csv` remains a supported local source. An installed pack replaces that legacy source's role; an invalid installed pack is reported instead of silently switching back to the legacy copy. + The legacy CSV is loaded as supplied: the official pack's checked-reading + ledger does not filter it. An old full upstream copy therefore retains its + known shifted readings. To use the checked subset, install the official pack; + to use only the base dictionary, set `ENABLE_JAPANESE_USER_DICTIONARY=false` + and restart the backend. This preserves existing local-source compatibility. - Compilation writes only `.cache/japanese_dictionary/`, keyed by source bytes, native runtime version and the installed system dictionary identity. The application does not rewrite or distribute personal/legacy CSVs or binaries. @@ -72,6 +77,9 @@ messages; installation status is not evidence of native activation. `TTS_PYTHON` environment is not silently adapted or claimed fully supported. 用户 CSV 与正式包分开,安装和升级不会覆盖。旧词典目录也不会被删除或改写。 +没有正式包且本机存在旧 CSV 时,仍会加载整份旧词典,其中的已知错读不会被 +正式包的审核清单过滤。安装正式包可改用审核子集;关闭 +`ENABLE_JAPANESE_USER_DICTIONARY` 并重启后端可仅使用基础词典。 编译结果是本机缓存,不能作为可维护的词典源。个人重复词条的优先级须通过 OpenJTalk 词条代价控制;文件排列顺序不保证覆盖。 diff --git a/electron/src/shared/configCatalog.generated.ts b/electron/src/shared/configCatalog.generated.ts index c49f3d48..44b3f196 100644 --- a/electron/src/shared/configCatalog.generated.ts +++ b/electron/src/shared/configCatalog.generated.ts @@ -3163,8 +3163,8 @@ export const catalogGroups: CatalogGroup[] = [ "zh-CN": "使用已安装的语境读音模型" }, "description": { - "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory (external audit about 650–900 MiB); runtime and workload affect usage. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", - "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存(外审约 650–900 MiB),随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" + "en-US": "Default off; known context errors remain. CPU inference adds hundreds of MiB of process memory; usage varies with runtime and workload. Install japanese-context-reading separately; no automatic download. Restart the backend after changes.", + "zh-CN": "默认关闭,仍有已知语境误读。CPU 推理会增加数百 MiB 进程内存,用量随运行时与负载变化。需单独安装 japanese-context-reading,不会自动下载;更改后重启后端生效。" }, "default": false, "accepted_values": [