Review · updated OCT 12

Nemotron 3 Super review: a solid all-rounder

It scored 85 out of 100, #13 of 63. It solved 21 of 30 coding jobs and scored 100 on reading documents. Runs on a Mac with 128 GB.

The short version
  • Nemotron 3 Super is a free model from NVIDIA that you can run on your own computer. In our tests it's a solid all-rounder: 85 out of 100, #13 of 63.
  • It solved 21 of 30 coding jobs and scored 100 on reading documents. On our hardest tasks it scored 83.
  • Runs on a Mac with 128 GB.

Coding

Our coding test is 30 programming jobs, from small ones like reading time durations or cleaning up messy data to harder ones like a config-file parser or a double-entry ledger. We run each answer against tests the model never sees, and a job only counts if everything passes. Nemotron 3 Super got 21 of 30 right. The best local coders solved 29 of 30.

Reading documents

The second test hands the model things like an expense claim thread, a pay stub or an insurance statement, and asks for specific numbers and dates. Many questions need a bit of math, or noticing a correction further down the email. Nemotron 3 Super scored 100, the best result we've seen.

TestScorePublic questionsSecret questions
Coding707170
Reading documents10098100
Decisions9410092

On the 18 hardest tasks (included in the scores above) it scored 83. This number separates the top models.

We tested the full-size version online. The smaller versions you download usually score a little lower.

Can your computer run it?

You can download Nemotron 3 Super in several sizes. Smaller versions fit on more computers and lose a little quality, so pick the biggest one that fits.

Your computerRuns it?Version to download
Graphics card, 8 GBNo–
Graphics card, 12 GBNo–
Graphics card, 16 GBNo–
Graphics card, 24 GBNo–
Graphics card, 32 GBNo–
Mac, 16 GBNo–
Mac, 24 GBNo–
Mac, 32 GBNo–
Mac, 48 GBNo–
Mac, 64 GBNo–
Mac, 96 GBNo–
Mac, 128 GBYesQ5

For normal-length chats. Long documents need more memory. Q4 or Q8? · How we worked this out

How to try it

Search for "Nemotron 3 Super GGUF" in LM Studio or on Hugging Face and pick the version that fits your computer. New to this? Start with our Ollama guide.

Want the strongest all-rounder instead? Qwen3.6 27B scored 97.

Its actual answers

What Nemotron 3 Super wrote for our public questions, word for word.

