58 yesterday

NuExtract3 is a 4B vision-language reasoning model by NuMind for local document understanding. Turn text and document images into structured JSON or clean Markdown with Ollama, with support for multilingual documents, OCR, and template generation.

vision tools thinking
ollama run numind/nuextract3:bf16

Details

yesterday

3f72674d2a65 · 9.3GB ·

{ "architectures": [ "Qwen3_5ForConditionalGeneration" ], "bos_token_id": null, "dtype": "bfloat16",
{ "_from_model_config": true, "eos_token_id": 248044, "transformers_version": "5.5.4", "use_cache":
{ "image_processor": { "do_convert_rgb": true, "do_normalize": true, "do_rescale": true, "do_resize"
{ "version": "1.0", "truncation": null, "padding": null, "added_tokens": [ { "id": 248044, "content"
{ "add_prefix_space": false, "audio_bos_token": "<|audio_start|>", "audio_eos_token": "<|audio_end|>
{{- $template := "" -}} {{- $instructions := "" -}} {{- $previous := "" -}} {{- $mode := "content" -
{ "num_ctx": 131072, "stop": [ "<|im_end|>", "<|endoftext|>" ], "tem
738 tensors

Readme

NuExtract3 for Ollama

NuExtract3 is a unified 4B vision-language reasoning model developed by NuMind for document understanding. It turns text and document images into structured JSON or clean Markdown, locally with Ollama.

NuExtract3 supports structured extraction, OCR-like content extraction, document-to-Markdown, template generation, multilingual documents, and text/image inputs.

Links: NuMind · NuExtract Platform · Model weights · GGUF weights

Try it online: Open NuExtract3 in the public Hugging Face Space. No subscription, sign-up, or credit card is required.

From local experiments to production

NuMind builds specialized multimodal language models for extracting information from documents. NuExtract3 is the open-weight model in that work and can be run locally through this Ollama integration.

For teams that want the highest extraction quality without managing inference infrastructure, the NuExtract Platform provides a managed SaaS and API. The platform is powered by a substantially larger, higher-performing NuExtract model than the 4B open-weight model published here. Its web interface lets you define and test extraction tasks with templates and examples, while its API processes text, PDFs, spreadsheets, scans, and other document formats at scale. Private cloud and on-premises enterprise deployments are also available for workloads with stricter control or confidentiality requirements.

Start extracting with NuExtract or see the current platform pricing.

Choose a model tag

All three variants are published under the same model name and use the same prompt template and API:

Tag Weights Recommended for
nuextract3:q4_k_m Official Q4_K_M GGUF Most local use; lower storage and memory requirements
nuextract3:q6_k Official Q6_K GGUF Higher-quality local inference when more memory is available
nuextract3:bf16 Original BF16 Safetensors Reference-quality evaluation and quality-sensitive workloads

Q4_K_M is the recommended default for most users. Q6_K uses more storage and memory in exchange for retaining more model precision. BF16 preserves the original model precision but has the highest storage and memory requirements.

Benchmark highlights

The figures below are loaded from the official NuExtract3 model card. They report results for the upstream evaluated model; the Q4_K_M and Q6_K quantizations may produce slightly different results.

Structured extraction

NuExtract3 was evaluated on NuMind’s internal benchmark of approximately 600 diverse documents, covering visual understanding, OCR, reasoning, and long input and output contexts.

NuExtract3 structured-extraction benchmark

Document-to-Markdown

For document-to-Markdown, models were compared on 100 documents with challenging layouts and tables. The chart reports model preferences from the evaluation described in the upstream model card.

NuExtract3 document-to-Markdown benchmark

Markdown-to-structured

This evaluation first converts documents to Markdown and then performs structured extraction, measuring how well document content and layout survive the conversion.

NuExtract3 Markdown-to-structured benchmark

See the full benchmark methodology and results for evaluation details and limitations.

Run

ollama run nuextract3:q4_k_m

For Q6_K:

ollama run nuextract3:q6_k

For BF16:

ollama run nuextract3:bf16

The examples below default to nuextract3:q4_k_m. Set the MODEL environment variable to use Q6_K or BF16 without changing the code:

MODEL=nuextract3:q6_k python your_script.py
MODEL=nuextract3:bf16 python your_script.py

Context length and memory

The default Modelfile sets a 131,072-token context to support long and multi-page documents. A context this large can require substantial memory for the KV cache. If your hardware cannot accommodate it, or your documents are shorter, lower PARAMETER num_ctx in the Modelfile to a size appropriate for your workload.

Ollama message roles

The official Transformers/vLLM examples pass template, mode, and enable_thinking through chat_template_kwargs. Ollama does not expose those kwargs directly, so this Modelfile maps them to chat message roles:

template        JSON extraction template
instructions    optional extraction instructions
mode            content, markdown, template-generation, document-detection
previous_output optional previous model output
examples.input  in-context example input
examples.output expected output for the preceding example input
user            document text or images

Ollama enables thinking by default for models that advertise thinking support. For fast deterministic extraction, pass think=False with the Ollama Python client, or extra_body={"reasoning_effort": "none"} with the OpenAI-compatible API.

Text structured extraction

#!/usr/bin/env python3

import json
import os
from ollama import chat

model = os.getenv("MODEL", "nuextract3:q4_k_m")

template = {
    "store": "verbatim-string",
    "date": "date-time",
    "total": "number",
    "currency": ["USD", "EUR", "GBP", "JPY", "Other"],
    "items": [
        {
            "name": "verbatim-string",
            "price": "number",
        }
    ],
}

messages = [
    {"role": "template", "content": json.dumps(template, indent=4)},
    {
        "role": "user",
        "content": "Yesterday I bought apples and coffee at Trader Joe's for a total of $12.40.",
    },
]

response = chat(
    model=model,
    messages=messages,
    think=False,
    options={"temperature": 0.2},
)

print(response.message.content)

Image structured extraction

This mirrors the official Transformers example, where receipt.png is passed with a structured JSON template.

#!/usr/bin/env python3

import json
import os
from ollama import chat

model = os.getenv("MODEL", "nuextract3:q4_k_m")

template = {
    "store": "verbatim-string",
    "date": "date-time",
    "total": "number",
    "payment_method": "verbatim-string",
}

messages = [
    {"role": "template", "content": json.dumps(template, indent=4)},
    {
        "role": "user",
        "content": "",
        "images": ["receipt.png"],
    },
]

response = chat(
    model=model,
    messages=messages,
    think=False,
    options={"temperature": 0.2},
)

print(response.message.content)

Content extraction

For OCR or document-to-Markdown style extraction, use mode: content and no template. markdown is accepted as an alias and is rendered as NuExtract’s content task.

#!/usr/bin/env python3

import os
from ollama import chat

model = os.getenv("MODEL", "nuextract3:q4_k_m")

messages = [
    {"role": "mode", "content": "content"},
    {
        "role": "user",
        "content": "",
        "images": ["document.png"],
    },
]

response = chat(
    model=model,
    messages=messages,
    think=False,
    options={"temperature": 0.2},
)

print(response.message.content)

In-context examples

#!/usr/bin/env python3

import json
import os
from ollama import chat

model = os.getenv("MODEL", "nuextract3:q4_k_m")

template = {
    "names": ["string"],
}

messages = [
    {"role": "template", "content": json.dumps(template, indent=4)},
    {"role": "examples.input", "content": "Stephen is the manager at Susan's store."},
    {"role": "examples.output", "content": '{"names": ["-STEPHEN-", "-SUSAN-"]}'},
    {
        "role": "user",
        "content": "John went to the restaurant with Mary. James went to the cinema.",
    },
]

response = chat(
    model=model,
    messages=messages,
    think=False,
    options={"temperature": 0.2},
)

print(response.message.content)

OpenAI Python client

Ollama exposes an OpenAI-compatible API at http://localhost:11434/v1. Use a dummy API key; requests stay on your local Ollama server.

pip install openai

The custom template, mode, and examples.* roles are Ollama/NuExtract-specific. Static type checkers may warn because the upstream OpenAI API role set is narrower, but the Ollama endpoint accepts these message dictionaries. When thinking is enabled, Ollama returns it as response.choices[0].message.reasoning.

Text extraction

#!/usr/bin/env python3

import json
import os

from openai import OpenAI

client = OpenAI(
    base_url=os.getenv("OLLAMA_OPENAI_BASE_URL", "http://localhost:11434/v1"),
    api_key=os.getenv("OLLAMA_OPENAI_API_KEY", "ollama"),
)

model = os.getenv("MODEL", "nuextract3:q4_k_m")

template = {
    "store": "verbatim-string",
    "total": "number",
}

messages = [
    {"role": "template", "content": json.dumps(template, indent=4)},
    {"role": "user", "content": "Receipt from Trader Joes. Total: $12.40."},
]

response = client.chat.completions.create(
    model=model,
    messages=messages,
    temperature=0.2,
    max_tokens=256,
    extra_body={"reasoning_effort": "none"},
)

print(response.choices[0].message.content)

Image extraction

#!/usr/bin/env python3

import base64
import json
import mimetypes
import os
from pathlib import Path

from openai import OpenAI

client = OpenAI(
    base_url=os.getenv("OLLAMA_OPENAI_BASE_URL", "http://localhost:11434/v1"),
    api_key=os.getenv("OLLAMA_OPENAI_API_KEY", "ollama"),
)

model = os.getenv("MODEL", "nuextract3:q4_k_m")


def image_data_url(path: str) -> str:
    mime_type = mimetypes.guess_type(path)[0] or "image/png"
    data = base64.b64encode(Path(path).read_bytes()).decode("utf-8")
    return f"data:{mime_type};base64,{data}"


template = {
    "store": "verbatim-string",
    "date": "date-time",
    "total": "number",
    "payment_method": "verbatim-string",
}

messages = [
    {"role": "template", "content": json.dumps(template, indent=4)},
    {
        "role": "user",
        "content": [
            {
                "type": "image_url",
                "image_url": {"url": image_data_url("receipt.png")},
            }
        ],
    },
]

response = client.chat.completions.create(
    model=model,
    messages=messages,
    temperature=0.2,
    max_tokens=512,
    extra_body={"reasoning_effort": "none"},
)

print(response.choices[0].message.content)

Thinking mode

NuExtract recommends non-thinking mode for fast deterministic extraction. For harder layouts, enable thinking and raise temperature:

response = chat(
    model=model,
    messages=messages,
    think=True,
    options={"temperature": 0.6},
)

print(response.message.thinking)
print(response.message.content)

With the OpenAI-compatible API, request thinking with a reasoning effort and read the reasoning field:

response = client.chat.completions.create(
    model=model,
    messages=messages,
    temperature=0.6,
    max_tokens=1024,
    extra_body={"reasoning_effort": "medium"},
)

print(response.choices[0].message.reasoning)
print(response.choices[0].message.content)

Use reasoning_effort: "none" to disable thinking through the OpenAI-compatible API.

Citation

If you use NuExtract3, please cite NuMind:

@misc{nuextract3,
  title  = {NuExtract3},
  author = {NuMind},
  year   = {2026},
  url    = {https://nuextract.ai/}
}

License

NuExtract3 is released under the Apache License 2.0.