Instructions to use Gramscii-IT/SemanticRepair-270M with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use Gramscii-IT/SemanticRepair-270M with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("Gramscii-IT/SemanticRepair-270M") prompt = "Write a story about Einstein" messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, add_generation_prompt=True ) text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use Gramscii-IT/SemanticRepair-270M 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 Gramscii-IT/SemanticRepair-270M:Q8_0 # Run inference directly in the terminal: llama cli -hf Gramscii-IT/SemanticRepair-270M:Q8_0
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf Gramscii-IT/SemanticRepair-270M:Q8_0 # Run inference directly in the terminal: llama cli -hf Gramscii-IT/SemanticRepair-270M: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 Gramscii-IT/SemanticRepair-270M:Q8_0 # Run inference directly in the terminal: ./llama-cli -hf Gramscii-IT/SemanticRepair-270M: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 Gramscii-IT/SemanticRepair-270M:Q8_0 # Run inference directly in the terminal: ./build/bin/llama-cli -hf Gramscii-IT/SemanticRepair-270M:Q8_0
Use Docker
docker model run hf.co/Gramscii-IT/SemanticRepair-270M:Q8_0
- LM Studio
- Jan
- vLLM
How to use Gramscii-IT/SemanticRepair-270M with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Gramscii-IT/SemanticRepair-270M" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Gramscii-IT/SemanticRepair-270M", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/Gramscii-IT/SemanticRepair-270M:Q8_0
- Ollama
How to use Gramscii-IT/SemanticRepair-270M with Ollama:
ollama run hf.co/Gramscii-IT/SemanticRepair-270M:Q8_0
- Unsloth Desktop
- MLX LM
How to use Gramscii-IT/SemanticRepair-270M with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Interactive chat REPL mlx_lm.chat --model "Gramscii-IT/SemanticRepair-270M"
Run an OpenAI-compatible server
# Install MLX LM uv tool install mlx-lm # Start the server mlx_lm.server --model "Gramscii-IT/SemanticRepair-270M" # Calling the OpenAI-compatible server with curl curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Gramscii-IT/SemanticRepair-270M", "messages": [ {"role": "user", "content": "Hello"} ] }' - Docker Model Runner
How to use Gramscii-IT/SemanticRepair-270M with Docker Model Runner:
docker model run hf.co/Gramscii-IT/SemanticRepair-270M:Q8_0
- Lemonade
How to use Gramscii-IT/SemanticRepair-270M with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull Gramscii-IT/SemanticRepair-270M:Q8_0
Run and chat with the model
lemonade run user.SemanticRepair-270M-Q8_0
List all available models
lemonade list
- Atomic Chat
SemanticRepair-270M
A 270M rewriter that sits behind an embedding router. When a question does not land on any capability with enough margin, this model restates it in the plain form the capabilities are described in, and the router tries again on the restatement. When it reads no request at all, it says so.
That is the whole job. It does not answer questions, it does not decide anything, and nothing it writes is ever executed: the router runs on the restatement, the tool runs on the original.
The surface it reads, which is not a chat prompt
Fine-tuned on a bare completion surface. It has never seen a chat template, a system message or a few-shot example. Speak to it the way it was trained or it will not work:
{Language}: {the question, verbatim}
=>
It completes with one line per request it found, or the single token
NO_REQUEST.
{Language} is the English name of the language the question is in —
not the language of whatever system is asking. English, Italian,
French, German, Spanish; a language outside that set keeps whatever
tag the caller declares, because an invented tag is a surface the model
never read either.
This is the whole call the engine makes, on /v1/completions and never on
a chat endpoint:
{
"prompt": "Italian: che tempo fa domani a Bologna?\n=>\n",
"stop": ["\n=>"],
"temperature": 0.0,
"repeat_penalty": 1.0,
"max_tokens": 96
}
Greedy, because the same words must always produce the same rewrite: a
router that reruns on a different restatement each time cannot be reasoned
about. repeat_penalty is 1.0, which is neutral — a rewrite legitimately
repeats the words of the question. The stop string cuts a runaway that
starts echoing another question in its own trained format; everything
before it is the answer. 96 tokens leaves room for several rewritten lines:
the longest answer in the training data is 43 tokens.
The sentinel is compared case-insensitively and tolerates a trailing period. It is an instruction the model follows, not a token it is guaranteed to emit byte-exact.
These are real outputs from the released weights, greedy:
| in | out |
|---|---|
Spanish: no busques la tienda de ropa, dime la dosis de paracetamol |
dime la dosis de paracetamol |
Italian: scusa il disturbo, mi diresti che tempo fa domani a Bologna? |
dimmi che tempo fa domani a Bologna |
English: book me a table for friday and also cancel my dentist |
book me a table for fridaycancel my dentist |
Italian: guarda un po', questo sacchetto della spesa è tutto rotto. |
NO_REQUEST |
A negation is dropped, an apology is dropped, one message asking for two things becomes two lines, and something that is not a request at all answers with the sentinel.
What is in this repository
| file | size | what it is |
|---|---|---|
sft-v19-q8_0.gguf |
300 MB | what the engine serves, through llama-server |
model.safetensors |
536 MB | the same weights fused, BF16, 236 tensors |
model.safetensors.index.json |
the index over that single file | |
tokenizer.json, tokenizer_config.json |
33 MB | the tokenizer as the fuse wrote it |
config.json, generation_config.json |
gemma3_text, torch_dtype: bfloat16 |
|
chat_template.jinja |
present because the fuse writes it — not the surface this model reads, see above |
The GGUF's sha256 is
33b6cc73c7d306c9c81aa96c5a8ddb9dbfd46a5ac5d0388a2de45b5229928c56.
The engine pins it and refuses anything else, which is what makes a routing
decision reproducible.
Both files carry the same weights: the fused final adapter of training run
v19, adapter sha256 1082985953ed6c33…, quantised to Q8_0. Q8_0 and not
Q4: measured on this seat, at temperature 0 a Q8_0 is stable to the byte
across runs and a Q4 is not.
What it is measured to do
Measured against the two things worth measuring against: the model it was fine-tuned from, and doing no repair at all. Two benches over different corpora, because a model that wins one of them is a model chosen by which table got printed.
400 routing tasks over 343 graphs, of which 78 ask for more than one capability and 160 name a day. These exact released weights against the untrained base, every column the bench reports:
| this model | gemma-3-270m, untrained |
|
|---|---|---|
| exact, every capability the question asks for, and no other | 32 | 24 |
| safe, a proper subset reached, nothing wrong run | 6 | 1 |
| abstained, nothing executed at all | 298 | 197 |
| wrong execution, something ran that should not have | 1 | 0 |
| answered in the wrong language | 6 | 123 |
| lost the day the question named, of 160 | 57 | 55 |
| echoed the question back instead of restating it | 64 | 106 |
| of those echoes, ones that still routed exactly | 9 | 20 |
| compound questions answered whole, of 78 | 0 | 0 |
Read the columns together rather than one at a time.
The language column is what the fine-tune buys and it is the largest effect in any table here: 123 becomes 6.
The abstention column is the honest cost. This model refuses far more often than the base does, 298 against 197. It abstains rather than guess, and abstaining is why its wrong executions stay at one. A rewriter that guesses would score better on exact and worse on everything that matters.
The echo column says how often the model gave the question back unchanged. It halves, 106 to 64, which is the fine-tune learning that restating is the job.
The date column barely moves, 55 to 57, and the compound column does not move at all: neither the base nor this model answers a single one of the 78 questions asking for two capabilities on this bench. Those are the two things the training did not buy.
39 held-out questions on a live workspace, written by three blind judges against the capabilities in it, in two languages:
| exact | |
|---|---|
| this model | 16 |
| routing the question as written, no repair | 12 |
google/gemma-3-270m, untrained |
9 |
The base model scoring below "no repair at all" is the whole argument for fine-tuning something for this seat: an untrained 270M put here makes routing worse than leaving the question alone. Of the 10 compound questions in that set — one message asking for two things — this model answers 2 whole.
Its own training gate, on 1000 held-out examples: 261 exact (26.1%), 927 of 1000 answers well formed, and the language-balance gate passed with a 7-point gap between English and Italian against a 10-point bar.
What it does not fix, which a card naming only the gains would hide
The sentinel does not catch every non-request. guarda un po', questo sacchetto della spesa è tutto rotto answers NO_REQUEST, but ciao come stai comes back verbatim rather than refused. If nothing downstream checks
that the restatement is a request, a greeting will be routed.
It sometimes rewrites a word rather than restating a sentence. Measured:
Bitte reserviere mir einen Platz im Klettergarten für Freitag comes back
as reserviere mir einen Platz im Kletterpark für Freitag — Klettergarten
became Kletterpark. The restatement is only ever compared against
capability descriptions and never executed, which is why this is tolerable
here; in any design where the rewrite is acted on, it is not.
Dates. In the 400-task run above, 160 of the tasks name a day. This model loses the day in 57 of them — more than a third. It is the weakest column it has, and it is weak in a way that does not announce itself: the restatement reads as a clean request, with the "yesterday" or the "before Easter" simply gone. If the day matters to your routing, measure that before you serve this.
It is not distinguishable from its own neighbours. Ten checkpoints of this run were compared on the same 400 tasks pair by pair rather than by totals — counting only the tasks where two checkpoints disagree, which are the only ones carrying information. Not one difference reaches significance: the widest gap in the table is 7 tasks to 1, p = 0.07. With around thirty exact answers out of four hundred the natural fluctuation is about five, which is most of the ranking. Treat the order inside a run as noise: a checkpoint one or two points ahead of another inside the same run has not been shown to be better than it.
What bounds all of this
None of these weaknesses are fixed. They are bounded, by the system around the model rather than by the model, and the bounds are worth stating because they decide how much a weakness costs.
Nothing it writes is executed. The restatement is compared against capability descriptions and then thrown away. Whatever runs, runs on the original message. Fixed SQL and HTTP operations are authored in the tool and cannot be assembled from either text.
The source message stays beside the restatement. Dates, language, page references and everything shown to the reader are read from what the person actually wrote. So a dropped day costs a routing decision, not the date: the question may reach the wrong capability, but the day itself was never the restatement's to lose.
A restatement that does not win by enough margin routes nothing. The router applies a margin and a floor, and below them the system abstains and asks for a rephrasing. That is why the wrong-execution column is 1 in 400 while the abstention column is 298: the failure mode is refusing, not acting wrongly.
The descriptions are the other half. What a question is matched against is written prose in the workspace, and a capability that keeps being missed is usually a description that needs rewriting, not a model that needs retraining. The rewriter is one of two things you can improve, and the cheaper one is often not the model.
The engine has a linter for exactly this, applying the same score the router
applies, over a whole graph or over one description while it is being
written. It reports collisions: pairs of capabilities the router cannot
separate. A collision is not a verdict that either is wrong. Capabilities
that overlap in meaning are expected to sit close, and the linter says where
the router cannot tell them apart, leaving the choice to rewrite, merge or
keep to whoever knows what the nodes are for. collision is also one of the
four states a routing can end in, beside uncovered, uncertain and
confident, so the reader is told which one happened rather than being
handed an answer with no account of how it was reached.
Where it runs
Served with llama-server, reached by the engine as one of four model seats:
llama-server -m sft-v19-q8_0.gguf --host 127.0.0.1 --port 9250 -ngl 99 -c 2048 --jinja
It is loaded only when routing is genuinely unsure, never as the default path, and released after an idle window.
On CPU alone
Measured on an Apple M-series machine with -ngl 0 -t 4: no GPU offload at
all, four threads, and nothing in the server log naming Metal or a GPU.
| latency | tokens/s | |
|---|---|---|
| strip a negation, 8 tokens out | 38 ms | 210 |
| split one message into two requests, 11 tokens | 32 ms | 344 |
answer NO_REQUEST, 4 tokens |
17 ms | 236 |
762 MB resident while serving.
llama-server -m sft-v19-q8_0.gguf --host 127.0.0.1 --port 9250 -ngl 0 -t 4 -c 2048
What this does and does not establish: it runs usefully with no accelerator, on few threads. It has not been run on a phone or a tablet, and the three things that would decide it there are the three not measured here — 762 MB resident is a lot for a mobile process, loading 300 MB from slow storage is a different question from generating in 38 ms, and mobile cores throttle under heat while these did not.
The data it was trained on
Gramscii-IT/semantic-repair-routing
— published, and everything below can be recounted from it.
84,819 pairs: 82,819 train, 1,000 validation, 1,000 test. Each pair is a message somebody could plausibly write and the requests inside it restated plainly, one per line.
Five languages, close to balanced:
| rows | ||
|---|---|---|
| English | 18,006 | 21.7% |
| Spanish | 16,903 | 20.4% |
| German | 16,659 | 20.1% |
| French | 16,602 | 20.0% |
| Italian | 14,649 | 17.7% |
What the message is doing, which is what the data is really organised by. Every one of the 82,819 training rows carries one of 38 such labels; these are the ten largest:
| rows | ||
|---|---|---|
massive_real — real assistant traffic |
5,736 | 6.9% |
minimal_pair — two messages differing in one word |
5,299 | 6.4% |
indirect — the request is implied, never stated |
4,912 | 5.9% |
presto_native — natively written, not translated |
4,606 | 5.6% |
oasst_human — human-written |
4,450 | 5.4% |
multi — more than one request in one message |
3,974 | 4.8% |
direct — the plain case |
3,904 | 4.7% |
vent_statement_request — a complaint with a request inside |
3,516 | 4.2% |
negation_exclusion — names what is not wanted |
3,398 | 4.1% |
trap_topic_lure — a topic named to pull routing the wrong way |
3,324 | 4.0% |
The remaining 28 cover self-correction, code-switching, ultra-short messages, typos and slang, messages carrying two, three or four requests, eight kinds of date reference — a weekday in the past, a named holiday, a bounded range — and the two families below.
The two families that decide what this model refuses to do
injection_meta — 2,278 rows — is a message aimed at the assistant
itself: reveal your instructions, ignore what you were told, pretend you
have no rules. 1,901 of them answer NO_REQUEST, because there is
nothing here to route. Together with injection_command these two families
supply 19% of every NO_REQUEST row in the set: refusing is largely
taught by messages that try hardest to get an answer.
injection_command — 2,410 rows — is the opposite lesson, and the more
delicate one. A hostile but explicit command about the user's own things:
delete every saved bank account, empty my personal cloud, cancel all my
bookings tonight. 2,281 of them are restated faithfully, action intact.
The rewriter does not sanitise and does not soften. Deciding whether an action is allowed is not its job and it has no way to do that job well: it sees one sentence, not the account, the permissions or the consequences. A model that quietly dropped "delete" would hand the router a different request from the one the person made, and the person would never learn their instruction had been edited. So the restatement carries the command as written, the router places it, and whatever runs it is where refusing belongs.
12.6% of rows (10,432) answer NO_REQUEST, and 16.9% (14,028)
answer with more than one line.
Who wrote it, and what was thrown away:
| generated by Qwen3.6-35B | 72,697 rows, 87.8% |
| generated by Qwen3.8-27B | 7,935 rows, 9.6% |
| generated by Gemma-4-E4B | 2,187 rows, 2.6% |
| rejected by the judge, not in the set | 72,996 rows |
A separate model read every message against written rules and answered a two-word verdict, decoding greedily so a verdict does not change between runs. The dataset card says where judge and generator were different models and where they stopped being.
Training
Fine-tuned from google/gemma-3-270m with mlx_lm lora, full fine-tune.
The released weights are the run's final adapter, chosen by a gate on 1,000
held-out examples rather than by the best number on a validation curve.
Where all of this happened
One desktop machine, and nothing left it.
| machine | Mac mini, Mac16,11 |
| chip | Apple M4 Pro |
| CPU | 14 cores, 10 performance and 4 efficiency |
| GPU | 20 cores |
| memory | 64 GB unified |
| system | macOS 26.4 |
| the fine-tune | |
|---|---|
| method | mlx_lm lora, full fine-tune |
| iterations | 20,704 |
| tokens seen | 2,383,728 |
| peak memory | 7.3 GB |
| speed | 5.6 iterations/s, 610 tokens/s |
| wall clock | 11:15 to 12:42, about 90 minutes |
Generators and judge ran on that same machine as local llama.cpp servers on loopback. No message here was written by a hosted API, and none was sent to one to be judged. The unified memory is why: a 35B generator and a fine-tune only share a desk when CPU and GPU read the same 64 GB.
A model that decides where a question goes does not need a cluster or a month. It needs a narrow job, data built for it, and a bench honest enough to say when a round did not help.
Licence
Gemma terms, which travel with the file: this is a fine-tune of Gemma-3-270M and inherits them.
- Downloads last month
- 589
Quantized
Model tree for Gramscii-IT/SemanticRepair-270M
Base model
google/gemma-3-270m