Text Generation
Safetensors
GGUF
English
Chinese
qwen2
reasoning-compression
cjk
chain-of-thought
distillation
qwen2.5
conversational
Instructions to use ZelligeAI/tessera-compressor with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- llama-cpp-python
How to use ZelligeAI/tessera-compressor with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="ZelligeAI/tessera-compressor", filename="gguf/compressor-v31-q8_0.gguf", )
llm.create_chat_completion( messages = [ { "role": "user", "content": "What is the capital of France?" } ] ) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use ZelligeAI/tessera-compressor with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf ZelligeAI/tessera-compressor:Q8_0 # Run inference directly in the terminal: llama cli -hf ZelligeAI/tessera-compressor:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf ZelligeAI/tessera-compressor:Q8_0 # Run inference directly in the terminal: llama cli -hf ZelligeAI/tessera-compressor:Q8_0
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf ZelligeAI/tessera-compressor:Q8_0 # Run inference directly in the terminal: ./llama-cli -hf ZelligeAI/tessera-compressor:Q8_0
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf ZelligeAI/tessera-compressor:Q8_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf ZelligeAI/tessera-compressor:Q8_0
Use Docker
docker model run hf.co/ZelligeAI/tessera-compressor:Q8_0
- LM Studio
- Jan
- vLLM
How to use ZelligeAI/tessera-compressor with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "ZelligeAI/tessera-compressor" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "ZelligeAI/tessera-compressor", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/ZelligeAI/tessera-compressor:Q8_0
- Ollama
How to use ZelligeAI/tessera-compressor with Ollama:
ollama run hf.co/ZelligeAI/tessera-compressor:Q8_0
- Unsloth Studio
How to use ZelligeAI/tessera-compressor with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ZelligeAI/tessera-compressor to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for ZelligeAI/tessera-compressor to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for ZelligeAI/tessera-compressor to start chatting
- Pi
How to use ZelligeAI/tessera-compressor with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ZelligeAI/tessera-compressor:Q8_0
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "ZelligeAI/tessera-compressor:Q8_0" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent new
How to use ZelligeAI/tessera-compressor with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ZelligeAI/tessera-compressor:Q8_0
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default ZelligeAI/tessera-compressor:Q8_0
Run Hermes
hermes
- Atomic Chat new
- OpenClaw new
How to use ZelligeAI/tessera-compressor with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf ZelligeAI/tessera-compressor:Q8_0
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "ZelligeAI/tessera-compressor:Q8_0" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- Docker Model Runner
How to use ZelligeAI/tessera-compressor with Docker Model Runner:
docker model run hf.co/ZelligeAI/tessera-compressor:Q8_0
- Lemonade
How to use ZelligeAI/tessera-compressor with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull ZelligeAI/tessera-compressor:Q8_0
Run and chat with the model
lemonade run user.tessera-compressor-Q8_0
List all available models
lemonade list
| """ | |
| tokenmax.py — Deterministic token-maxing post-processor for compressed think blocks. | |
| Applies ONLY substitutions that are verified to save tokens on the Qwen 248K tokenizer | |
| (OmniCoder-9B / Qwen3.5). Every substitution was tested in-context (not isolation) to | |
| confirm real token savings without boundary interference. | |
| Design: | |
| - LLM does semantic compression (what to keep vs drop) | |
| - This code enforces consistent notation deterministically | |
| - GUARD: only returns the processed version if ntok(result) < ntok(original) | |
| - Idempotent: safe to run multiple times | |
| Usage: | |
| from caveman.compress.tokenmax import tokenmax, tokenmax_with_stats | |
| compressed = tokenmax(think_text, tokenizer) | |
| compressed, stats = tokenmax_with_stats(think_text, tokenizer) | |
| Verified: 2026-05-31 on Qwen 248K vocab. 27/28 substitutions save in-context. | |
| Zero false positives. One zero-effect (贪心 for "greedy" — boundary-dependent). | |
| """ | |
| import re | |
| from typing import Optional | |
| # ── Phase 1: Filler drops ────────────────────────────────────────────── | |
| # Phrases that carry zero information in compressed reasoning. | |
| # Only patterns that are NEVER load-bearing in a think block. | |
| _FILLER_PATTERNS = [ | |
| # Metacognition (the model narrating its own process) | |
| r"\bI need to\b", | |
| r"\bwe need to\b", | |
| r"\bI will\b", | |
| r"\bI'll\b", | |
| r"\blet me\b", | |
| r"\blet's\b", | |
| r"\bI want to\b", | |
| r"\bI should\b", | |
| r"\bwe should\b", | |
| # Hedging | |
| r"\bprobably\b", | |
| r"\bbasically\b", | |
| r"\bessentially\b", | |
| r"\bit seems like\b", | |
| # Filler transitions | |
| r"\bin order to\b", | |
| r"\bfirst of all\b", | |
| r"\bin other words\b", | |
| r"\bon the other hand\b", | |
| r"\bmore specifically\b", | |
| r"\bto be more precise\b", | |
| r"\band so on\b", | |
| # Conversational padding (require word boundary at end to avoid "Greatest", "Perfectly") | |
| r"\bGreat\b[,!.]?\s*", | |
| r"\bPerfect\b[,!.]?\s*", | |
| # Obvious statements | |
| r"\bAs (?:we|you) can see\b", | |
| r"\bAs mentioned (?:above|earlier|before)\b", | |
| ] | |
| # ── Phase 2: Phrase → cheapest token ─────────────────────────────────── | |
| # Ordered LONGEST FIRST to prevent partial matches. | |
| # Each entry: (regex_pattern, replacement, category) | |
| # Categories: 'cjk', 'symbol', 'abbrev' — for stats tracking. | |
| _SUBSTITUTIONS = [ | |
| # ── COMPOUND PATTERNS FIRST (must fire before their components) ── | |
| # Verbose comparison phrases (+5t savings) | |
| (r'\bis\s+greater\s+than\s+or\s+equal\s+to\b', '≥', 'symbol'), # +5t | |
| (r'\bis\s+less\s+than\s+or\s+equal\s+to\b', '≤', 'symbol'), # +5t | |
| # Verbose discourse phrases (+3t savings) | |
| (r'\bwe\s+can\s+see\s+that\b', '可知', 'cjk'), # +3t | |
| (r'\bat\s+the\s+same\s+time\b', '同时', 'cjk'), # +3t | |
| (r'\bthat\s+is\s+to\s+say\b', '即', 'cjk'), # +3t | |
| # Multi-word phrases (+2t savings) | |
| (r'\bin\s+this\s+case\b', '此时', 'cjk'), # +2t | |
| (r'\bthe\s+number\s+of\b', '个数', 'cjk'), # +2t | |
| (r'\bis\s+equal\s+to\b', '等于', 'cjk'), # +2t | |
| # Complexity boilerplate (biggest per-occurrence savings) | |
| (r'[Oo]\(n\)\s*time[,;]?\s*[Oo]\(n\)\s*space\.?', 'O(n|n).', 'abbrev'), | |
| (r'[Oo]\(n\)\s*time[,;]?\s*[Oo]\(1\)\s*space\.?', 'O(n|1).', 'abbrev'), | |
| (r'[Oo]\(n\s*log\s*n\)\s*time[,;]?\s*[Oo]\(n\)\s*space', 'O(n㏒n|n)', 'abbrev'), | |
| (r'[Oo]\(n\s*log\s*n\)\s*time[,;]?\s*[Oo]\(1\)\s*space', 'O(n㏒n|1)', 'abbrev'), | |
| (r'[Tt]ime\s*complexity[:\s]+', 'T=', 'abbrev'), | |
| (r'[Ss]pace\s*complexity[:\s]+', 'S=', 'abbrev'), | |
| # Multi-word compounds (BEFORE their single-word components) | |
| (r'\bassume without loss of generality\b', '设 不妨', 'cjk'), # before "assume" | |
| (r'\bproof by contradiction\b', '反证', 'cjk'), # before "proof", "contradiction" | |
| (r'\bnecessary and sufficient\b', '充要', 'cjk'), # before "sufficient" | |
| (r'\bnot equal(?:\s+to)?\b', '≠', 'symbol'), # before "is not", "does not" | |
| (r'\bif and only if\b', 'iff', 'abbrev'), # before "for all" | |
| (r'\bmuch greater than\b', '≫', 'symbol'), # before "greater than" | |
| (r'\bkeep track(?:\s+of)?\b', '记录', 'cjk'), # before article strip | |
| (r'\bin ascending order\b', 'asc', 'abbrev'), | |
| (r'\bin descending order\b', 'desc', 'abbrev'), | |
| (r'\bmaximum value\b', '最大值', 'cjk'), | |
| (r'\bminimum value\b', '最小值', 'cjk'), | |
| (r'\breturn value\b', '返回値', 'cjk'), | |
| (r'\brather than\b', '而非', 'cjk'), | |
| (r'\baccording to\b', '按照', 'cjk'), | |
| # DS compounds (before components) | |
| (r'\bdoubly linked list\b', 'DLL', 'abbrev'), # BEFORE "linked list" | |
| (r'\bbinary indexed tree\b', 'BIT', 'abbrev'), # BEFORE "binary" | |
| (r'\bminimum spanning tree\b', 'MST', 'abbrev'), | |
| (r'\bdepth[- ]first search\b', 'DFS', 'abbrev'), | |
| (r'\bbreadth[- ]first search\b', 'BFS', 'abbrev'), | |
| (r'\bdynamic programming\b', 'DP', 'abbrev'), | |
| (r'\bdivide and conquer\b', '分治', 'cjk'), | |
| (r'\bmonot(?:onic|one)\s*stack\b', '单调栈', 'cjk'), | |
| (r'\btime limit exceeded\b', '超时', 'cjk'), | |
| (r'\bout of bounds\b', '越界', 'cjk'), | |
| (r'\bremove duplicates?\b', '去重', 'cjk'), | |
| (r'\benumerate all\b', '穷举', 'cjk'), | |
| (r'\bbinary search\b', '二分', 'cjk'), | |
| (r'\bsliding window\b', 'sw', 'abbrev'), | |
| (r'\bunion[- ]find\b', 'UF', 'abbrev'), | |
| (r'\btopological sort\b', '拓扑序', 'cjk'), | |
| (r'\bshortest path\b', 'sp', 'abbrev'), | |
| (r'\blinked list\b', 'LL', 'abbrev'), | |
| (r'\bpriority queue\b', 'heap', 'abbrev'), | |
| (r'\bprefix sum\b', 'ps', 'abbrev'), | |
| (r'\bbrute force\b', '暴力', 'cjk'), | |
| (r'\bno solution\b', '无解', 'cjk'), | |
| (r'\bedge cases?\b', '边界', 'cjk'), | |
| (r'\bbase case\b', 'bc', 'abbrev'), | |
| (r'\bworst case\b', 'wc', 'abbrev'), | |
| # ── SINGLE-WORD SUBSTITUTIONS (safe after compounds consumed) ── | |
| # +4t savings | |
| (r'\bobviously\b', '显然', 'cjk'), | |
| # +3t savings | |
| (r'\bredundant\b', '冗余', 'cjk'), | |
| (r'\bsatisf(?:y|ies|ied)\b', '满足', 'cjk'), # +2t, 333x in data | |
| (r'\bunsorted\b', '无序', 'cjk'), | |
| (r'\bdue to\b', '由于', 'cjk'), | |
| (r'\bhence\b', '故', 'cjk'), | |
| (r'\bnamely\b', '即', 'cjk'), | |
| (r'\bassume\b', '设', 'cjk'), | |
| (r'\bsuppose\b', '设', 'cjk'), | |
| (r'\bderive\b', '推导', 'cjk'), | |
| # +2t savings | |
| (r'\bmonotone\b', '单调', 'cjk'), | |
| (r'\bconvergent\b', '收敛', 'cjk'), | |
| (r'\bdivergent\b', '发散', 'cjk'), | |
| (r'\bcommutative\b', '交换', 'cjk'), | |
| (r'\bdeterministic\b', '确定', 'cjk'), | |
| (r'\bprobabilistic\b', '概率', 'cjk'), | |
| (r'\bprove\b', '证明', 'cjk'), | |
| (r'\bproof\b', '证明', 'cjk'), | |
| (r'\bflip\b', '翻转', 'cjk'), | |
| (r'\bsorted\b(?!\s*[=(\[])', '有序', 'cjk'), # not before = ( [ (assignment/call) | |
| # +1t savings | |
| (r'\bbacktrack(?:ing)?\b', '回溯', 'cjk'), | |
| (r'\btravers(?:e|al|ing)\b', '遍历', 'cjk'), | |
| (r'\brecursi(?:on|ve|vely)\b', '递归', 'cjk'), | |
| (r'\bcontradiction\b', '矛盾', 'cjk'), | |
| (r'\bsufficient\b', '充分', 'cjk'), | |
| (r'\bequivalent\b', '等价', 'cjk'), | |
| (r'\bsymmetric\b', '对称', 'cjk'), | |
| (r'\binvariant\b', '不变', 'cjk'), | |
| (r'\bexponential\b', '指数', 'cjk'), | |
| (r'\bpermutation\b', '排列', 'cjk'), | |
| (r'\badjacent\b', '相邻', 'cjk'), | |
| (r'\boptimal\b', '最优', 'cjk'), | |
| (r'\bfeasible\b', '可行', 'cjk'), | |
| (r'\binduction\b', '归纳', 'cjk'), | |
| (r'\bmaintain\b', '维护', 'cjk'), | |
| (r'\bswap\b(?!\s*[=(\[])', '交换', 'cjk'), # not before = ( [ (assignment/call) | |
| (r'\bcumulative\b', '累积', 'cjk'), | |
| (r'\bquotient\b', '商', 'cjk'), | |
| (r'\bmemoiz(?:ation|e)\b', 'memo', 'abbrev'), | |
| # +2t savings (mined from v19 data) | |
| (r'\bmathematical\b', '数学', 'cjk'), | |
| (r'\bcorresponding(?:ly)?\b', '对应', 'cjk'), | |
| (r'\brequirement\b', '需求', 'cjk'), | |
| # +1t savings (mined from v19 data) | |
| (r'\bcomplexity\b', '复杂度', 'cjk'), | |
| (r'\bsimilarly\b', '同理', 'cjk'), | |
| (r'\bsubstitut(?:e|ion)\b', '代入', 'cjk'), | |
| (r'\bincreasing(?:ly)?\b', '递增', 'cjk'), | |
| (r'\bdecreasing(?:ly)?\b', '递减', 'cjk'), | |
| (r'\brespectively\b', '分别', 'cjk'), | |
| (r'\bnecessarily\b', '必然', 'cjk'), | |
| (r'\btransformation\b', '变换', 'cjk'), | |
| (r'\bprerequisite\b', '前提', 'cjk'), | |
| (r'\bconsequently\b', '从而', 'cjk'), | |
| (r'\boverlapping\b', '重叠', 'cjk'), | |
| (r'\bcontribut(?:e|ion)\b', '贡献', 'cjk'), | |
| (r'\bindependent(?:ly)?\b', '独立', 'cjk'), | |
| (r'\bimpossible\b', '不可能', 'cjk'), | |
| (r'\biterat(?:e|ion|ing)\b', '迭代', 'cjk'), | |
| (r'\benumerat(?:e|ion|ing)\b', '枚举', 'cjk'), | |
| # ── LOGIC SYMBOLS ── | |
| (r'\btherefore\b', '⇒', 'symbol'), | |
| (r'\bthus\b', '⇒', 'symbol'), | |
| (r'\bsuch that\b', 'st', 'abbrev'), | |
| (r'\bthere exists?\b', '∃', 'symbol'), | |
| (r'\bfor each\b', '∀', 'symbol'), | |
| (r'\bfor every\b', '∀', 'symbol'), | |
| (r'\bfor all\b', '∀', 'symbol'), | |
| (r'\bdoes not\b', '¬', 'symbol'), | |
| (r"\bdoesn't\b", '¬', 'symbol'), | |
| (r'\bis not\b(?!\s+(?:None|null|undefined|empty|zero|0))', '非', 'cjk'), # protect "is not None" etc | |
| (r'\bat least\b', '≥', 'symbol'), | |
| (r'\bat most\b', '≤', 'symbol'), | |
| (r'\bgreater than\b', '>', 'symbol'), | |
| (r'\bless than\b', '<', 'symbol'), | |
| # ── ARTICLE STRIPPING (last — lowest priority) ── | |
| (r'\bthe\b\s+(?!(?:same|only|first|last|next|other)\b)', '', 'filler'), # protect "the same", "the only" etc | |
| (r'\ba\b\s+(?=[bcdfghjklmnpqrstvwxyz])', '', 'filler'), | |
| (r'\ban\b\s+', '', 'filler'), | |
| ] | |
| # ── Compile once ─────────────────────────────────────────────────────── | |
| _FILLER_COMPILED = [(re.compile(p, re.IGNORECASE), '') for p in _FILLER_PATTERNS] | |
| _SUBS_COMPILED = [(re.compile(p, re.IGNORECASE), r, cat) for p, r, cat in _SUBSTITUTIONS] | |
| def _ntok(text: str, tokenizer) -> int: | |
| """Token count using the provided tokenizer.""" | |
| return len(tokenizer.encode(text, add_special_tokens=False)) | |
| def _protect_code_fences(text: str) -> tuple[str, list]: | |
| """Extract code-fenced blocks, replace with placeholders. | |
| Returns (text_with_placeholders, list_of_extracted_blocks).""" | |
| blocks = [] | |
| def _replace(m): | |
| blocks.append(m.group(0)) | |
| return f'\x00CODEFENCE{len(blocks)-1}\x00' | |
| # Match ```...``` and inline `...` (non-greedy) | |
| protected = re.sub(r'```.*?```|`[^`\n]+`', _replace, text, flags=re.DOTALL) | |
| return protected, blocks | |
| def _restore_code_fences(text: str, blocks: list) -> str: | |
| """Restore code-fenced blocks from placeholders.""" | |
| for i, block in enumerate(blocks): | |
| text = text.replace(f'\x00CODEFENCE{i}\x00', block) | |
| return text | |
| def _apply_subs(text: str) -> tuple[str, dict]: | |
| """Apply all substitutions, return (result, stats). | |
| Code fences (``` and inline `) are protected from substitution.""" | |
| stats = {'filler_drops': 0, 'cjk': 0, 'symbol': 0, 'abbrev': 0, 'total_subs': 0} | |
| # Phase 0: protect code fences from substitution | |
| text, code_blocks = _protect_code_fences(text) | |
| # Phase 1: filler drops | |
| for pat, repl in _FILLER_COMPILED: | |
| text, n = pat.subn(repl, text) | |
| if n: | |
| stats['filler_drops'] += n | |
| stats['total_subs'] += n | |
| # Phase 2: substitutions | |
| for pat, repl, cat in _SUBS_COMPILED: | |
| text, n = pat.subn(repl, text) | |
| if n: | |
| stats[cat] = stats.get(cat, 0) + n | |
| stats['total_subs'] += n | |
| # Phase 3: restore code fences | |
| text = _restore_code_fences(text, code_blocks) | |
| # Phase 4: whitespace normalization | |
| text = re.sub(r'[ \t]+', ' ', text) | |
| text = re.sub(r'\n{3,}', '\n\n', text) | |
| text = re.sub(r' *\n *', '\n', text) | |
| text = text.strip() | |
| return text, stats | |
| def tokenmax(text: str, tokenizer, force_cjk: bool = False) -> str: | |
| """Apply token-maxing. Returns original if no savings achieved. | |
| Args: | |
| text: The think block content (without <think> tags). | |
| tokenizer: A HuggingFace tokenizer with .encode() method. | |
| force_cjk: If True, always return the processed version when CJK | |
| substitutions were applied, even if total token count increased. | |
| Use this to maximize CJK adoption in training data. | |
| Returns: | |
| The token-maxed text, or the original if processing didn't save tokens | |
| (unless force_cjk=True and CJK subs were applied). | |
| """ | |
| if not text or not text.strip(): | |
| return text | |
| original_tokens = _ntok(text, tokenizer) | |
| result, stats = _apply_subs(text) | |
| result_tokens = _ntok(result, tokenizer) | |
| # GUARD: only return processed version if it actually saves tokens | |
| # OVERRIDE: force_cjk bypasses the guard when CJK substitutions were made | |
| if result_tokens < original_tokens: | |
| return result | |
| if force_cjk and stats.get('cjk', 0) > 0: | |
| return result | |
| return text | |
| def tokenmax_with_stats(text: str, tokenizer, force_cjk: bool = False) -> tuple[str, dict]: | |
| """Like tokenmax() but also returns substitution statistics. | |
| Args: | |
| force_cjk: If True, always apply when CJK substitutions were made, | |
| even if total token count increased. Prioritizes CJK adoption | |
| over token savings. | |
| Returns: | |
| (processed_text, stats_dict) where stats_dict contains: | |
| - original_tokens: token count before processing | |
| - result_tokens: token count after processing | |
| - saved: tokens saved (negative = token increase; check forced_cjk) | |
| - applied: whether the processed version was used | |
| - forced_cjk: True when force_cjk override caused acceptance despite no savings | |
| - filler_drops, cjk, symbol, abbrev: substitution counts by category | |
| - total_subs: total substitutions applied | |
| """ | |
| if not text or not text.strip(): | |
| return text, {'original_tokens': 0, 'result_tokens': 0, 'saved': 0, | |
| 'applied': False, 'forced_cjk': False, 'total_subs': 0} | |
| original_tokens = _ntok(text, tokenizer) | |
| result, stats = _apply_subs(text) | |
| result_tokens = _ntok(result, tokenizer) | |
| saved = original_tokens - result_tokens | |
| stats['original_tokens'] = original_tokens | |
| stats['result_tokens'] = result_tokens | |
| stats['saved'] = saved | |
| stats['forced_cjk'] = False | |
| if saved > 0: | |
| stats['applied'] = True | |
| return result, stats | |
| if force_cjk and stats.get('cjk', 0) > 0: | |
| stats['applied'] = True | |
| stats['forced_cjk'] = True | |
| return result, stats | |
| stats['applied'] = False | |
| return text, stats | |
| # ── CLI: batch process a JSONL file ──────────────────────────────────── | |
| if __name__ == '__main__': | |
| import json, sys, argparse | |
| from transformers import AutoTokenizer | |
| parser = argparse.ArgumentParser(description='Token-max post-processor for think blocks') | |
| parser.add_argument('--input', required=True, help='Input JSONL (messages format)') | |
| parser.add_argument('--output', help='Output JSONL (default: dry run, stats only)') | |
| parser.add_argument('--tokenizer', default='ZelligeAI/tessera-compressor', | |
| help='HF repo id or local path of the tokenizer to count savings under') | |
| parser.add_argument('--force-cjk', action='store_true', | |
| help='Force CJK substitutions even if total tokens increase. ' | |
| 'Prioritizes CJK adoption over token savings.') | |
| args = parser.parse_args() | |
| tok = AutoTokenizer.from_pretrained(args.tokenizer, trust_remote_code=True) | |
| total_before = total_after = applied = skipped = forced = 0 | |
| out_lines = [] | |
| with open(args.input) as f: | |
| for line in f: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| rec = json.loads(line) | |
| for m in rec.get('messages', rec.get('conversations', [])): | |
| role = m.get('role', m.get('from', '')) | |
| if role not in ('assistant', 'gpt'): | |
| continue | |
| content_key = 'content' if 'content' in m else 'value' | |
| c = m.get(content_key, '') or '' | |
| if '<think>' not in c or '</think>' not in c: | |
| continue | |
| # Extract think content, preserving prefix before <think> and suffix after </think> | |
| think_start = c.index('<think>') + len('<think>') | |
| think_end = c.index('</think>') | |
| prefix = c[:think_start - len('<think>')] | |
| think = c[think_start:think_end] | |
| suffix = c[think_end + len('</think>'):] | |
| maxed, stats = tokenmax_with_stats(think, tok, force_cjk=args.force_cjk) | |
| total_before += stats['original_tokens'] | |
| if stats['applied']: | |
| applied += 1 | |
| total_after += stats['result_tokens'] | |
| m[content_key] = f'{prefix}<think>{maxed}</think>{suffix}' | |
| if stats.get('forced_cjk'): | |
| forced += 1 | |
| else: | |
| skipped += 1 | |
| total_after += stats['original_tokens'] | |
| out_lines.append(json.dumps(rec, ensure_ascii=False)) | |
| if args.output: | |
| with open(args.output, 'w') as f: | |
| for line in out_lines: | |
| f.write(line + '\n') | |
| total = applied + skipped | |
| saved = total_before - total_after | |
| if total > 0: | |
| print(f'Processed {total} think blocks') | |
| print(f' Applied: {applied} ({100*applied/total:.0f}%)') | |
| if forced: | |
| print(f' Forced CJK: {forced} (applied despite no token savings)') | |
| print(f' Skipped (no savings): {skipped}') | |
| pct = f'{100*saved/total_before:.1f}' if total_before > 0 else '0.0' | |
| print(f' Tokens: {total_before} → {total_after} = {saved:+d} ({pct}%)') | |
| else: | |
| print(f'No think blocks found in {args.input}') | |