Coding: INI parser (hard) Didn't work
```python
def parse_ini(text: str) -> dict[str, dict[str, str]]:
    """Parse INI text according to the specified dialect."""
    import re

    lines = text.splitlines()
    sections: dict[str, dict[str, str]] = {}
    current_section: str | None = None

    # State for building a multi-line value
    building = False
    building_key: str | None = None
    building_parts: list[str] = []

    def finalize_building() -> None:
        """Store the accumulated value for the current key."""
        nonlocal building, building_key, building_parts, current_section, sections
        if building:
            if current_section is None:
                raise ValueError("Key line before any section header")
            sections.setdefault(current_section, {})[building_key] = "\n".join(building_parts)
            building = False
            building_key = None
            building_parts = []

    def parse_value(v: str) -> str:
        """Parse a raw value string according to quoting and comment rules."""
        in_quote = False
        i = 0
        L = len(v)
        while i < L:
            ch = v[i]
            if ch == '"':
                in_quote = not in_quote
                i += 1
            elif not in_quote and i + 1 < L and v[i] in " \t" and v[i + 1] in ";#":
                # start of an inline comment
                return v[:i]
            else:
                i += 1
        commentless = v
        stripped = commentless.strip()
        if len(stripped) >= 2 and stripped[0] == '"' and stripped[-1] == '"':
            # quoted value – keep interior exactly, remove the quotes
            return stripped[1:-1]
        else:
            # unquoted value – strip surrounding whitespace
            return commentless.strip()

    idx = 0
    while idx < len(lines):
        line = lines[idx]
        stripped = line.lstrip()

        # 1. Empty line or comment line
        if not stripped or stripped[0] in ";#":
            if building:
                finalize_building()
            idx += 1
            continue

        # 2. Section header
        if stripped.startswith("["):
            if building:
                finalize_building()
            bracket_end = stripped.find("]", 1)
            if bracket_end == -1:
                raise ValueError(f"Malformed section header: {line}")
            name = stripped[1:bracket_end].strip()
            remainder = stripped[bracket_end + 1 :]
            # Remove possible inline comment (outside quotes) from the remainder
            comment_pos = None
            for marker in (" ;", " #"):
                pos = remainder.find(marker)
                if pos != -1:
                    if comment_pos is None or pos < comment_pos:
                        comment_pos = pos
            if comment_pos is not None:
                remainder = remainder[:comment_pos]
            if remainder.strip():
                raise ValueError(f"Extra text after section header: {line}")
            current_section = name
            sections.setdefault(current_section, {})
            idx += 1
            continue

        # 3. Continuation line (only if we are currently building a value)
        if building and line and line[0] in " \t":
            building_parts.append(line.strip())
            idx += 1
            continue

        # 4. Key line
        if building:
            finalize_building()

        eq_pos = line.find("=")
        colon_pos = line.find(":")
        delim_pos = -1
        if eq_pos != -1 and colon_pos != -1:
            delim_pos = min(eq_pos, colon_pos)
        elif eq_pos != -1:
            delim_pos = eq_pos
        elif colon_pos != -1:
            delim_pos = colon_pos
        else:
            raise ValueError(f"Malformed key line (no delimiter): {line}")

        key_raw = line[:delim_pos]
        value_raw = line[delim_pos + 1 :]
        key = key_raw.strip().lower()

        value = parse_value(value_raw)

        building_key = key
        building_parts = [value]
        building = True
        idx += 1
        continue

    # Finalise any pending value after the loop
    if building:
        finalize_building()

    # The DEFAULT section is special: its keys are inherited but not returned
    default_dict = sections.pop("DEFAULT", {})

    # ------------------------------------------------------------------
    # Interpolation resolution (recursive, with cycle detection)
    # ------------------------------------------------------------------
    pattern = re.compile(r"\$\{([^}]+)\}")

    def get_raw(sec: str, key: str) -> str:
        """Fetch the raw (uninterpolated) value for a key, falling back to DEFAULT."""
        if sec in sections and key in sections[sec]:
            return sections[sec][key]
        if key in default_dict:
            return default_dict[key]
        raise KeyError(f"Key {key!r} not found in section {sec!r} or DEFAULT")

    resolved: dict[tuple[str, str], str] = {}

    def resolve(sec: str, key: str, visited: set[tuple[str, str]]) -> str:
        """Return the fully interpolated value for (sec, key)."""
        if (sec, key) in visited:
            raise ValueError(f"Interpolation cycle detected involving {sec!r}:{key!r}")
        if (sec, key) in resolved:
            return resolved[(sec, key)]

        raw = get_raw(sec, key)

        def repl(m: re.Match) -> str:
            inner = m.group(1)
            if ":" in inner:
                sec_name, subkey = inner.split(":", 1)
                sec_name = sec_name.strip()
                subkey = subkey.strip().lower()
            else:
                sec_name = sec
                subkey = inner.strip().lower()
            return resolve(sec_name, subkey, visited | {(sec, key)})

        result = raw
        while True:
            m = pattern.search(result)
            if not m:
                break
            replacement = repl(m)
            result = result[:m.start()] + replacement + result[m.end():]

        resolved[(sec, key)] = result
        return result

    output: dict[str, dict[str, str]] = {}
    for sec_name, sec_dict in sections.items():
        if sec_name == "DEFAULT":
            continue
        final_dict: dict[str, str] = {}
        for key in sec_dict:
            final_dict[key] = resolve(sec_name, key, set())
        output[sec_name] = final_dict

    return output
```
Coding: TTL LRU (hard) Worked
```python
from collections import OrderedDict
from typing import Callable, Any, Dict, Tuple

class TTLCache:
    def __init__(self, capacity: int, ttl: float, clock: Callable[[], float]):
        self.capacity = capacity
        self.ttl = ttl
        self.clock = clock
        self._data: OrderedDict[Any, Tuple[Any, float]] = OrderedDict()
        self._hits = 0
        self._misses = 0
        self._evictions = 0
        self._expirations = 0

    def _now(self) -> float:
        return self.clock()

    def _remove_expired(self) -> None:
        """Delete all entries that have expired at the current time."""
        now = self._now()
        expired_keys = [k for k, (_, exp) in self._data.items() if now >= exp]
        for k in expired_keys:
            del self._data[k]
            self._expirations += 1

    def put(self, key: Any, value: Any) -> None:
        """Insert or replace a key, respecting capacity and TTL rules."""
        if self.capacity == 0:
            return

        now = self._now()
        if key in self._data:
            # Refresh existing entry
            self._data[key] = (value, now + self.ttl)
            self._data.move_to_end(key)
            return

        # New key: first purge expired entries
        self._remove_expired()
        if len(self._data) >= self.capacity:
            # Evict least‑recently‑used item
            lru_key, _ = self._data.popitem(last=False)
            self._evictions += 1

        self._data[key] = (value, now + self.ttl)
        self._data.move_to_end(key)   # ensure it's marked as most‑recent

    def get(self, key: Any, default: Any = None) -> Any:
        """Return value if present and not expired; otherwise return default."""
        if self.capacity == 0:
            self._misses += 1
            return default

        now = self._now()
        if key in self._data:
            value, exp = self._data[key]
            if now >= exp:
                # Expired entry – remove and count as expiration & miss
                del self._data[key]
                self._expirations += 1
                self._misses += 1
                return default
            # Hit – move to most‑recent
            self._hits += 1
            self._data.move_to_end(key)
            return value
        else:
            self._misses += 1
            return default

    def __len__(self) -> int:
        """Number of non‑expired entries (does not affect stats)."""
        now = self._now()
        return sum(1 for _, exp in self._data.values() if now < exp)

    def stats(self) -> Dict[str, int]:
        """Return hit/miss/eviction/expiration counters."""
        return {
            "hits": self._hits,
            "misses": self._misses,
            "evictions": self._evictions,
            "expirations": self._expirations,
        }
```
Coding: Parse duration Didn't work
```python
def parse_duration(s: str) -> int:
    """
    Convert a duration string to total seconds.

    Supported format: optional whitespace, then parts in order h, m, s.
    Each part: non‑negative integer followed immediately by its unit (case‑insensitive).
    Each unit may appear at most once. Whitespace may appear anywhere between parts.
    Raises ValueError for any malformed input.
    """
    if not isinstance(s, str):
        raise ValueError("Input must be a string")

    i = 0
    n = len(s)
    total_seconds = 0
    seen_units = set()
    # mapping unit -> (index in order, seconds per unit)
    order = {'h': 0, 'm': 1, 's': 2}
    multipliers = {'h': 3600, 'm': 60, 's': 1}
    last_order_index = -1  # ensures units appear in non-decreasing order

    while True:
        # skip whitespace
        while i < n and s[i].isspace():
            i += 1
        if i >= n:  # reached end
            break

        # parse integer part
        start = i
        while i < n and s[i].isdigit():
            i += 1
        if start == i:  # no digits found
            raise ValueError("Expected a number before a unit")
        number = int(s[start:i])

        # parse unit
        if i >= n:
            raise ValueError("Missing unit after number")
        unit_char = s[i]
        i += 1
        unit = unit_char.lower()
        if unit not in order:
            raise ValueError(f"Unknown unit '{unit_char}'")
        # check for repeated unit
        if unit in seen_units:
            raise ValueError(f"Repeated unit '{unit_char}'")
        # check ordering
        cur_order = order[unit]
        if cur_order < last_order_index:
            raise ValueError(f"Units out of order: '{unit_char}' appears after a later unit")
        # accumulate seconds
        total_seconds += number * multipliers[unit]
        # record state
        seen_units.add(unit)
        last_order_index = cur_order

        # continue loop; whitespace before next part will be skipped at top

    # after processing all parts, ensure no stray characters remain
    while i < n and s[i].isspace():
        i += 1
    if i != n:
        raise ValueError("Invalid characters in duration string")

    return total_seconds
```
This implementation follows the specification: it accepts optional whitespace, ensures each unit appears at most once and in the correct order (h → m → s), rejects malformed inputs with a `ValueError`, and returns the total duration in seconds. No external libraries beyond the standard library are used.
Coding: Summarize ranges Worked
```python
def summarize_ranges(nums: list[int]) -> str:
    """Return a compact string representation of sorted unique integers.
    Consecutive runs of length >=3 are shown as "start..end";
    shorter runs are listed individually, joined by commas without spaces.
    """
    if not nums:
        return ""

    # Sort and deduplicate
    uniq = sorted(set(nums))
    parts = []
    i = 0
    n = len(uniq)

    while i < n:
        start = uniq[i]
        j = i
        # Extend while the next number is consecutive
        while j + 1 < n and uniq[j + 1] == uniq[j] + 1:
            j += 1
        end = uniq[j]

        # Determine formatting based on run length
        if j - i + 1 >= 3:
            parts.append(f"{start}..{end}")
        else:
            for k in range(i, j + 1):
                parts.append(str(uniq[k]))

        i = j + 1

    return ",".join(parts)
```
Coding: Top customers Worked
```python
def top_customers(rows: list[dict], n: int) -> list[tuple[str, float]]:
    """
    Return the top `n` customers by total amount.

    Each row is expected to have a "customer" key (string) and an "amount"
    key (string possibly containing '$', commas, and a leading sign).
    Customer names are matched case‑insensitively; the name returned is the
    trimmed spelling from the first valid row for that customer.
    Rows with empty customer names or unparsable amounts are ignored.
    Totals are summed per customer, rounded to two decimal places, and the
    result is sorted by total descending then name ascending.
    """
    if n <= 0:
        return []

    # mapping: lower‑cased name -> [original_name, accumulated_total]
    accum: dict[str, list] = {}

    for row in rows:
        raw_cust = row.get('customer')
        raw_amt = row.get('amount')
        if raw_cust is None or raw_amt is None:
            continue

        name = raw_cust.strip()
        if not name:
            continue

        # Parse amount: strip, remove $ and commas, then convert to float
        amt_str = str(raw_amt).strip()
        cleaned = amt_str.replace('$', '').replace(',', '')
        try:
            amount = float(cleaned)
        except ValueError:
            continue

        norm = name.lower()
        if norm not in accum:
            accum[norm] = [name, amount]
        else:
            accum[norm][1] += amount

    # Build list of (original_name, rounded_total)
    result = [(orig, round(total, 2)) for orig, total in accum.values()]

    # Sort: descending total, then ascending name (case‑insensitive)
    result.sort(key=lambda x: (-x[1], x[0].lower()))

    return result[:n]
```
Coding: Slugify Worked
```python
import unicodedata
import re

def slugify(title: str, max_len: int = 50) -> str:
    # 1. Transliterate to ASCII, dropping any remaining non‑ASCII characters
    ascii_title = unicodedata.normalize('NFKD', title).encode('ascii', 'ignore').decode('ascii')
    # 2. Lowercase
    ascii_title = ascii_title.lower()
    # 3. Replace runs of non‑a-z0-9 with a single hyphen and strip leading/trailing hyphens
    slug = re.sub(r'[^a-z0-9]+', '-', ascii_title).strip('-')
    # 4. Truncate if needed, respecting word boundaries
    if len(slug) <= max_len:
        return slug
    words = slug.split('-')
    result = ''
    for w in words:
        candidate = w if not result else f'{result}-{w}'
        if len(candidate) <= max_len:
            result = candidate
        else:
            break
    if result:
        return result.rstrip('-')
    # first word exceeds max_len – hard cut
    return slug[:max_len].rstrip('-')
```
Coding: Token bucket Worked
```python
class TokenBucket:
    """Token bucket rate limiter.

    Args:
        capacity: Maximum number of tokens the bucket can hold.
        refill_per_sec: Rate at which tokens are added per second.
        clock: Zero‑argument callable returning the current time as a float.
    """

    def __init__(self, capacity: float, refill_per_sec: float, clock):
        if capacity < 0:
            raise ValueError("capacity must be non‑negative")
        if refill_per_sec < 0:
            raise ValueError("refill_per_sec must be non‑negative")
        self._capacity = float(capacity)
        self._refill_rate = float(refill_per_sec)
        self._clock = clock
        self._last_time = self._clock()
        self._tokens = float(capacity)  # start full

    def _refill(self):
        """Add tokens based on elapsed time, capping at capacity."""
        now = self._clock()
        # Guard against a non‑monotonic clock
        elapsed = max(0.0, now - self._last_time)
        if elapsed and self._refill_rate:
            self._tokens = min(self._capacity,
                               self._tokens + elapsed * self._refill_rate)
        self._last_time = now

    @property
    def tokens(self) -> float:
        """Current token count after performing a refill."""
        self._refill()
        return self._tokens

    def allow(self, cost: float = 1.0) -> bool:
        """Attempt to consume `cost` tokens.

        Returns True if enough tokens were available and they were deducted,
        otherwise False and the bucket remains unchanged.
        """
        if cost < 0:
            raise ValueError("cost must be non‑negative")
        self._refill()
        if self._tokens >= cost:
            self._tokens -= cost
            return True
        return False
```
Decisions: Refund window (hard) 100% right
{
  "outcome": {
    "full_refund": 0,
    "partial_refund": 0,
    "store_credit": 1,
    "nothing": 0
  },
  "defective": {
    "true": 0,
    "false": 1
  }
}
Decisions: Incident matrix (hard) 100% right
{
  "severity": {"0": 0, "1": 1, "2": 0, "3": 0},
  "page": {"true": 0, "false": 1}
}
Decisions: Tool followup (hard) 100% right
{
  "tool": {
    "web_search": 0.0,
    "calculator": 0.0,
    "calendar": 0.98,
    "email": 0.0,
    "none": 0.02
  },
  "confirm": {
    "true": 0.95,
    "false": 0.05
  }
}
Decisions: Legit security alert (hard) 100% right
{
  "phishing": {
    "true": 0.02,
    "false": 0.98
  },
  "action_needed": {
    "true": 0.05,
    "false": 0.95
  }
}
Decisions: Meeting slot (hard) 100% right
{
  "slot": {"A": 0, "B": 1, "C": 0, "D": 0},
  "raj_last": {"true": 1, "false": 0}
}
Decisions: Review mixed (hard) 100% right
{
  "hardware": {
    "true": 1.0,
    "false": 0.0
  },
  "support": {
    "true": 1.0,
    "false": 0.0
  }
}
Decisions: Support checkout down 100% right
{
  "department": {"billing": 0.02, "technical": 0.94, "account": 0.01, "shipping": 0.01, "sales": 0.02},
  "urgency": {"0": 0.0, "1": 0.0, "2": 0.0, "3": 1.0},
  "outage": {"false": 0.0, "true": 1.0}
}
Decisions: Refund wrong plan 100% right
{
  "department": {
    "billing": 0.95,
    "technical": 0.0125,
    "account": 0.0125,
    "shipping": 0.0125,
    "sales": 0.0125
  },
  "refund": {
    "true": 0.98,
    "false": 0.02
  },
  "tone": {
    "frustrated": 0.03,
    "calm": 0.97
  }
}
Decisions: Moderation doxxing 100% right
{
  "policy": {
    "none": 0.01,
    "harassment": 0.96,
    "hate": 0.01,
    "spam": 0.01,
    "self_harm": 0.01
  },
  "personal_info": {
    "true": 0.99,
    "false": 0.01
  }
}
Decisions: Route calendar 100% right
{
  "tool": {
    "web_search": 0.01,
    "calculator": 0.01,
    "calendar": 0.95,
    "email": 0.01,
    "none": 0.02
  },
  "confirm": {
    "true": 0.9,
    "false": 0.1
  }
}
Decisions: Doc invoice missing due 100% right
{
  "doc_type": {
    "invoice": 0.98,
    "resume": 0.005,
    "contract": 0.005,
    "bank_statement": 0.005,
    "other": 0.005
  },
  "missing_due_date": {
    "true": 0.99,
    "false": 0.01
  }
}
Decisions: Phishing paypal 100% right
{
  "phishing": {
    "true": 0.999,
    "false": 0.001
  },
  "risk": {
    "0": 0.0,
    "1": 0.0,
    "2": 0.0,
    "3": 1.0
  }
}
Decisions: Pii ssn email 100% right
{
  "data_kind": {"none": 0, "contact": 0, "financial": 0, "government_id": 1, "health": 0},
  "sensitive": {"true": 1, "false": 0}
}
Decisions: Review mixed 100% right
{
  "sentiment": {"positive": 0.05, "neutral": 0.15, "negative": 0.8},
  "defect": {"true": 0.99, "false": 0.01},
  "recommend": {"true": 0.02, "false": 0.98}
}
Documents: Saas escalator (hard) 100% right
{
  "year2_price_per_seat_month": 47.25,
  "year3_price_per_seat_month": 47.25,
  "year1_invoice": 58320.00,
  "year2_invoice": 61236.00,
  "addon_months_billed": 6,
  "addon_invoice": 38556.00,
  "year3_invoice": 134946.00,
  "year3_discount_percent": 15,
  "total_contract_value": 293058.00,
  "contract_end_date": "2027-02-28"
}
Documents: Expense thread 100% right
{
  "employee_id": "EMP-20417",
  "destination_city": "Lisbon",
  "trip_start": "2025-02-24",
  "trip_end": "2025-02-27",
  "approved_items": [
    {"date": "2025-02-24", "category": "airfare", "amount_usd": 1184.60},
    {"date": "2025-02-24", "category": "ground_transport", "amount_usd": 38.88},
    {"date": "2025-02-25", "category": "meals", "amount_usd": 229.39},
    {"date": "2025-02-26", "category": "lodging", "amount_usd": 466.56},
    {"date": "2025-02-27", "category": "ground_transport", "amount_usd": 44.82}
  ],
  "rejected_item_count": 1,
  "per_diem_days": 3,
  "per_diem_usd": 195.00,
  "total_reimbursable_usd": 2159.25,
  "approver_email": "priya.raman@corvane.com"
}
Documents: Lease amendment 100% right
{
  "tenants": ["Marcus Lin", "Sofia Lin"],
  "landlord": "Ridgeline Property Group LLC",
  "zip": "97205",
  "lease_end": "2025-11-30",
  "original_monthly_rent": 2150.0,
  "monthly_rent_from_2025_06_01": 2236.0,
  "late_fee_from_2025_06_01": 111.8,
  "security_deposit": 2150.0,
  "total_pet_deposits": 800.0,
  "total_monthly_payment_july_2025": 2306.0,
  "move_in_payment": 4700.0
}
Documents: Ticket SLA 92% right
{
  "ticket_id": "48213",
  "account_id": "ACC-7731",
  "open_issue": "inventory_sync",
  "resolved_issues": [
    "billing_address",
    "invoice_pdf"
  ],
  "affected_orders": [
    "SO-99812",
    "SO-99820",
    "SO-99827"
  ],
  "priority": "P2",
  "sla_due_local": "2025-09-15T15:30",
  "sla_due_utc": "2025-09-15T20:30:00Z",
  "reissued_invoice": "INV-2025-0812"
}
Documents: Sales footnotes 100% right
{
  "q3_total_usd": 15346000,
  "q2_total_usd": 14464000,
  "q2_central_originally_reported_usd": 3047000,
  "q2_to_q3_change_pct": 6.1,
  "top_region_q3": "East",
  "fastest_growing_region_q1_to_q3": "International",
  "regions_declining_q2_to_q3": ["East"],
  "international_q3_organic_usd": 1731000,
  "west_excluding_mountain_q3_usd": 4201000
}

Size: 124B parameters. First tested OCT 11.

Models that scored about the same

Comments

Sign in with GitHub to comment. Spam and abuse are hidden automatically.