- Qwen3 Next 80B A3B Instruct is a free model from Alibaba's Qwen team that you can run on your own computer. In our tests it's not one we'd recommend right now: 57 out of 100, #31 of 63.
- It solved 18 of 30 coding jobs and scored 54 on reading documents. On our hardest tasks it scored 35.
- Runs on a Mac with 96 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. Qwen3 Next 80B A3B Instruct got 18 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. Qwen3 Next 80B A3B Instruct scored 54; the best model scored 100.
| Test | Score | Public questions | Secret questions |
|---|---|---|---|
| Coding | 60 | 57 | 61 |
| Reading documents | 54 | 67 | 50 |
| Decisions | 84 | 82 | 85 |
On the 18 hardest tasks (included in the scores above) it scored 35. This number separates the top models.
This model did much better on our public questions than on the secret ones. Sometimes that's luck; it can also mean the model has seen the public questions before.
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 Qwen3 Next 80B A3B Instruct in several sizes. Smaller versions fit on more computers and lose a little quality, so pick the biggest one that fits.
| Your computer | Runs it? | Version to download |
|---|---|---|
| Graphics card, 8 GB | No | – |
| Graphics card, 12 GB | No | – |
| Graphics card, 16 GB | No | – |
| Graphics card, 24 GB | No | – |
| Graphics card, 32 GB | No | – |
| Mac, 16 GB | No | – |
| Mac, 24 GB | No | – |
| Mac, 32 GB | No | – |
| Mac, 48 GB | No | – |
| Mac, 64 GB | No | – |
| Mac, 96 GB | Yes | Q6 |
| Mac, 128 GB | Yes | Q8 |
For normal-length chats. Long documents need more memory. Q4 or Q8? · How we worked this out
How to try it
Search for "Qwen3 Next 80B A3B Instruct 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 Qwen3 Next 80B A3B Instruct wrote for our public questions, word for word.
Coding: INI parser (hard) Didn't work
```python
import re
from typing import Dict, Any
def parse_ini(text: str) -> Dict[str, Dict[str, str]]:
lines = text.splitlines()
result: Dict[str, Dict[str, str]] = {}
current_section = None
last_key = None
default_section: Dict[str, str] = {}
# Track interpolation references to detect cycles
interpolation_stack = set()
def interpolate_value(value: str, section: str, seen: set) -> str:
if not value:
return value
# Detect recursion
if (section, value) in seen:
raise ValueError("Interpolation cycle detected")
seen.add((section, value))
# Find all ${key} and ${section:key} patterns
pattern = r'\$\{([^}]+)\}'
matches = list(re.finditer(pattern, value))
if not matches:
return value
# Process from right to left to avoid index shifting issues
result_str = value
for match in reversed(matches):
full_match = match.group(0)
key_ref = match.group(1)
# Parse section:key or just key
if ':' in key_ref:
ref_section, ref_key = key_ref.split(':', 1)
ref_section = ref_section.strip()
ref_key = ref_key.strip()
else:
ref_section = section
ref_key = key_ref.strip()
# Check if key exists in current section or inherited from DEFAULT
if ref_section not in result and ref_section != "DEFAULT":
raise KeyError(f"Section '{ref_section}' not found")
# Look for the key in the specified section
target_section = result.get(ref_section, {})
if ref_section == "DEFAULT":
target_section = default_section
if ref_key.lower() not in target_section:
raise KeyError(f"Key '{ref_key}' not found in section '{ref_section}'")
# Get the value and recursively interpolate it
target_value = target_section[ref_key.lower()]
interpolated_value = interpolate_value(target_value, ref_section, seen.copy())
# Replace the reference
result_str = result_str[:match.start()] + interpolated_value + result_str[match.end():]
return result_str
for line_num, line in enumerate(lines, 1):
# Skip empty lines and comments
stripped_line = line.strip()
if not stripped_line or stripped_line.startswith((';', '#')):
continue
# Check for section header
if stripped_line.startswith('[') and stripped_line.endswith(']'):
section_name = stripped_line[1:-1].strip()
if section_name == "DEFAULT":
current_section = "DEFAULT"
continue
else:
current_section = section_name
result[section_name] = {}
last_key = None
continue
# Check for key-value pair
if '=' in line or ':' in line:
# Find the first occurrence of = or :
eq_pos = line.find('=')
colon_pos = line.find(':')
if eq_pos == -1:
sep_pos = colon_pos
sep_char = ':'
elif colon_pos == -1:
sep_pos = eq_pos
sep_char = '='
else:
sep_pos = min(eq_pos, colon_pos)
sep_char = line[sep_pos]
# Check if separator is inside quotes
quote_count = 0
in_quotes = False
for i, char in enumerate(line):
if char == '"' and (i == 0 or line[i-1] != '\\'):
in_quotes = not in_quotes
if i == sep_pos and in_quotes:
# Separator is inside quotes, continue searching
if sep_char == '=':
eq_pos = line.find('=', i + 1)
if eq_pos != -1:
sep_pos = eq_pos
sep_char = '='
else:
colon_pos = line.find(':', i + 1)
if colon_pos != -1:
sep_pos = colon_pos
sep_char = ':'
break
# Split on the separator
key_part = line[:sep_pos]
value_part = line[sep_pos + 1:]
# Extract key and value
key = key_part.strip().lower()
value = value_part.rstrip()
# Check for inline comments outside quotes
in_quotes = False
for i, char in enumerate(value):
if char == '"' and (i == 0 or value[i-1] != '\\'):
in_quotes = not in_quotes
elif not in_quotes and char in ';#' and (i == 0 or value[i-1] == ' '):
value = value[:i]
break
# Handle quoted values
if value.startswith('"') and value.endswith('"') and len(value) >= 2:
# Remove quotes but preserve content exactly
value = value[1:-1]
else:
# Strip whitespace for non-quoted values
value = value.strip()
# Add to current section
if current_section is None:
raise ValueError("Key-value pair found before any section header")
if current_section == "DEFAULT":
default_section[key] = value
else:
result[current_section][key] = value
last_key = key
continue
# Handle continuation lines (starting with whitespace)
if line.startswith((' ', '\t')) and last_key is not None:
# Remove leading whitespace and handle inline comments
stripped_line = line.lstrip()
in_quotes = False
for i, char in enumerate(stripped_line):
if char == '"' and (i == 0 or stripped_line[i-1] != '\\'):
in_quotes = not in_quotes
elif not in_quotes and char in ';#' and (i == 0 or stripped_line[i-1] == ' '):
stripped_line = stripped_line[:i]
break
# Append to previous value with newline
if current_section == "DEFAULT":
default_section[last_key] += "\n" + stripped_line
else:
result[current_section][last_key] += "\n" + stripped_line
continue
# Malformed line
raise ValueError(f"Malformed line at line {line_num}: {line}")
# Apply DEFAULT section inheritance
for section_name, section_data in result.items():
for key, value in default_section.items():
if key not in section_data:
section_data[key] = value
# Perform interpolation on all values
for section_name, section_data in result.items():
for key, value in list(section_data.items()):
try:
interpolation_stack.clear()
interpolated_value = interpolate_value(value, section_name, interpolation_stack)
section_data[key] = interpolated_value
except ValueError as e:
raise ValueError(f"Interpolation error in section '{section_name}', key '{key}': {str(e)}")
except KeyError as e:
raise KeyError(f"Interpolation error in section '{section_name}', key '{key}': {str(e)}")
return result
``` Coding: TTL LRU (hard) Didn't work
```python
import time
from collections import OrderedDict
class TTLCache:
def __init__(self, capacity: int, ttl: float, clock):
self.capacity = capacity
self.ttl = ttl
self.clock = clock
self.cache = OrderedDict()
self.stats = {"hits": 0, "misses": 0, "evictions": 0, "expirations": 0}
def put(self, key, value):
if self.capacity == 0:
return
now = self.clock()
# If key exists, update it (refreshes recency and expiry)
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = (value, now + self.ttl)
return
# For new key, first remove expired entries
self._remove_expired(now)
# If still full, evict LRU
if len(self.cache) >= self.capacity:
oldest_key = next(iter(self.cache))
del self.cache[oldest_key]
self.stats["evictions"] += 1
# Insert new entry
self.cache[key] = (value, now + self.ttl)
def get(self, key, default=None):
if self.capacity == 0:
self.stats["misses"] += 1
return default
now = self.clock()
# Remove expired entries if present
if key in self.cache:
value, expiry = self.cache[key]
if now >= expiry:
del self.cache[key]
self.stats["expirations"] += 1
self.stats["misses"] += 1
return default
else:
# Mark as most recently used
self.cache.move_to_end(key)
self.stats["hits"] += 1
return value
# Key not found or expired (already handled above)
self.stats["misses"] += 1
return default
def __len__(self):
now = self.clock()
self._remove_expired(now)
return len(self.cache)
def _remove_expired(self, now):
# Remove all expired entries
expired_keys = [k for k, (_, expiry) in self.cache.items() if now >= expiry]
for key in expired_keys:
del self.cache[key]
self.stats["expirations"] += 1
``` Coding: Parse duration Didn't work
```python
import re
def parse_duration(s: str) -> int:
if not s or s.strip() == "":
raise ValueError("empty string")
# Normalize whitespace: replace multiple spaces with single space and strip
s = ' '.join(s.split())
# Split by whitespace to get individual parts
parts = s.split()
# Define valid units and their multipliers
unit_multipliers = {'h': 3600, 'm': 60, 's': 1}
unit_names = {'h': 'h', 'm': 'm', 's': 's', 'H': 'h', 'M': 'm', 'S': 's'}
# Track which units we've seen
seen_units = set()
total_seconds = 0
for part in parts:
# Match a number followed by a unit (case-insensitive)
match = re.match(r'^(\d+)([hmsHMS])$', part)
if not match:
raise ValueError("invalid format")
num_str, unit_char = match.groups()
unit = unit_names[unit_char]
# Check for repeated units
if unit in seen_units:
raise ValueError("repeated unit")
# Check unit order: must be h, then m, then s
unit_order = ['h', 'm', 's']
if seen_units:
# The current unit must come after all previously seen units in order
last_seen_unit_index = -1
for u in seen_units:
last_seen_unit_index = max(last_seen_unit_index, unit_order.index(u))
if unit_order.index(unit) <= last_seen_unit_index:
raise ValueError("units out of order")
# Convert to integer
num = int(num_str)
if num < 0:
raise ValueError("negative number")
total_seconds += num * unit_multipliers[unit]
seen_units.add(unit)
# If no valid parts were found
if not seen_units:
raise ValueError("no valid units found")
return total_seconds
``` Coding: Summarize ranges Worked
```python
def summarize_ranges(nums: list[int]) -> str:
if not nums:
return ""
# Sort and remove duplicates
nums = sorted(set(nums))
if not nums:
return ""
result = []
start = nums[0]
end = nums[0]
for i in range(1, len(nums)):
if nums[i] == end + 1:
# Continue the run
end = nums[i]
else:
# End of current run
if end - start >= 2: # 3 or more consecutive numbers
result.append(f"{start}..{end}")
elif end - start == 1: # 2 consecutive numbers
result.append(str(start))
result.append(str(end))
else: # Single number
result.append(str(start))
start = nums[i]
end = nums[i]
# Handle the last run
if end - start >= 2:
result.append(f"{start}..{end}")
elif end - start == 1:
result.append(str(start))
result.append(str(end))
else:
result.append(str(start))
return ",".join(result)
``` Coding: Top customers Worked
```python
import re
def top_customers(rows: list[dict], n: int) -> list[tuple[str, float]]:
customer_totals = {}
customer_names = {}
for row in rows:
customer = row.get("customer", "").strip()
amount_str = row.get("amount", "").strip()
if not customer:
continue
# Clean and parse amount
cleaned_amount = amount_str.replace('$', '').replace(',', '')
try:
amount = float(cleaned_amount)
except ValueError:
continue
# Use first valid occurrence's spelling (trimmed)
customer_lower = customer.lower()
if customer_lower not in customer_names:
customer_names[customer_lower] = customer
if customer_lower not in customer_totals:
customer_totals[customer_lower] = 0.0
customer_totals[customer_lower] += amount
# Convert to list of tuples with original trimmed names
result = [
(customer_names[lower_name], round(total, 2))
for lower_name, total in customer_totals.items()
]
# Sort by total descending, then by name ascending
result.sort(key=lambda x: (-x[1], x[0]))
return result[:n]
``` Coding: Slugify Worked
```python
import re
import unicodedata
def slugify(title: str, max_len: int = 50) -> str:
# Step 1: Transliterate accented characters to ASCII and drop other non-ASCII
normalized = unicodedata.normalize('NFKD', title)
ascii_only = ''.join(c for c in normalized if ord(c) < 128)
# Step 2: Lowercase
lowercased = ascii_only.lower()
# Step 3: Replace runs of non-alphanumeric characters with single "-", strip leading/trailing "-"
cleaned = re.sub(r'[^a-z0-9]+', '-', lowercased)
cleaned = cleaned.strip('-')
# Step 4: If longer than max_len, shorten without cutting a word
if len(cleaned) <= max_len:
return cleaned
# Split by '-' to get words
words = cleaned.split('-')
# If first word is longer than max_len, hard-cut it
if len(words[0]) > max_len:
return words[0][:max_len]
# Otherwise, build up the slug by adding words until we exceed max_len
result = []
current_length = 0
for word in words:
# Calculate length if we add this word (with a '-' if not first)
new_length = current_length + len(word) + (1 if result else 0)
if new_length <= max_len:
result.append(word)
current_length = new_length
else:
break
return '-'.join(result)
``` Coding: Token bucket Worked
```python
import time
class TokenBucket:
def __init__(self, capacity: float, refill_per_sec: float, clock):
self._capacity = capacity
self._refill_per_sec = refill_per_sec
self._clock = clock
self._tokens = capacity
self._last_refill_time = clock()
@property
def tokens(self):
current_time = self._clock()
elapsed = current_time - self._last_refill_time
self._tokens = min(self._capacity, self._tokens + elapsed * self._refill_per_sec)
self._last_refill_time = current_time
return self._tokens
def allow(self, cost: float = 1) -> bool:
current_time = self._clock()
elapsed = current_time - self._last_refill_time
self._tokens = min(self._capacity, self._tokens + elapsed * self._refill_per_sec)
self._last_refill_time = current_time
if self._tokens >= cost:
self._tokens -= cost
return True
return False
``` Decisions: Refund window (hard) 50% right
{
"outcome": {
"full_refund": 0.0,
"partial_refund": 1.0,
"store_credit": 0.0,
"nothing": 0.0
},
"defective": {
"true": 0.0,
"false": 1.0
}
} Decisions: Incident matrix (hard) 0% right
{
"severity": {
"0": 0.0,
"1": 0.1,
"2": 0.8,
"3": 0.1
},
"page": {
"true": 0.9,
"false": 0.1
}
} Decisions: Tool followup (hard) 100% right
{
"tool": {
"web_search": 0.0,
"calculator": 0.0,
"calendar": 1.0,
"email": 0.0,
"none": 0.0
},
"confirm": {
"true": 1.0,
"false": 0.0
}
} Decisions: Legit security alert (hard) 100% right
{
"phishing": {
"true": 0.05,
"false": 0.95
},
"action_needed": {
"true": 0.1,
"false": 0.9
}
} Decisions: Meeting slot (hard) 0% right
{
"slot": {
"A": 0.0,
"B": 0.0,
"C": 1.0,
"D": 0.0
},
"raj_last": {
"true": false,
"false": 1.0
}
} Decisions: Review mixed (hard) 100% right
{
"hardware": {
"true": 0.95,
"false": 0.05
},
"support": {
"true": 0.98,
"false": 0.02
}
} Decisions: Support checkout down 100% right
{
"department": {
"billing": 0.0,
"technical": 1.0,
"account": 0.0,
"shipping": 0.0,
"sales": 0.0
},
"urgency": {
"0": 0.0,
"1": 0.0,
"2": 0.0,
"3": 1.0
},
"outage": {
"true": 1.0,
"false": 0.0
}
} Decisions: Refund wrong plan 100% right
{
"department": {
"billing": 1.0,
"technical": 0.0,
"account": 0.0,
"shipping": 0.0,
"sales": 0.0
},
"refund": {
"true": 1.0,
"false": 0.0
},
"tone": {
"frustrated": 0.0,
"calm": 1.0
}
} Decisions: Moderation doxxing 100% right
{
"policy": {
"none": 0.0,
"harassment": 1.0,
"hate": 0.0,
"spam": 0.0,
"self_harm": 0.0
},
"personal_info": {
"true": 1.0,
"false": 0.0
}
} Decisions: Route calendar 100% right
{
"tool": {
"web_search": 0.0,
"calculator": 0.0,
"calendar": 1.0,
"email": 0.0,
"none": 0.0
},
"confirm": {
"true": 1.0,
"false": 0.0
}
} Decisions: Doc invoice missing due 100% right
{
"doc_type": {
"invoice": 1.0,
"resume": 0.0,
"contract": 0.0,
"bank_statement": 0.0,
"other": 0.0
},
"missing_due_date": {
"true": 1.0,
"false": 0.0
}
} Decisions: Phishing paypal 100% right
{
"phishing": {
"true": 1.0,
"false": 0.0
},
"risk": {
"0": 0.0,
"1": 0.0,
"2": 0.0,
"3": 1.0
}
} Decisions: Pii ssn email 100% right
{
"data_kind": {
"none": 0.0,
"contact": 0.0,
"financial": 0.0,
"government_id": 1.0,
"health": 0.0
},
"sensitive": {
"true": 1.0,
"false": 0.0
}
} Decisions: Review mixed 100% right
{
"sentiment": {
"positive": 0.1,
"neutral": 0.1,
"negative": 0.8
},
"defect": {
"true": 0.95,
"false": 0.05
},
"recommend": {
"true": 0.05,
"false": 0.95
}
} Documents: Saas escalator (hard) 40% right
{
"year2_price_per_seat_month": 47.25,
"year3_price_per_seat_month": 47.25,
"year1_invoice": 48600,
"year2_invoice": 50940,
"addon_months_billed": 5,
"addon_invoice": 3780,
"year3_invoice": 50940,
"year3_discount_percent": 15,
"total_contract_value": 141360,
"contract_end_date": "2027-02-28"
} Documents: Expense thread 79% 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.6
},
{
"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": 477.36
},
{
"date": "2025-02-27",
"category": "ground_transport",
"amount_usd": 44.82
}
],
"rejected_item_count": 2,
"per_diem_days": 2,
"per_diem_usd": 130,
"total_reimbursable_usd": 2104.85,
"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,
"monthly_rent_from_2025_06_01": 2236,
"late_fee_from_2025_06_01": 111.8,
"security_deposit": 2150,
"total_pet_deposits": 800,
"total_monthly_payment_july_2025": 2306,
"move_in_payment": 4700
} Documents: Ticket SLA 82% right
{
"ticket_id": "48213",
"account_id": "ACC-7731",
"open_issue": "inventory_sync",
"resolved_issues": ["billing_address"],
"affected_orders": ["SO-99812", "SO-99820", "SO-99827"],
"priority": "P2",
"sla_due_local": "2025-09-12T23:30",
"sla_due_utc": "2025-09-13T04:30:00Z",
"reissued_invoice": "INV-2025-0812"
} Documents: Sales footnotes 33% right
{
"q3_total_usd": 15346,
"q2_total_usd": 14464,
"q2_central_originally_reported_usd": 3047,
"q2_to_q3_change_pct": 6.0,
"top_region_q3": "East",
"fastest_growing_region_q1_to_q3": "International",
"regions_declining_q2_to_q3": [
"East"
],
"international_q3_organic_usd": 1731,
"west_excluding_mountain_q3_usd": 4201
} Size: 81B parameters. First tested OCT 11.
Comments
Sign in with GitHub to comment. Spam and abuse are hidden automatically.