Downloads · 30 days
0
vrushket/legalcite-explain-7b-lora
legalcite-explain-7b-lora is a text generation model from vrushket. Use it when you need the model to write or continue text. The card lists the license as apache-2.0.
A QLoRA adapter on Qwen/Qwen2.5-7B-Instruct that writes a short, grounded explanation for a citation-support label. It does not decide the label — the label comes from legalcite-support-base (the primary encoder artif…
Downloads · 30 days
0
Access
Public
Updated Aug 5, 2026
Repo size
323 MB
Likes
0
Public
Click a slice to open those files.
.safetensors323 MB · 97%
From the Hugging Face model README
A QLoRA adapter on Qwen/Qwen2.5-7B-Instruct that writes a short,
grounded explanation for a citation-support label. It does not decide
the label — the label comes from legalcite-support-base (the primary
encoder artifact), upstream of this model in the legalcite pipeline.
This adapter's only job is to justify a verdict someone else already
reached, from the same evidence that produced it.
Automated verification only. Not legal advice. Every citation flagged or cleared here must be independently confirmed by a licensed attorney before filing. Jurisdiction: US federal and state case law only.
This notice is printed on every output of the legalcite CLI and cannot
be suppressed by any flag or config value.
Secondary, optional component: given (label, proposition, citation, case_name, cited_text), write one or two sentences explaining why the
cited text does or does not support the proposition, consistent with the
given label. Intended to make a legalcite finding easier for a human
reviewer to audit quickly — not to add independent verification weight.
Do not use this model for: deciding a support label (it doesn't have that capability by design — see "How this model was trained" below), legal advice, drafting argument text, or recommending alternative citations.
Important asymmetry to understand before trusting this model's output: if the upstream label is wrong, this model will still write a fluent, confident-sounding explanation justifying that wrong label — it was trained to explain a given verdict, not to independently check one. A well-written explanation is not evidence that the label is correct.
Not yet wired into the legalcite CLI. legalcite check currently
runs the encoder only — this adapter has to be invoked separately for
now, with a label you've already obtained (from the encoder, or
otherwise). This is an honest gap, not a design choice: the pipeline's
docstring has said "optional decoder explanation" since Stage E was
built, but CitationVerifier never actually calls a decoder. Wiring it
in is open work.
Requires peft, transformers, bitsandbytes, torch. The base model
downloads separately from this adapter (~15GB) the first time you run
this.
import torch
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
ADAPTER = "vrushket/legalcite-explain-7b-lora"
BASE_MODEL = "Qwen/Qwen2.5-7B-Instruct"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(ADAPTER)
base_model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL, quantization_config=bnb_config, device_map="auto"
)
model = PeftModel.from_pretrained(base_model, ADAPTER)
model.eval()
The prompt is ChatML: a fixed system message, then a user turn with the
label and evidence fields laid out plainly. Reusing the exact same
example verified in legalcite-support-base's model card (the encoder
classified it SUPPORTED at 0.99 confidence):
SYSTEM_PROMPT = (
"You are a citation-verification assistant. A citation-checking system has already "
"determined the label below. In one or two sentences, explain why the cited text does "
"or does not support the proposition, given that label. Do not decide or second-guess "
"the label. Do not give legal advice, and do not suggest an alternative citation."
)
label = "SUPPORTED"
proposition = (
"The right to privacy, as established in Roe v. Wade, 410 U.S. 113 (1973), "
"is broad enough to encompass a woman's decision whether to terminate her pregnancy."
)
citation_string = "410 U.S. 113"
case_name = "Roe v. Wade"
cited_text = (
"The Court held that the right to privacy, whether it be founded in the Fourteenth "
"Amendment's concept of personal liberty, is broad enough to encompass a woman's "
"decision whether or not to terminate her pregnancy."
)
user_prompt = (
f"Label: {label}\nProposition: {proposition}\n"
f"Citation: {citation_string} ({case_name})\nCited text: {cited_text}"
)
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_prompt},
]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
output_ids = model.generate(**inputs, max_new_tokens=150, do_sample=False)
explanation = tokenizer.decode(
output_ids[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
).strip()
print(explanation)
# The cited text supports this proposition. It states: "The Court held that the right
# to privacy, whether it be founded in the Fourteenth Amendment's concept of personal
# liberty, is broad enough to encompass a..."
Or, with the package installed, reuse the exact tested prompt-building code instead of hand-rolling it:
from legal_citation_check.models.explain import build_chat_messages
messages = build_chat_messages(label, proposition, citation_string, case_name, cited_text)
Note that legal_citation_check.models.explain.load_model_and_tokenizer
is the training-time loader (it applies a fresh LoRA config via
get_peft_model, it does not load saved adapter weights) — for
inference against these published weights, use PeftModel.from_pretrained
as shown above, not that function.
Qwen/Qwen2.5-7B-Instruct, 4-bit NF4 quantization (bfloat16 compute
dtype), LoRA rank 32 / alpha 64 / dropout 0.05, targeting
q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj. Prompt is
ChatML-formatted: a system message instructing the model to explain
(never decide) the label and never give legal advice, a user turn with
the (label, proposition, citation, case_name, cited_text) fields, and
an assistant turn with the target explanation. Loss is computed only on
the assistant turn (DataCollatorForCompletionOnlyLM).
There is no human-written explanation anywhere in this project's
dataset. The fix: examples.jsonl (produced during encoder training
data synthesis) already carries a perturbation field recording why a
given negative example was constructed (wrong_case_name,
fabricated_cite, real_cite_wrong_quote, etc.) — available at
training-target-generation time, but never part of what this model
actually sees as input, at training or inference. Each label/perturbation
combination maps to a literal template ("the cited text does/does not
say X"), never a legal conclusion — Stage D's gate explicitly fails any
explanation that crosses that line. This means the training signal is
grounded and correctly-labeled, but the range of phrasing and reasoning
styles it teaches is bounded by what a modest set of templates can
express — see "Known limitations" below.
15,000 training examples / 2,000 eval examples, uniform-randomly subsampled from the ~356K-example labeled set (a random sample from an already label-balanced set preserves the same label proportions). This is a deliberate resource trade-off, not a data-availability limit: a real training run on the full set measured ~53 seconds per optimizer step regardless of how batch size vs. gradient accumulation was split (same total compute either way), projecting to roughly 70 wall-clock hours against a target of single-digit hours. The subsampled run took 3.56 hours for 3 epochs across 3x RTX 6000 Ada (48GB, PCIe, no NVLink).
Documented here because they shaped the final training config, not just
as trivia: (1) a DDP model-loading race where concurrent from_pretrained
calls across ranks intermittently left one process with an empty model,
fixed with accelerate's main_process_first() barrier; (2) NCCL's
peer-to-peer transport hangs indefinitely on this PCIe-only, no-NVLink
hardware (NCCL_P2P_DISABLE=1 required, not optional); (3) DDP +
gradient checkpointing's default reentrant autograd is incompatible with
a frozen-base/LoRA model (RuntimeError: Expected to mark a variable ready only once), fixed with non-reentrant checkpointing; (4) the
originally-specified max_seq_length=4096 was ~2.5x the real data's
actual max (measured: p50=1010, p90=1235, p99=1487, max=1603 tokens),
causing both a CUDA OOM and much of the excess training time.
Eval loss dropped from 0.0063 (epoch 1) to 0.0041 (epoch 3) — consistent with the model learning the templated target patterns well, including on held-out examples (different citations from the training set, same underlying template distribution).
retrieved text on 100 samples; any explanation asserting a legal conclusion beyond 'the cited text does or does not say X' is a failure")
Ran an automated proxy for this gate — not the full human-judgment process the spec describes — on 100 held-out test examples: generated an explanation for each, checked for a fixed list of legal-conclusion-style phrases ("you should", "we recommend", "the law requires", "is unconstitutional", etc.).
Result: 0/100 forbidden-phrase violations. Spot-checked generations
were correctly label-consistent and grounded in the actual retrieved
text — SUPPORTED explanations quoted real evidence, FABRICATED/
wrong_case_name-style UNSUPPORTED explanations correctly named the
mismatch, on examples the adapter had not seen during training.
This is not the same as true human-judged faithfulness. The automated check catches an explicit list of forbidden phrases; it cannot catch a subtler legal-conclusion violation phrased differently, and it says nothing about explanation quality beyond the binary faithfulness question. Genuine human review of the 100-sample gate, as the spec describes, has not yet been done.
SUPPORTED/NOT_VERIFIABLE phrasing
patterns) are less certain to generalize well, in the same spirit as
the encoder card's documented generalization gap on
out-of-distribution proposition phrasing.Qwen/Qwen2.5-7B-Instruct (~15GB) loaded alongside it, typically in
4-bit.legalcite-support-base's model card, in particular
the documented out-of-distribution generalization gap on
synthetically-phrased or question-form propositions, and the
CourtListener existence-check rate limit (125 requests/day).This tool assists a legal professional in checking their own work. It
does not tell anyone what the law is, and does not create an
attorney-client relationship. This model writes an explanation of an
existing label — never a recommendation, never "you should cite X
instead." Every citation flagged or cleared by the legalcite pipeline
must be independently confirmed by a licensed attorney before filing.
Apache-2.0 (adapter weights). Base model Qwen/Qwen2.5-7B-Instruct is
licensed separately by Alibaba Cloud — check its own license before
redistribution. Training data: derived from CC0 (Caselaw Access Project)
and CourtListener public data; explanation targets are templated, not
sourced from any third-party text.