tujia-llm 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ibaibing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tujia_llm-0.2.0/NOTICE ADDED
@@ -0,0 +1,11 @@
1
+ Usage boundaries — tujia-llm
2
+
3
+ Code in this repository is MIT-licensed (see LICENSE).
4
+
5
+ Model weights — LoRA adapters and merged or quantized checkpoints — are provided
6
+ for research use only. No rights are granted to the training corpora, the source
7
+ recordings, or their speakers, and none of that material is distributed here.
8
+
9
+ The base model (Qwen/Qwen3-8B) keeps its own license (Apache-2.0) and must retain
10
+ its attribution wherever these weights or results derived from them are
11
+ redistributed or published.
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: tujia-llm
3
+ Version: 0.2.0
4
+ Summary: Tujia language generation with a LoRA adapter on Qwen3-8B (MLX, Apple Silicon)
5
+ Author: ibaibing
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ibaibing/tujia-llm
8
+ Project-URL: Model (Hugging Face), https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2
9
+ Project-URL: Model (ModelScope), https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2
10
+ Keywords: tujia,low-resource,language,mlx,lora
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Topic :: Text Processing :: Linguistic
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ License-File: NOTICE
19
+ Requires-Dist: huggingface_hub>=0.24
20
+ Requires-Dist: mlx-lm>=0.21; sys_platform == "darwin" and platform_machine == "arm64"
21
+ Dynamic: license-file
22
+
23
+ # tujia-llm
24
+
25
+ LoRA weights and evaluation for Tujia language generation. First public release: **v0.2** (research preview). v0.1 was internal only.
26
+
27
+ > **Status: research preview.** Suitable for draft lookup and short-sentence translation, not for
28
+ > authoritative output. Exact word-lookup accuracy is 5.8%; sentence similarity is 0.45.
29
+ > See [evaluation](docs/eval-v0.2.md) for details.
30
+
31
+ ## Model
32
+
33
+ | Item | Value |
34
+ |---|---|
35
+ | Base model | `Qwen/Qwen3-8B` (base, not instruct); MLX conversion `mlx-community/Qwen3-8B-bf16` |
36
+ | Method | LoRA, rank 16, 16 layers, scale 32, dropout 0.05 |
37
+ | Weight size | 74 MB (`adapters.safetensors`) |
38
+ | Training steps | 35,000 + 12,000 continued |
39
+ | Target variety | Northern Tujia, Longshan standard |
40
+ | License | Research use only; see [NOTICE](NOTICE) |
41
+
42
+ ## Download
43
+
44
+ | Channel | Location |
45
+ |---|---|
46
+ | ModelScope (MLX) | [TujiaNLP/tujia-llm-8b-v0.2](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2) |
47
+ | ModelScope (GGUF) | [TujiaNLP/tujia-llm-8b-v0.2-gguf](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2-gguf) |
48
+ | Hugging Face | [MLX adapter](https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2); GGUF repo is metadata only |
49
+ | GitHub Releases | `github.com/ibaibing/tujia-llm/releases` → `tujia-llm-8b-v0.2.tar.gz` |
50
+ | PyPI | `pip install tujia-llm` (Apple Silicon) |
51
+
52
+ ## Install
53
+
54
+ **Requirements:** macOS 13+, Apple Silicon, Python 3.10+.
55
+
56
+ | Memory | Flag | Download | Notes |
57
+ |---|---|---|---|
58
+ | 32 GB+ | default (`bf16`) | ~15 GB | Reference quality |
59
+ | 16 GB | `--precision 4bit` | ~4.5 GB | Runs, but output degrades visibly (repetition, tone errors) |
60
+
61
+ ```bash
62
+ pip install tujia-llm # after the PyPI release
63
+ pip install dist/tujia_llm-0.2.0-py3-none-any.whl # offline / local wheel
64
+ ```
65
+
66
+ The wheel contains code only. Weights (74 MB) and the base model are downloaded on first run.
67
+
68
+ ## CLI
69
+
70
+ ```bash
71
+ tujia-llm info # backend, precision, memory
72
+ tujia-llm lookup 酒
73
+ tujia-llm translate "大家都过好日子。"
74
+ tujia-llm translate "大家都过好日子,土家人们都相好。" --split
75
+ tujia-llm translate "zɤ³⁵" --to zh
76
+ tujia-llm translate "zɤ³⁵" --to en
77
+ ```
78
+
79
+ Options: `--adapter` (local directory or HF repo id), `--precision auto|bf16|4bit`,
80
+ `--max-tokens`, `--json`.
81
+
82
+ ```bash
83
+ tujia-llm lookup 酒 --adapter /path/to/adapter-dir --json
84
+ ```
85
+
86
+ For development, `python scripts/infer.py` offers the same prompts without installation.
87
+
88
+ ## Windows / CPU (GGUF)
89
+
90
+ The MLX build is Apple Silicon only. For Windows, Linux, and Intel Macs use the GGUF build
91
+ with llama.cpp or Ollama — see [docs/windows-gguf.md](docs/windows-gguf.md).
92
+
93
+ | File | Size | Memory |
94
+ |---|---|---|
95
+ | `gguf/tujia-llm-8b-v0.2-Q4_K_M.gguf` | 4.7 GB | 16 GB+ |
96
+ | `gguf/tujia-llm-8b-v0.2-f16.gguf` | 15 GB | 20 GB+ |
97
+
98
+ ```powershell
99
+ ollama create tujia -f gguf/Modelfile
100
+ ollama run tujia "「酒」用土家语怎么说?"
101
+ ```
102
+
103
+ ## Capabilities and limits
104
+
105
+ **Works**
106
+
107
+ - Chinese word → Tujia form + IPA (reliable for high-frequency words)
108
+ - Chinese short sentence → Tujia sentence (stable up to roughly 8 syllables)
109
+ - Tujia sentence → English
110
+
111
+ **Does not work**
112
+
113
+ - Authoritative translation, publication-grade text, legal or medical use
114
+ - Folk songs, ritual language, and other low-resource registers
115
+ - Southern Tujia (Luxi) and non-Longshan varieties such as Baojing
116
+ - Out-of-vocabulary words: there is no refusal mechanism, so the model will invent forms
117
+
118
+ ## Evaluation summary
119
+
120
+ | Metric | Base | v0.2 |
121
+ |---|---|---|
122
+ | Lexicon 500, first-candidate exact | 0% | 5.8% |
123
+ | Lexicon 500, edit similarity | 0.020* | 0.511 |
124
+ | Single-entry 250, similarity | 0.024* | 0.449 |
125
+ | Sentences 100, similarity (split decoding) | 0.082 | 0.451 |
126
+ | Song lyrics, 12 lines, similarity | — | 0.583 |
127
+
128
+ \* Base model evaluated on the first 100 items only, so not directly comparable to the full sets.
129
+
130
+ Full tables, dialect breakdowns, and failure modes: [docs/eval-v0.2.md](docs/eval-v0.2.md).
131
+
132
+ ## Prompt format
133
+
134
+ The model was trained on four fixed templates. Keep them unchanged at inference time:
135
+
136
+ ```
137
+ 「{Chinese word}」用土家语怎么说?
138
+ 把这句话翻译成土家语:{Chinese sentence}
139
+ 把这句话翻译成汉语:{Tujia sentence}
140
+ 把这句话翻译成英语:{Tujia sentence}
141
+ ```
142
+
143
+ Output uses mixed Ye pinyin / IPA notation with superscript tone digits, e.g. `zɤ³⁵`.
144
+
145
+ ## Layout
146
+
147
+ ```
148
+ scripts/infer.py inference example
149
+ docs/eval-v0.2.md evaluation report
150
+ release/ release config and upload script
151
+ MODEL_CARD.md model card
152
+ NOTICE usage boundaries
153
+ ```
154
+
155
+ ## Citation
156
+
157
+ ```bibtex
158
+ @misc{tujia-llm-v0.1,
159
+ title = {tujia-llm: LoRA adapter for Tujia language generation (v0.2)},
160
+ author = {ibaibing},
161
+ year = {2026},
162
+ note = {Research preview}
163
+ }
164
+ ```
@@ -0,0 +1,142 @@
1
+ # tujia-llm
2
+
3
+ LoRA weights and evaluation for Tujia language generation. First public release: **v0.2** (research preview). v0.1 was internal only.
4
+
5
+ > **Status: research preview.** Suitable for draft lookup and short-sentence translation, not for
6
+ > authoritative output. Exact word-lookup accuracy is 5.8%; sentence similarity is 0.45.
7
+ > See [evaluation](docs/eval-v0.2.md) for details.
8
+
9
+ ## Model
10
+
11
+ | Item | Value |
12
+ |---|---|
13
+ | Base model | `Qwen/Qwen3-8B` (base, not instruct); MLX conversion `mlx-community/Qwen3-8B-bf16` |
14
+ | Method | LoRA, rank 16, 16 layers, scale 32, dropout 0.05 |
15
+ | Weight size | 74 MB (`adapters.safetensors`) |
16
+ | Training steps | 35,000 + 12,000 continued |
17
+ | Target variety | Northern Tujia, Longshan standard |
18
+ | License | Research use only; see [NOTICE](NOTICE) |
19
+
20
+ ## Download
21
+
22
+ | Channel | Location |
23
+ |---|---|
24
+ | ModelScope (MLX) | [TujiaNLP/tujia-llm-8b-v0.2](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2) |
25
+ | ModelScope (GGUF) | [TujiaNLP/tujia-llm-8b-v0.2-gguf](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2-gguf) |
26
+ | Hugging Face | [MLX adapter](https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2); GGUF repo is metadata only |
27
+ | GitHub Releases | `github.com/ibaibing/tujia-llm/releases` → `tujia-llm-8b-v0.2.tar.gz` |
28
+ | PyPI | `pip install tujia-llm` (Apple Silicon) |
29
+
30
+ ## Install
31
+
32
+ **Requirements:** macOS 13+, Apple Silicon, Python 3.10+.
33
+
34
+ | Memory | Flag | Download | Notes |
35
+ |---|---|---|---|
36
+ | 32 GB+ | default (`bf16`) | ~15 GB | Reference quality |
37
+ | 16 GB | `--precision 4bit` | ~4.5 GB | Runs, but output degrades visibly (repetition, tone errors) |
38
+
39
+ ```bash
40
+ pip install tujia-llm # after the PyPI release
41
+ pip install dist/tujia_llm-0.2.0-py3-none-any.whl # offline / local wheel
42
+ ```
43
+
44
+ The wheel contains code only. Weights (74 MB) and the base model are downloaded on first run.
45
+
46
+ ## CLI
47
+
48
+ ```bash
49
+ tujia-llm info # backend, precision, memory
50
+ tujia-llm lookup 酒
51
+ tujia-llm translate "大家都过好日子。"
52
+ tujia-llm translate "大家都过好日子,土家人们都相好。" --split
53
+ tujia-llm translate "zɤ³⁵" --to zh
54
+ tujia-llm translate "zɤ³⁵" --to en
55
+ ```
56
+
57
+ Options: `--adapter` (local directory or HF repo id), `--precision auto|bf16|4bit`,
58
+ `--max-tokens`, `--json`.
59
+
60
+ ```bash
61
+ tujia-llm lookup 酒 --adapter /path/to/adapter-dir --json
62
+ ```
63
+
64
+ For development, `python scripts/infer.py` offers the same prompts without installation.
65
+
66
+ ## Windows / CPU (GGUF)
67
+
68
+ The MLX build is Apple Silicon only. For Windows, Linux, and Intel Macs use the GGUF build
69
+ with llama.cpp or Ollama — see [docs/windows-gguf.md](docs/windows-gguf.md).
70
+
71
+ | File | Size | Memory |
72
+ |---|---|---|
73
+ | `gguf/tujia-llm-8b-v0.2-Q4_K_M.gguf` | 4.7 GB | 16 GB+ |
74
+ | `gguf/tujia-llm-8b-v0.2-f16.gguf` | 15 GB | 20 GB+ |
75
+
76
+ ```powershell
77
+ ollama create tujia -f gguf/Modelfile
78
+ ollama run tujia "「酒」用土家语怎么说?"
79
+ ```
80
+
81
+ ## Capabilities and limits
82
+
83
+ **Works**
84
+
85
+ - Chinese word → Tujia form + IPA (reliable for high-frequency words)
86
+ - Chinese short sentence → Tujia sentence (stable up to roughly 8 syllables)
87
+ - Tujia sentence → English
88
+
89
+ **Does not work**
90
+
91
+ - Authoritative translation, publication-grade text, legal or medical use
92
+ - Folk songs, ritual language, and other low-resource registers
93
+ - Southern Tujia (Luxi) and non-Longshan varieties such as Baojing
94
+ - Out-of-vocabulary words: there is no refusal mechanism, so the model will invent forms
95
+
96
+ ## Evaluation summary
97
+
98
+ | Metric | Base | v0.2 |
99
+ |---|---|---|
100
+ | Lexicon 500, first-candidate exact | 0% | 5.8% |
101
+ | Lexicon 500, edit similarity | 0.020* | 0.511 |
102
+ | Single-entry 250, similarity | 0.024* | 0.449 |
103
+ | Sentences 100, similarity (split decoding) | 0.082 | 0.451 |
104
+ | Song lyrics, 12 lines, similarity | — | 0.583 |
105
+
106
+ \* Base model evaluated on the first 100 items only, so not directly comparable to the full sets.
107
+
108
+ Full tables, dialect breakdowns, and failure modes: [docs/eval-v0.2.md](docs/eval-v0.2.md).
109
+
110
+ ## Prompt format
111
+
112
+ The model was trained on four fixed templates. Keep them unchanged at inference time:
113
+
114
+ ```
115
+ 「{Chinese word}」用土家语怎么说?
116
+ 把这句话翻译成土家语:{Chinese sentence}
117
+ 把这句话翻译成汉语:{Tujia sentence}
118
+ 把这句话翻译成英语:{Tujia sentence}
119
+ ```
120
+
121
+ Output uses mixed Ye pinyin / IPA notation with superscript tone digits, e.g. `zɤ³⁵`.
122
+
123
+ ## Layout
124
+
125
+ ```
126
+ scripts/infer.py inference example
127
+ docs/eval-v0.2.md evaluation report
128
+ release/ release config and upload script
129
+ MODEL_CARD.md model card
130
+ NOTICE usage boundaries
131
+ ```
132
+
133
+ ## Citation
134
+
135
+ ```bibtex
136
+ @misc{tujia-llm-v0.1,
137
+ title = {tujia-llm: LoRA adapter for Tujia language generation (v0.2)},
138
+ author = {ibaibing},
139
+ year = {2026},
140
+ note = {Research preview}
141
+ }
142
+ ```
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "tujia-llm"
7
+ version = "0.2.0"
8
+ description = "Tujia language generation with a LoRA adapter on Qwen3-8B (MLX, Apple Silicon)"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "ibaibing" }]
13
+ keywords = ["tujia", "low-resource", "language", "mlx", "lora"]
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: MacOS :: MacOS X",
18
+ "Topic :: Text Processing :: Linguistic",
19
+ ]
20
+ dependencies = [
21
+ "huggingface_hub>=0.24",
22
+ "mlx-lm>=0.21; sys_platform == 'darwin' and platform_machine == 'arm64'",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://github.com/ibaibing/tujia-llm"
27
+ "Model (Hugging Face)" = "https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2"
28
+ "Model (ModelScope)" = "https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2"
29
+
30
+ [project.scripts]
31
+ tujia-llm = "tujia_llm.cli:main"
32
+
33
+ [tool.setuptools.packages.find]
34
+ include = ["tujia_llm*"]
35
+
36
+ [tool.setuptools]
37
+ license-files = ["LICENSE", "NOTICE"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ """Tujia language generation (research preview)."""
2
+
3
+ __version__ = "0.2.0"
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
@@ -0,0 +1,47 @@
1
+ """MLX inference backend."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+
6
+
7
+ class MlxBackend:
8
+ def __init__(self, base: str, adapter: str, max_tokens: int = 96):
9
+ from mlx_lm import generate, load
10
+
11
+ self._generate = generate
12
+ self.max_tokens = max_tokens
13
+ self.model, self.tok = load(base, adapter_path=adapter)
14
+
15
+ def ask(self, prompt: str, max_tokens: int | None = None) -> str:
16
+ messages = [{"role": "user", "content": prompt}]
17
+ templated = self.tok.apply_chat_template(
18
+ messages, add_generation_prompt=True, enable_thinking=False
19
+ )
20
+ out = self._generate(
21
+ self.model,
22
+ self.tok,
23
+ prompt=templated,
24
+ max_tokens=max_tokens or self.max_tokens,
25
+ verbose=False,
26
+ )
27
+ return re.sub(r"^.*?</think>", "", out, flags=re.S).strip()
28
+
29
+
30
+ def resolve_adapter(spec: str | None, default_repo: str) -> str:
31
+ """Accept a local directory, a local file, or a Hugging Face repo id.
32
+
33
+ Cached snapshots are preferred so repeated runs work offline.
34
+ """
35
+ from huggingface_hub import snapshot_download
36
+
37
+ if spec:
38
+ p = Path(spec).expanduser()
39
+ if p.exists():
40
+ return str(p)
41
+
42
+ repo = spec or default_repo
43
+ kwargs = dict(repo_id=repo, allow_patterns=["*.safetensors", "*.json"])
44
+ try:
45
+ return snapshot_download(local_files_only=True, **kwargs)
46
+ except Exception:
47
+ return snapshot_download(**kwargs)
@@ -0,0 +1,102 @@
1
+ """Command-line interface."""
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import re
7
+ import sys
8
+
9
+ from . import __version__
10
+ from .config import (
11
+ DEFAULT_REPO,
12
+ TPL_LOOKUP,
13
+ TPL_TJ2EN,
14
+ TPL_TJ2ZH,
15
+ TPL_ZH2TJ,
16
+ require_mlx,
17
+ settings,
18
+ )
19
+
20
+
21
+ def _common() -> argparse.ArgumentParser:
22
+ p = argparse.ArgumentParser(add_help=False)
23
+ p.add_argument("--adapter", default=argparse.SUPPRESS,
24
+ help="local adapter directory/file, or a Hugging Face repo id")
25
+ p.add_argument("--precision", choices=["auto", "bf16", "4bit"], default=argparse.SUPPRESS,
26
+ help="auto picks bf16 on machines with 32 GB or more")
27
+ p.add_argument("--max-tokens", type=int, default=argparse.SUPPRESS)
28
+ p.add_argument("--json", action="store_true", default=argparse.SUPPRESS,
29
+ help="machine-readable output")
30
+ return p
31
+
32
+
33
+ def build_parser() -> argparse.ArgumentParser:
34
+ p = argparse.ArgumentParser(
35
+ prog="tujia-llm",
36
+ description="Tujia language generation (research preview).",
37
+ )
38
+ p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
39
+
40
+ common = _common()
41
+ sub = p.add_subparsers(dest="cmd", required=True)
42
+
43
+ lk = sub.add_parser("lookup", parents=[common], help="Chinese word to Tujia form")
44
+ lk.add_argument("word")
45
+
46
+ tr = sub.add_parser("translate", parents=[common], help="translate a sentence")
47
+ tr.add_argument("text")
48
+ tr.add_argument("--to", choices=["tj", "zh", "en"], default="tj",
49
+ help="target language: tj (Tujia), zh (Chinese), en (English)")
50
+ tr.add_argument("--split", action="store_true",
51
+ help="split long Chinese sentences at punctuation before translating")
52
+
53
+ sub.add_parser("info", parents=[common], help="show backend and model settings")
54
+ return p
55
+
56
+
57
+ def _split_zh(text: str) -> list[str]:
58
+ return [x for x in re.split(r"(?<=[,。!?;])", text) if x.strip()]
59
+
60
+
61
+ def run(args) -> dict:
62
+ precision = getattr(args, "precision", "auto")
63
+ max_tokens = getattr(args, "max_tokens", 96)
64
+ adapter = getattr(args, "adapter", "") or os.environ.get("TUJIA_LLM_ADAPTER", "")
65
+
66
+ cfg = settings(precision, DEFAULT_REPO)
67
+
68
+ if args.cmd == "info":
69
+ return {
70
+ "version": __version__,
71
+ "base": cfg.base,
72
+ "precision": cfg.precision,
73
+ "ram_gb": cfg.ram_gb,
74
+ "adapter": adapter or cfg.repo,
75
+ "platform": f"{sys.platform}/{os.uname().machine}",
76
+ }
77
+
78
+ require_mlx()
79
+ from .backend_mlx import MlxBackend, resolve_adapter
80
+
81
+ backend = MlxBackend(cfg.base, resolve_adapter(adapter or None, cfg.repo),
82
+ max_tokens=max_tokens)
83
+
84
+ if args.cmd == "lookup":
85
+ prompt = TPL_LOOKUP.format(zh=args.word)
86
+ return {"input": args.word, "output": backend.ask(prompt)}
87
+
88
+ tpl = {"tj": TPL_ZH2TJ, "zh": TPL_TJ2ZH, "en": TPL_TJ2EN}[args.to]
89
+ if args.to == "tj" and args.split and len(args.text) > 18:
90
+ parts = [backend.ask(tpl.format(text=s), 64) for s in _split_zh(args.text)]
91
+ return {"input": args.text, "output": " ".join(parts), "split": len(parts)}
92
+
93
+ return {"input": args.text, "output": backend.ask(tpl.format(text=args.text))}
94
+
95
+
96
+ def main(argv=None) -> None:
97
+ args = build_parser().parse_args(argv)
98
+ result = run(args)
99
+ if getattr(args, "json", False):
100
+ print(json.dumps(result, ensure_ascii=False))
101
+ else:
102
+ print(result.get("output", result))
@@ -0,0 +1,65 @@
1
+ """Configuration and local environment helpers."""
2
+
3
+ import os
4
+ import subprocess
5
+ import sys
6
+ from dataclasses import dataclass
7
+
8
+ DEFAULT_REPO = "TujiaNLP/tujia-llm-8b-v0.2"
9
+ BASE_BF16 = "mlx-community/Qwen3-8B-bf16"
10
+ BASE_4BIT = "mlx-community/Qwen3-8B-4bit"
11
+
12
+ # Prompt templates must match training exactly.
13
+ TPL_LOOKUP = "「{zh}」用土家语怎么说?"
14
+ TPL_ZH2TJ = "把这句话翻译成土家语:{text}"
15
+ TPL_TJ2ZH = "把这句话翻译成汉语:{text}"
16
+ TPL_TJ2EN = "把这句话翻译成英语:{text}"
17
+
18
+
19
+ @dataclass
20
+ class Settings:
21
+ repo: str
22
+ base: str
23
+ precision: str
24
+ ram_gb: float
25
+
26
+
27
+ def total_ram_gb() -> float:
28
+ """Best-effort physical memory size in GiB."""
29
+ if sys.platform == "darwin":
30
+ try:
31
+ out = subprocess.check_output(["sysctl", "-n", "hw.memsize"], text=True)
32
+ return int(out.strip()) / 2**30
33
+ except Exception:
34
+ pass
35
+ try:
36
+ pages = os.sysconf("SC_PHYS_PAGES")
37
+ size = os.sysconf("SC_PAGE_SIZE")
38
+ return pages * size / 2**30
39
+ except Exception:
40
+ return 0.0
41
+
42
+
43
+ def pick_precision(requested: str) -> str:
44
+ if requested in ("bf16", "4bit"):
45
+ return requested
46
+ # 8B bf16 needs roughly 16 GB for weights plus KV cache.
47
+ return "bf16" if total_ram_gb() >= 32 else "4bit"
48
+
49
+
50
+ def base_model(precision: str) -> str:
51
+ return BASE_BF16 if precision == "bf16" else BASE_4BIT
52
+
53
+
54
+ def settings(precision: str = "auto", repo: str = DEFAULT_REPO) -> Settings:
55
+ p = pick_precision(precision)
56
+ return Settings(repo=repo, base=base_model(p), precision=p, ram_gb=round(total_ram_gb(), 1))
57
+
58
+
59
+ def require_mlx() -> None:
60
+ if sys.platform != "darwin" or os.uname().machine != "arm64":
61
+ raise SystemExit(
62
+ "This build requires Apple Silicon (macOS arm64) because the adapter is in MLX "
63
+ "format. On other platforms, use the GGUF build with llama.cpp or Ollama "
64
+ "(see the README)."
65
+ )
@@ -0,0 +1,164 @@
1
+ Metadata-Version: 2.4
2
+ Name: tujia-llm
3
+ Version: 0.2.0
4
+ Summary: Tujia language generation with a LoRA adapter on Qwen3-8B (MLX, Apple Silicon)
5
+ Author: ibaibing
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ibaibing/tujia-llm
8
+ Project-URL: Model (Hugging Face), https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2
9
+ Project-URL: Model (ModelScope), https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2
10
+ Keywords: tujia,low-resource,language,mlx,lora
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: MacOS :: MacOS X
14
+ Classifier: Topic :: Text Processing :: Linguistic
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ License-File: NOTICE
19
+ Requires-Dist: huggingface_hub>=0.24
20
+ Requires-Dist: mlx-lm>=0.21; sys_platform == "darwin" and platform_machine == "arm64"
21
+ Dynamic: license-file
22
+
23
+ # tujia-llm
24
+
25
+ LoRA weights and evaluation for Tujia language generation. First public release: **v0.2** (research preview). v0.1 was internal only.
26
+
27
+ > **Status: research preview.** Suitable for draft lookup and short-sentence translation, not for
28
+ > authoritative output. Exact word-lookup accuracy is 5.8%; sentence similarity is 0.45.
29
+ > See [evaluation](docs/eval-v0.2.md) for details.
30
+
31
+ ## Model
32
+
33
+ | Item | Value |
34
+ |---|---|
35
+ | Base model | `Qwen/Qwen3-8B` (base, not instruct); MLX conversion `mlx-community/Qwen3-8B-bf16` |
36
+ | Method | LoRA, rank 16, 16 layers, scale 32, dropout 0.05 |
37
+ | Weight size | 74 MB (`adapters.safetensors`) |
38
+ | Training steps | 35,000 + 12,000 continued |
39
+ | Target variety | Northern Tujia, Longshan standard |
40
+ | License | Research use only; see [NOTICE](NOTICE) |
41
+
42
+ ## Download
43
+
44
+ | Channel | Location |
45
+ |---|---|
46
+ | ModelScope (MLX) | [TujiaNLP/tujia-llm-8b-v0.2](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2) |
47
+ | ModelScope (GGUF) | [TujiaNLP/tujia-llm-8b-v0.2-gguf](https://modelscope.cn/models/TujiaNLP/tujia-llm-8b-v0.2-gguf) |
48
+ | Hugging Face | [MLX adapter](https://huggingface.co/TujiaNLP/tujia-llm-8b-v0.2); GGUF repo is metadata only |
49
+ | GitHub Releases | `github.com/ibaibing/tujia-llm/releases` → `tujia-llm-8b-v0.2.tar.gz` |
50
+ | PyPI | `pip install tujia-llm` (Apple Silicon) |
51
+
52
+ ## Install
53
+
54
+ **Requirements:** macOS 13+, Apple Silicon, Python 3.10+.
55
+
56
+ | Memory | Flag | Download | Notes |
57
+ |---|---|---|---|
58
+ | 32 GB+ | default (`bf16`) | ~15 GB | Reference quality |
59
+ | 16 GB | `--precision 4bit` | ~4.5 GB | Runs, but output degrades visibly (repetition, tone errors) |
60
+
61
+ ```bash
62
+ pip install tujia-llm # after the PyPI release
63
+ pip install dist/tujia_llm-0.2.0-py3-none-any.whl # offline / local wheel
64
+ ```
65
+
66
+ The wheel contains code only. Weights (74 MB) and the base model are downloaded on first run.
67
+
68
+ ## CLI
69
+
70
+ ```bash
71
+ tujia-llm info # backend, precision, memory
72
+ tujia-llm lookup 酒
73
+ tujia-llm translate "大家都过好日子。"
74
+ tujia-llm translate "大家都过好日子,土家人们都相好。" --split
75
+ tujia-llm translate "zɤ³⁵" --to zh
76
+ tujia-llm translate "zɤ³⁵" --to en
77
+ ```
78
+
79
+ Options: `--adapter` (local directory or HF repo id), `--precision auto|bf16|4bit`,
80
+ `--max-tokens`, `--json`.
81
+
82
+ ```bash
83
+ tujia-llm lookup 酒 --adapter /path/to/adapter-dir --json
84
+ ```
85
+
86
+ For development, `python scripts/infer.py` offers the same prompts without installation.
87
+
88
+ ## Windows / CPU (GGUF)
89
+
90
+ The MLX build is Apple Silicon only. For Windows, Linux, and Intel Macs use the GGUF build
91
+ with llama.cpp or Ollama — see [docs/windows-gguf.md](docs/windows-gguf.md).
92
+
93
+ | File | Size | Memory |
94
+ |---|---|---|
95
+ | `gguf/tujia-llm-8b-v0.2-Q4_K_M.gguf` | 4.7 GB | 16 GB+ |
96
+ | `gguf/tujia-llm-8b-v0.2-f16.gguf` | 15 GB | 20 GB+ |
97
+
98
+ ```powershell
99
+ ollama create tujia -f gguf/Modelfile
100
+ ollama run tujia "「酒」用土家语怎么说?"
101
+ ```
102
+
103
+ ## Capabilities and limits
104
+
105
+ **Works**
106
+
107
+ - Chinese word → Tujia form + IPA (reliable for high-frequency words)
108
+ - Chinese short sentence → Tujia sentence (stable up to roughly 8 syllables)
109
+ - Tujia sentence → English
110
+
111
+ **Does not work**
112
+
113
+ - Authoritative translation, publication-grade text, legal or medical use
114
+ - Folk songs, ritual language, and other low-resource registers
115
+ - Southern Tujia (Luxi) and non-Longshan varieties such as Baojing
116
+ - Out-of-vocabulary words: there is no refusal mechanism, so the model will invent forms
117
+
118
+ ## Evaluation summary
119
+
120
+ | Metric | Base | v0.2 |
121
+ |---|---|---|
122
+ | Lexicon 500, first-candidate exact | 0% | 5.8% |
123
+ | Lexicon 500, edit similarity | 0.020* | 0.511 |
124
+ | Single-entry 250, similarity | 0.024* | 0.449 |
125
+ | Sentences 100, similarity (split decoding) | 0.082 | 0.451 |
126
+ | Song lyrics, 12 lines, similarity | — | 0.583 |
127
+
128
+ \* Base model evaluated on the first 100 items only, so not directly comparable to the full sets.
129
+
130
+ Full tables, dialect breakdowns, and failure modes: [docs/eval-v0.2.md](docs/eval-v0.2.md).
131
+
132
+ ## Prompt format
133
+
134
+ The model was trained on four fixed templates. Keep them unchanged at inference time:
135
+
136
+ ```
137
+ 「{Chinese word}」用土家语怎么说?
138
+ 把这句话翻译成土家语:{Chinese sentence}
139
+ 把这句话翻译成汉语:{Tujia sentence}
140
+ 把这句话翻译成英语:{Tujia sentence}
141
+ ```
142
+
143
+ Output uses mixed Ye pinyin / IPA notation with superscript tone digits, e.g. `zɤ³⁵`.
144
+
145
+ ## Layout
146
+
147
+ ```
148
+ scripts/infer.py inference example
149
+ docs/eval-v0.2.md evaluation report
150
+ release/ release config and upload script
151
+ MODEL_CARD.md model card
152
+ NOTICE usage boundaries
153
+ ```
154
+
155
+ ## Citation
156
+
157
+ ```bibtex
158
+ @misc{tujia-llm-v0.1,
159
+ title = {tujia-llm: LoRA adapter for Tujia language generation (v0.2)},
160
+ author = {ibaibing},
161
+ year = {2026},
162
+ note = {Research preview}
163
+ }
164
+ ```
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ NOTICE
3
+ README.md
4
+ pyproject.toml
5
+ tujia_llm/__init__.py
6
+ tujia_llm/__main__.py
7
+ tujia_llm/backend_mlx.py
8
+ tujia_llm/cli.py
9
+ tujia_llm/config.py
10
+ tujia_llm.egg-info/PKG-INFO
11
+ tujia_llm.egg-info/SOURCES.txt
12
+ tujia_llm.egg-info/dependency_links.txt
13
+ tujia_llm.egg-info/entry_points.txt
14
+ tujia_llm.egg-info/requires.txt
15
+ tujia_llm.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tujia-llm = tujia_llm.cli:main
@@ -0,0 +1,4 @@
1
+ huggingface_hub>=0.24
2
+
3
+ [:sys_platform == "darwin" and platform_machine == "arm64"]
4
+ mlx-lm>=0.21
@@ -0,0 +1 @@
1
+ tujia_llm