Instructions to use lendmindltd/lendmind-intake-4b with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use lendmindltd/lendmind-intake-4b with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-4B") model = PeftModel.from_pretrained(base_model, "lendmindltd/lendmind-intake-4b") - Notebooks
- Google Colab
- Kaggle
Lendmind-Intake-4b, release candidate 2 (lendmind-intake-4b-v1.0-rc2)
Published by Lendmind Ltd. A LoRA adapter for Qwen/Qwen3.5-4B that
reads a UK mortgage adviser's message into a structured frame. This repository is lendmind-intake-4b; this release
is the tagged revision v1.0-rc2.
Status: release candidate, not a release. On the headline evaluation stratum it meets 4 of our 5 bars; the fifth
(MARKET/UNKNOWN messages read as a case) misses by one message. On messages from writers it never saw it does not
meet the figure-precision bar (see Evaluation). Under our naming rule it is v1.0-rc2; v1.0 is kept
for a model that meets all five bars.
What it does
Lendmind-Intake-4b turns a UK mortgage adviser's message about a client into an IntakeFrame v4: one JSON object with the message's intents, the lenders it names, the client's stated figures and choices, each with the adviser's own words as its quote, and (as a second task) up to three plain questions for what a lender's affordability calculator still needs. A program then finds every quote in the message and reads the value from it. The model is trained to read figures, not to calculate or infer them, and to serve no verdict on a message that only asks about the market or follows up an earlier turn; Known limitations lists where it still does.
Two tasks, one adapter, two system prompts: EXTRACT (message → frame) and ASK (frame → the questions).
Intents, as the frame defines them: A, which lenders would lend this case (or whether one named lender would); B, what one named lender's criteria say on a point of policy; C, which products fit; MARKET, a criteria question across lenders; UNKNOWN, none of these, or a follow-up to an earlier turn.
Intended use and scope
Intended use: structured extraction from UK mortgage adviser notes, behind a constrained reader like the one described below, as one input to a system in which people make the decisions.
Not financial advice. Not for consumer use. Not for making lending decisions. The model reads what an adviser wrote; it does not assess affordability, eligibility or suitability, and its output is not a recommendation. Do not put it in front of consumers, and do not use its output to decide whether anyone is lent money.
Out of scope: any lending, affordability or eligibility decision; consumer-facing use; messages that are not adviser notes about a case; languages other than English and markets other than the UK; use without a constrained reader (a plain chat completion from this adapter carries none of the numbers below).
How it is run
The numbers on this card come from the adapter under Lendmind's constrained reader, not from the adapter alone:
- greedy decoding under the IntakeFrame v4 JSON schema, thinking off;
- every quote constrained to a stretch of the message, standing as whole words, so a value the message does not contain cannot be served;
- the override drop: any value the decoder had to force against the model's own top choice is withheld before anything is read, scored or served;
- the ASK task decoded under a per-message schema: one question per input to ask for, in order, with no figures.
The decoding constraint is open source as quotebound
(Apache-2.0): JSON-Schema-constrained greedy decoding for Qwen3.5 on Apple silicon, quotes bound to the user's
message (--quote-source user), a record of every token the constraint forced (from which a caller can withhold
those values), exact speculative decoding, and the constraint as a small C library for other decoders. quotebound
loads adapters in its own layout, not this PEFT file. It does not contain the reader's prompts, the IntakeFrame v4
schema, the override drop, the rules that decide what to ask, or the scorer, and those are not part of this
upload either. The numbers here were measured with Lendmind's internal build of the decoder that quotebound was
released from, not with the public release.
Reference hardware for the numbers: Apple M4 Pro, 48 GB, MLX 0.32.3, bf16 weights, the adapter applied unmerged, plain greedy decoding. Exact speculative drafts are byte-identical to plain greedy on the 470 dev rows (measured with rc1's adapter) and cut EXTRACT from 7.10 s to 2.31 s and ASK from 2.40 s to 0.97 s a row on that Mac.
Loading the adapter
import torch
from peft import PeftModel
from transformers import AutoModelForImageTextToText, AutoTokenizer
BASE, BASE_REV = "Qwen/Qwen3.5-4B", "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a"
tokenizer = AutoTokenizer.from_pretrained(BASE, revision=BASE_REV)
base = AutoModelForImageTextToText.from_pretrained(BASE, revision=BASE_REV, dtype=torch.bfloat16)
model = PeftModel.from_pretrained(base, "lendmindltd/lendmind-intake-4b", revision="v1.0-rc2")
Load the base with AutoModelForImageTextToText (the checkpoint's Qwen3_5ForConditionalGeneration):
target_modules is a pattern over that class's model.language_model.layers.* projections, and it matches no module
of the text-only class. Nothing in the vision tower is adapted. Prompts are rendered with the base's chat template,
generation prompt on and enable_thinking=False.
Parity of this PEFT file with the trained adapter
adapter_model.safetensors is a rename of the trained adapter's 400 float32 tensors to PEFT's names, with no
transpose and no cast: every tensor is byte-identical to the file the trainer wrote. Loaded onto the base with
PeftModel.from_pretrained (transformers 5.14.1, peft 0.21.2, torch 2.13.0), it was compared on 22 Oct 2026, on CPU
in float32, with the trainer's own PyTorch LoRA layer holding the trained tensors (scale alpha / r = 2.0), on 10 fixed
dev prompts (7 EXTRACT, 3 ASK):
- 200 of 200 adapted modules wrapped by PEFT, every
lora_A/lora_Bbit-identical to the trained tensors; - logits max-abs difference 0.0 over every prompt position and every greedy step (bar 1e-2);
- greedy tokens identical on 10 of 10 prompts (1,160 tokens, each to its stop token), and
generate()gives the same tokens.
Not covered: bf16, a GPU, and the constrained reader, which is not part of the adapter.
Training
| base | Qwen/Qwen3.5-4B, revision 851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a, bf16; hybrid Gated DeltaNet and gated attention, 32 layers, hidden 2560, tied embeddings |
| adapter | LoRA rank 16, alpha 32 (scale 2.0), on q/k/v/o_proj (8 full-attention layers), in_proj_qkv, in_proj_z, out_proj (24 Gated DeltaNet layers) and gate/up/down_proj (32 layers) of the language model: 200 modules, 400 float32 tensors, 122 MB |
| recipe | rc1's: learning rate 1e-4, linear warmup over 5% of steps then cosine to zero, AdamW (0.9, 0.999, eps 1e-8), no weight decay, global-norm clip 1.0, 2 epochs, batches packed to 4,096 tokens, max length 2,048, gradient checkpointing, seed 0; loss on the assistant turns only |
| trainer | a PyTorch LoRA trainer (PyTorch 2.11.0 + CUDA 12.8, transformers 5.14.1, flash-linear-attention 0.5.2), bf16 |
| hardware | one NVIDIA L4 (24 GB) on a Google Cloud Spot VM, 19 Oct 2026; the final attempt ran 1.14 h after two Spot reclaims |
| steps | 1,141 of 1,141; this checkpoint is step 1,000, the best dev loss (0.010765; 1.033436 before the first step; rc1: 0.009915) |
| training data | 4,741 examples: 2,971 EXTRACT + 1,770 ASK; dev 470 (288 EXTRACT + 182 ASK), rc1's |
| label rules | IntakeFrame v4 labelling rules v2.2 at training time |
| second seed | trained with the same recipe (seed 1): 1,143 steps, best at step 1,143, dev loss 0.011002; the same 4 of 5 bars on s2_unseen + s3; not part of this release |
Training data provenance
Synthetic throughout: no real client, adviser or lender data.
- 3,416 rows from rc1's training build: adviser messages written by four open models (Gemma 4 E4B-it, Qwen3-8B,
Mistral Small 3.2 24B, Phi-4) run locally from code-generated cases, labelled by construction and audited (label
rules v2 and v2.1); the exact writer checkpoints are listed on the
v1.0-rc1card. - 1,325 rows from the generator's training split: 1,000 code-generated seeds, texts written by a larger open-weight model called through an API, every label verified against its seed by two judges of different model families (Claude Opus 5.5, Gemini 4); gold where both agree (810 gold, 190 contested left out).
- Dev: the same 470 rows as rc1. The evaluation set (parts s1, s2, s3) is never used in training; the training builder drops any text it shares with the evaluation set, by hash.
The training data and the evaluation set are not published.
Evaluation
The evaluation set is held by Lendmind and never published or used in training. Two strata are reported: s2_unseen, adviser messages from writers (models) whose texts are in no training row, and s3, the training data generator's held-out set. The headline is the two together (547 messages scored).
The bars:
- figure precision, tier 1 (≥ .98): of the tier-1 figures served (annual income, purchase price, deposit, deposit %, LTV, loan, age, term in years), the share at the label's value;
- intent precision, lowest of A/B/C (≥ .95);
- clarification recall, designed (≥ .90): of the messages written to need a clarifying question, the share whose question is asked;
- MARKET/UNKNOWN read as A (0): messages labelled MARKET or UNKNOWN (or a refusal) that are read as a case;
- frames returned (1.0): the share of messages for which a schema-valid frame is returned.
The labelling rules were revised after audits of the evaluation labels; v2.5 is the latest, and the tables below give v2.1 (the version the release candidate was first judged on) and v2.5. A revision changes the expected labels, not the model's outputs; for rc2 it moved no bar on any stratum.
Labels v2.1, s2_unseen + s3 (547 messages)
| reader | figure precision tier 1 (bar ≥ .98) | intent precision, lowest of A/B/C (≥ .95) | clarification recall, designed (≥ .90) | MARKET/UNKNOWN read as A (0) | frames returned (1.0) | bars met |
|---|---|---|---|---|---|---|
| lendmind-intake-4b-v1.0-rc2 (seed 0) | .991 | .975 | .929 | 1 of 111 | 1.000 | 4 of 5 |
| seed 1 (not released) | .989 | .983 | .935 | 1 of 111 | 1.000 | 4 of 5 |
rc1 (tag v1.0-rc1) |
.975 | .778 | .858 | 3 of 111 | 1.000 | 1 of 5 |
By s3 difficulty level (the generator's levels 1 to 5; bars met of 5), rc2 / seed 1 / rc1: level 1 4/5/4, level 2 5/4/3, level 3 4/5/2, level 4 4/4/2, level 5 4/4/1.
Labels v2.5, rc2 by stratum
| stratum | messages | figure precision tier 1 (≥ .98) | intent precision, lowest of A/B/C (≥ .95) | clarification recall, designed (≥ .90) | MARKET/UNKNOWN read as A (0) | frames returned (1.0) | bars met |
|---|---|---|---|---|---|---|---|
| s2_unseen + s3 | 547 | .991 (16 wrong of 1,796) | .975 | .929 (157 of 169) | 1 of 111 | 1.000 | 4 of 5 |
| s2_unseen | 106 | .967 (7 wrong of 209) | .955 | .857 (12 of 14) | 0 of 37 | 1.000 | 3 of 5 |
| s3 | 441 | .994 (9 wrong of 1,587) | .979 | .935 (145 of 155) | 1 of 74 | 1.000 | 4 of 5 |
Labels v2.5, figure precision tier 1 (wrong of served) and bars met, our builds
| reader | s2_unseen + s3 | bars met | s2_unseen | bars met | s3 | bars met |
|---|---|---|---|---|---|---|
| lendmind-intake-4b-v1.0-rc2 (seed 0) | .991 (16 of 1,796) | 4 of 5 | .967 (7 of 209) | 3 of 5 | .994 (9 of 1,587) | 4 of 5 |
| seed 1 (not released) | .989 (19 of 1,796) | 4 of 5 | .957 (9 of 208) | 3 of 5 | .994 (10 of 1,588) | 4 of 5 |
rc1 (tag v1.0-rc1) |
.975 (44 of 1,769) | 1 of 5 | .969 (6 of 196) | 3 of 5 | .976 (38 of 1,573) | 1 of 5 |
rc2 does not meet the figure-precision bar on messages from unseen writers. On s2_unseen, under labels v2.5, it serves 7 wrong tier-1 figures of 209 (precision .967; under v2.1, 8 of 209, .962). The bar is .98, which at that volume allows at most 4 wrong. Its clarification recall there, .857 (12 of 14 designed asks), is also under its bar, so rc2 meets 3 of 5 bars on s2_unseen; rc1 and the second seed also meet 3 of 5 there.
On the dev set (288 EXTRACT messages, with the 182 ASK rows behind them; dev labels v2): figure precision .995 (842 of 846), figure recall .995, choices precision .978 and recall .985, lenders precision and recall 1.000, lowest intent precision .955, clarification recall .967 (319 of 330), no MARKET/UNKNOWN message read as A (0 of 63), every frame returned: all five bars met on dev, as for rc1.
Latency is reported, not yet a bar: p95 11.5 s a message on the evaluation set on the M4 Pro with plain greedy decoding (rc1: 13.0 s).
Known limitations
- One MARKET/UNKNOWN message is read as a case. For rc2 it is a follow-up that carries a complete second case; the model serves a frame instead of asking for the use case. The second seed reads that message right and instead misses a refusal written as the last short sentence after a full case in shorthand. Both are errors under the labelling rules.
- Unseen writers. On messages from writers the model never saw (s2_unseen), figure precision (.967 under labels v2.5, 7 wrong of 209) and clarification recall (.857) are under their bars. The gain over rc1 is on the training data generator's distribution (s3), not on unseen writers.
- Numbers are read, never computed. A total, a conversion or a range is not labelled; a figure in a range or an uncorrected correction is left out and asked for.
- Speed. About 24 tokens a second bf16 on the M4 Pro with plain greedy decoding; exact drafts cut EXTRACT from 7.10 s to 2.31 s a row and ASK from 2.40 s to 0.97 s (dev rows, byte-identical). About 10 to 12 tokens a second on an NVIDIA L4 with a CUDA port of the reader, plain greedy, one sequence at a time.
- CUDA is not byte-identical. A CUDA port of the reader (measured with rc1) matched the Mac on every s2_unseen rate but served one new wrong value on the dev set and one on another evaluation stratum, each at a near-tie between the top two tokens. A 0.3-logit margin floor on each value closes that on the dev set at the cost of nine values withheld on the Mac; the evaluation set has not been checked under it. Every serving path must pass the full bar before use.
- English, UK mortgage domain only. Written for UK adviser notes, intermediary lending criteria and lender affordability calculators; it has not been evaluated on anything else.
Files
| file | what it is |
|---|---|
adapter_model.safetensors |
the LoRA weights, PEFT names, float32 (sha256 5b969bc4198d10eed4170a0b88cdf752a9379c5a8d79d8a12f799edd1f19a235) |
adapter_config.json |
the PEFT config: r 16, lora_alpha 32, target_modules over the language model only |
LICENSE |
Apache License 2.0, the text distributed with the base model |
NOTICE |
the modification and attribution notices |
README.md |
this card |
Licences and provenance
- This adapter: Apache License 2.0, © 2026 Lendmind Ltd.
- Base:
Qwen/Qwen3.5-4B, Apache License 2.0. This model is a modification of it;LICENSEis the licence text andNOTICEstates the modification. - Writers of training texts: Gemma 4 E4B-it (Apache 2.0), Qwen3-8B and Mistral Small 3.2 24B (Apache 2.0), Phi-4 (MIT), and a larger open-weight model called through an API for the generator split. Judges of labels: Claude Opus 5.5 and Gemini 4 (verification only; they wrote no training text).
- No personal data: every message is synthetic; the evaluation set and the training data are not published.
Versions
| name | result |
|---|---|
| lendmind-intake-4b-v0.1 … v0.4 | earlier internal rounds, not released |
| lendmind-intake-4b-v1.0-rc1 | step 700 of 792; 1 of 5 bars on s2_unseen + s3 (labels v2.1 and v2.5); tag v1.0-rc1 of this repository |
| lendmind-intake-4b-v1.0-rc2 | step 1,000 of 1,141; 4 of 5 bars on s2_unseen + s3 (labels v2.1 and v2.5); 3 of 5 on s2_unseen |
| lendmind-intake-4b-v1.0 | not yet: all 5 bars on the evaluation set |
- Downloads last month
- -
Model tree for lendmindltd/lendmind-intake-4b
Evaluation results
- Figure precision, tier 1 (bar 0.98) on Lendmind intake evaluation set, s2_unseen + s3, 547 messages, labels v2.5 (not published)self-reported0.991
- Intent precision, lowest of A/B/C (bar 0.95) on Lendmind intake evaluation set, s2_unseen + s3, 547 messages, labels v2.5 (not published)self-reported0.975
- Clarification recall, designed asks (bar 0.90) on Lendmind intake evaluation set, s2_unseen + s3, 547 messages, labels v2.5 (not published)self-reported0.929
- MARKET/UNKNOWN messages read as a case, of 111 (bar 0) on Lendmind intake evaluation set, s2_unseen + s3, 547 messages, labels v2.5 (not published)self-reported1.000
- Frames returned (bar 1.0) on Lendmind intake evaluation set, s2_unseen + s3, 547 messages, labels v2.5 (not published)self-reported1.000
- Figure precision, tier 1 (bar 0.98) on Lendmind intake evaluation set, s2_unseen (unseen writers), 106 messages, labels v2.5 (not published)self-reported0.967
- Intent precision, lowest of A/B/C (bar 0.95) on Lendmind intake evaluation set, s2_unseen (unseen writers), 106 messages, labels v2.5 (not published)self-reported0.955
- Clarification recall, designed asks (bar 0.90) on Lendmind intake evaluation set, s2_unseen (unseen writers), 106 messages, labels v2.5 (not published)self-reported0.857