SchemaLM v2 — LFM2.5 350M

A 350M parameter model fine-tuned for JSON-schema-conditioned information extraction.
Give it a JSON Schema and any unstructured text — it returns a JSON object that matches the schema exactly.

Base model: LiquidAI/LFM2.5-350M (SSM hybrid, ideal for edge/CPU deployment)


Benchmark Results

Metric Score
Overall score (v2 training benchmark) 97.7%
JSON validity 100%
Schema conformance 100%
Field extraction accuracy 97.6%
Internal benchmark cases 96.2%

Evaluated on a 25-case internal benchmark covering easy/medium/hard extraction across 7 domains.

Domain Avg Accuracy
Software Projects ~74%
Governance / Compliance ~75%
HR ~85%
Education ~81%
Sales ~55%
Healthcare ~53%
Support (phone) ~61%

Training

  • Dataset: 49K synthetic schema-extraction examples (93 schemas, 29 domains, nested object/array coverage)
  • Method: PEFT/LoRA SFT — rank 32, alpha 64, ~2% trainable parameters
  • Epochs: 5 | LR: 2e-4 (cosine) | Max seq: 768 tokens
  • Precision: bf16 on Ampere/Hopper, fp16 otherwise

Usage

The model follows the ChatML format: system message = JSON Schema, user message = unstructured text, assistant response = JSON.

Basic (Transformers)

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch, json

model_id = "senthil090/schemalm-v2-lfm2-350m"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(
    model_id, torch_dtype=torch.bfloat16, device_map="auto"
)
model.eval()

def extract(schema: dict, text: str, max_new_tokens: int = 256) -> dict:
    messages = [
        {"role": "system", "content": json.dumps(schema)},
        {"role": "user",   "content": text},
    ]
    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():
        out = model.generate(
            **inputs,
            max_new_tokens=max_new_tokens,
            do_sample=False,
            repetition_penalty=1.08,
        )
    raw = tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
    return json.loads(raw.strip())

Example 1 — Sprint planning note → structured task

schema = {
    "type": "object",
    "properties": {
        "task_title":    {"type": "string"},
        "assignee":      {"type": "string"},
        "priority":      {"type": "string", "enum": ["critical", "high", "medium", "low"]},
        "story_points":  {"type": "integer"},
        "status":        {"type": "string", "enum": ["todo", "in_progress", "done"]},
        "epic":          {"type": "string"},
    },
    "required": ["task_title", "assignee", "priority"],
}

text = (
    "Assign the Redis caching implementation to Priya for Sprint 14. "
    "It's high priority under the Performance epic, estimated 5 story points. "
    "Mark it as in progress since she already started."
)

result = extract(schema, text)
# → {
#   "task_title": "Redis caching implementation",
#   "assignee": "Priya",
#   "priority": "high",
#   "story_points": 5,
#   "status": "in_progress",
#   "epic": "Performance"
# }

Example 2 — Nested support ticket (nested objects + arrays)

schema = {
    "type": "object",
    "properties": {
        "customer": {
            "type": "object",
            "properties": {
                "name":    {"type": "string"},
                "company": {"type": "string"},
                "email":   {"type": "string", "format": "email"},
            },
            "required": ["name", "company"],
        },
        "issue": {
            "type": "object",
            "properties": {
                "summary":  {"type": "string"},
                "severity": {"type": "string", "enum": ["low", "medium", "high", "critical"]},
            },
            "required": ["summary", "severity"],
        },
        "actions": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "owner": {"type": "string"},
                    "task":  {"type": "string"},
                },
                "required": ["owner", "task"],
            },
        },
    },
    "required": ["customer", "issue"],
}

text = (
    "Support note: customer Priya Shah from Velocity Corp, email priya@velocity.io. "
    "Dashboard charts won't load — severity high. "
    "Action: Sarah to open an engineering ticket. Marcus to send ETA to customer."
)

result = extract(schema, text)
# → {
#   "customer": {"name": "Priya Shah", "company": "Velocity Corp", "email": "priya@velocity.io"},
#   "issue": {"summary": "Dashboard charts won't load", "severity": "high"},
#   "actions": [
#     {"owner": "Sarah", "task": "open an engineering ticket"},
#     {"owner": "Marcus", "task": "send ETA to customer"}
#   ]
# }

Example 3 — Compliance finding with date and enum fields

schema = {
    "type": "object",
    "properties": {
        "finding_id":   {"type": "string"},
        "standard":     {"type": "string", "enum": ["ISO27001", "SOC2", "HIPAA", "GDPR", "PCI-DSS"]},
        "clause":       {"type": "string"},
        "severity":     {"type": "string", "enum": ["critical", "major", "minor", "observation"]},
        "description":  {"type": "string"},
        "due_date":     {"type": "string", "format": "date"},
        "owner":        {"type": "string"},
        "status":       {"type": "string", "enum": ["open", "in_remediation", "closed"]},
    },
    "required": ["standard", "severity", "description", "due_date"],
}

text = (
    "Audit finding AF-2024-089: ISO27001 clause A.9.4.2 violation — "
    "privileged accounts are not protected with MFA. Severity: major. "
    "Remediation owner: Chen, due by 2024-09-30. Status: in_remediation."
)

result = extract(schema, text)
# → {
#   "finding_id": "AF-2024-089",
#   "standard": "ISO27001",
#   "clause": "A.9.4.2",
#   "severity": "major",
#   "description": "Privileged accounts are not protected with MFA",
#   "due_date": "2024-09-30",
#   "owner": "Chen",
#   "status": "in_remediation"
# }

Example 4 — CPU inference (no GPU)

model = AutoModelForCausalLM.from_pretrained(
    "senthil090/schemalm-v2-lfm2-350m",
    torch_dtype=torch.float32,  # fp32 for CPU
)
model.eval()
# Same extract() function as above — just slower (~5–15s per call on a modern CPU)

Limitations

  • Best on extraction tasks (reading text and pulling fields) — not a general-purpose chat model
  • Weaker on very long schemas (>50 fields) or schemas with complex pattern/format constraints
  • Sales numeric extraction and phone-call transcripts have lower accuracy (~50–60%) — targeted in v2.1 hardcase training
  • Not suitable for code generation, reasoning, or creative tasks

Citation

@misc{schemalm-v2,
  title  = {SchemaLM v2 — JSON Schema-Conditioned Information Extraction},
  author = {Senthil},
  year   = {2025},
  url    = {https://huggingface.co/senthil090/schemalm-v2-lfm2-350m}
}
Downloads last month
2
Safetensors
Model size
0.4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for senthil090/schemalm-v2-lfm2-350m

Adapter
(27)
this model
Free AI Image Generator No sign-up. Instant results. Open Now