- Qwen3 30B A3B Instruct 2507 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: 40 out of 100, #39 of 56.
- It solved 10 of 30 coding jobs and scored 46 on reading documents. On our hardest tasks it scored 25.
- Runs on a 24 GB graphics card or a Mac with 32 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 30B A3B Instruct 2507 got 10 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 30B A3B Instruct 2507 scored 46; the best model scored 100.
| Test | Score | Public questions | Secret questions |
|---|---|---|---|
| Coding | 33 | 57 | 26 |
| Reading documents | 46 | 56 | 43 |
| Decisions | 84 | 86 | 83 |
On the 18 hardest tasks (included in the scores above) it scored 25. 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 30B A3B Instruct 2507 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 | Yes | Q5 |
| Graphics card, 32 GB | Yes | Q6 |
| Mac, 16 GB | No | – |
| Mac, 24 GB | No | – |
| Mac, 32 GB | Yes | Q4 |
| Mac, 48 GB | Yes | Q8 |
| Mac, 64 GB | Yes | Q8 |
| Mac, 96 GB | Yes | Q8 |
| 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 30B A3B Instruct 2507 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 30B A3B Instruct 2507 wrote for our public questions, word for word.
Coding: INI parser (hard) Didn't work
```python
import re
from typing import Dict, Tuple, List, Optional, Iterator
def parse_ini(text: str) -> Dict[str, Dict[str, str]]:
# Parse the INI text into sections and keys
sections: Dict[str, Dict[str, str]] = {}
default_section: Dict[str, str] = {}
current_section = None
prev_line = None
key = None
continuation_lines: List[str] = []
referenced_keys: List[str] = []
interpolation_stack: List[str] = []
# Compile regex patterns
section_pattern = re.compile(r'^\s*\[(.*?)\]\s*$')
key_value_pattern = re.compile(r'^\s*([^=:#]+)[=:]\s*(.+)$')
inline_comment_pattern = re.compile(r'(\s+[#;].*)$')
interpolation_pattern = re.compile(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}')
section_key_pattern = re.compile(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\:([a-zA-Z_][a-zA-Z0-9_]*)\}')
def get_value(section_name: str, key_name: str) -> str:
"""Get a value with interpolation, handling cycles and missing keys."""
if not section_name and not key_name:
raise ValueError("Empty interpolation reference")
# Prevent cycles
ref = f"{section_name}:{key_name}" if section_name else key_name
if ref in interpolation_stack:
raise ValueError("Interpolation cycle detected")
# Check if the section exists
if section_name and section_name not in sections:
raise KeyError(f"Section '{section_name}' not found for interpolation")
# If we're in the DEFAULT section or it's inherited
target_section = sections.get(section_name, default_section)
# Check if key exists in the target section
value = target_section.get(key_name)
if value is None:
raise KeyError(f"Key '{key_name}' not found in section '{section_name}'")
# Process interpolation recursively
if '${' in value:
interpolation_stack.append(ref)
try:
# Handle ${key} and ${section:key} patterns
def replace_interpolation(match):
inner = match.group(1)
if ':' in inner:
sub_section, sub_key = inner.split(':', 1)
return get_value(sub_section, sub_key)
else:
return get_value(section_name, inner)
value = re.sub(r'\$\{[^}]+\}', replace_interpolation, value)
finally:
interpolation_stack.pop()
return value
lines = text.splitlines()
in_section = False
for line_num, line in enumerate(lines):
stripped_line = line.strip()
# Skip empty lines and comment lines
if not stripped_line or stripped_line[0] in ';#':
if prev_line is None or prev_line.startswith(' '):
continue
prev_line = None
continue
# Check for section header
section_match = section_pattern.match(line)
if section_match:
section_name = section_match.group(1).strip()
if not section_name:
raise ValueError(f"Invalid section header at line {line_num + 1}")
if section_name == "DEFAULT":
current_section = None
in_section = False
continue
sections[section_name] = {}
current_section = section_name
in_section = True
prev_line = None
continue
# Handle lines that start with whitespace and follow a key line (continuation)
if line.startswith(' ') and prev_line and key is not None:
continuation_lines.append(line.strip())
prev_line = line
continue
# If we're not in any section yet, and we haven't seen a section header, then it's invalid
if not in_section:
raise ValueError(f"Key line before section header at line {line_num + 1}")
# Try to parse key value pair
key_match = key_value_pattern.match(line)
if not key_match:
raise ValueError(f"Malformed key-value line at line {line_num + 1}")
# Extract key and value
raw_key = key_match.group(1).strip().lower()
raw_value = key_match.group(2).strip()
# Process inline comments (outside quotes)
comment_match = inline_comment_pattern.search(line)
if comment_match:
# If value is quoted, comment is inside the value, so we don't split
first_quote_idx = raw_value.find('"')
if first_quote_idx == -1 or raw_value.find('"', first_quote_idx + 1) == -1:
# Not a properly quoted value, so split at comment
raw_value = raw_value[:comment_match.start(1)].strip()
# Process quoted values
if raw_value.startswith('"') and raw_value.endswith('"'):
# Remove quotes and keep everything inside
value = raw_value[1:-1]
else:
value = raw_value
# If there are continuation lines, append with newline
if continuation_lines:
value = "\n".join(continuation_lines) + "\n" + value
continuation_lines = []
# Apply interpolation
if '${' in value:
try:
value = get_value(current_section, value)
except (KeyError, ValueError) as e:
raise e
except Exception as e:
raise ValueError(f"Error during interpolation at line {line_num + 1}: {e}")
# Store key-value in current section
sections[current_section][raw_key] = value
key = raw_key
prev_line = line
# Now merge DEFAULT values into other sections
for section_name, section_data in sections.items():
for default_key, default_value in default_section.items():
if default_key not in section_data:
section_data[default_key] = default_value
return sections
``` Coding: TTL LRU (hard) Worked
```python
from collections import OrderedDict
from typing import Any, Callable, Dict, Optional, Tuple
class TTLCache:
def __init__(self, capacity: int, ttl: float, clock: Callable[[], float]):
self.capacity = capacity
self.ttl = ttl
self.clock = clock
self._cache = OrderedDict()
self._stats = {"hits": 0, "misses": 0, "evictions": 0, "expirations": 0}
def _expire_old(self):
"""Remove all expired entries from the cache."""
now = self.clock()
keys_to_remove = [key for key, (value, expiry) in self._cache.items() if now >= expiry]
for key in keys_to_remove:
del self._cache[key]
self._stats["expirations"] += 1
def put(self, key: Any, value: Any) -> None:
if self.capacity == 0:
return
now = self.clock()
# Remove expired entries
self._expire_old()
# If key already exists, update it
if key in self._cache:
self._cache[key] = (value, now + self.ttl)
# Move to end to mark as recently used
self._cache.move_to_end(key)
return
# If inserting new entry and would exceed capacity after removing expired
# remove LRU entry only if still at capacity (after expiring old ones)
if len(self._cache) >= self.capacity:
# Evict LRU entry
self._cache.popitem(last=False)
self._stats["evictions"] += 1
# Insert new entry
self._cache[key] = (value, now + self.ttl)
def get(self, key: Any, default: Any = None) -> Any:
if self.capacity == 0:
self._stats["misses"] += 1
return default
now = self.clock()
# Remove expired entries
self._expire_old()
# Try to get the entry
if key not in self._cache:
self._stats["misses"] += 1
return default
value, expiry = self._cache[key]
# Check if expired after removal of old ones
if now >= expiry:
# Expired - remove and count
del self._cache[key]
self._stats["expirations"] += 1
self._stats["misses"] += 1
return default
# Valid, not expired - mark as recently used
self._cache.move_to_end(key)
self._stats["hits"] += 1
return value
def __len__(self) -> int:
if self.capacity == 0:
return 0
now = self.clock()
count = 0
for key, (value, expiry) in self._cache.items():
if now < expiry:
count += 1
return count
def stats(self) -> Dict[str, int]:
return self._stats.copy()
``` Coding: Parse duration Didn't work
```python
import re
def parse_duration(s: str) -> int:
# Strip whitespace and check for empty string
s = s.strip()
if not s:
raise ValueError("Empty or blank string")
# Pattern to match valid duration parts: optional spaces, number, unit (h/m/s), optional spaces
pattern = r'^\s*(\d+)(h|m|s)\s*'
parts = []
remaining = s
# Expected order of units
expected_units = ['h', 'm', 's']
seen_units = set()
# Parse each part in order
for unit in expected_units:
match = re.match(pattern + r'(\s*,?\s*|)', remaining, re.IGNORECASE)
if match:
value = int(match.group(1))
found_unit = match.group(2).lower()
# Check if unit is correct and not repeated
if found_unit != unit:
raise ValueError(f"Unit '{found_unit}' is not in expected order or is invalid")
if found_unit in seen_units:
raise ValueError(f"Unit '{found_unit}' repeated")
parts.append((unit, value))
seen_units.add(found_unit)
remaining = remaining[match.end():].strip()
else:
# If we expect a unit but don't find it, it's missing or out of order
if unit in ['h', 'm'] and len(parts) == 0:
# Must start with 'h'
raise ValueError("Duration must start with 'h'")
# If we're past the expected unit, it's out of order
if len(parts) > 0 and (unit == 'm' and 'h' not in [p[0] for p in parts]) or (unit == 's' and ('h' not in [p[0] for p in parts] or 'm' not in [p[0] for p in parts])):
raise ValueError("Units must be in order: h, then m, then s")
# Check if there's anything left after parsing
if remaining:
# If remaining string still has digits, it means no unit was provided
if re.match(r'^\s*\d+', remaining):
raise ValueError("Number without a unit")
# If remaining has unknown characters or units, it's invalid
if re.search(r'\d', remaining) and not re.search(r'[hms]', remaining):
raise ValueError("Unknown units or malformed input")
raise ValueError("Invalid duration format")
# Check that we have all required parts in order or just partial parts
# But we must have units in correct order
for i, (unit, value) in enumerate(parts):
if unit != expected_units[i]:
raise ValueError(f"Unit {unit} out of order")
# Convert to seconds
total_seconds = 0
for unit, value in parts:
if unit == 'h':
total_seconds += value * 3600
elif unit == 'm':
total_seconds += value * 60
elif unit == 's':
total_seconds += value
return total_seconds
``` Coding: Summarize ranges Worked
```python
def summarize_ranges(nums: list[int]) -> str:
if not nums:
return ""
# Remove duplicates and sort
unique_nums = sorted(set(nums))
result = []
i = 0
n = len(unique_nums)
while i < n:
start = unique_nums[i]
# Find the end of the current consecutive sequence
j = i
while j + 1 < n and unique_nums[j + 1] == unique_nums[j] + 1:
j += 1
# Check if the run is 3 or more numbers
if j - i + 1 >= 3:
result.append(f"{start}..{unique_nums[j]}")
else:
# Add individual numbers
for k in range(i, j + 1):
result.append(str(unique_nums[k]))
i = j + 1
return ",".join(result)
``` Coding: Top customers Didn't work
```python
from typing import List, Dict, Tuple
import re
def top_customers(rows: List[Dict], n: int) -> List[Tuple[str, float]]:
customer_totals = {}
customer_names = {}
# Regular expression to match valid amount strings
amount_pattern = re.compile(r'^([-+]?)\$(\d{1,3}(,\d{3})*|\d+)(\.\d{1,2})?$')
for row in rows:
customer = row.get("customer", "").strip()
amount_str = row.get("amount", "").strip()
# Skip empty customer name or unparsable amount
if not customer or not amount_str:
continue
# Check if amount string matches the pattern
if not amount_pattern.match(amount_str):
continue
# Parse the amount
try:
# Remove $ and commas, convert to float
clean_amount = amount_str.replace("$", "").replace(",", "")
amount = float(clean_amount)
except ValueError:
continue
# Normalize customer name (case-insensitive) and store first occurrence
customer_lower = customer.lower()
if customer_lower not in customer_names:
customer_names[customer_lower] = customer # Use first valid spelling
# Add to total
if customer_lower not in customer_totals:
customer_totals[customer_lower] = 0.0
customer_totals[customer_lower] += amount
# Create result list with (name, total) tuples
result = []
for customer_lower, total in customer_totals.items():
name = customer_names[customer_lower]
# Round to 2 decimals
rounded_total = round(total, 2)
result.append((name, rounded_total))
# Sort by total descending, then by name ascending
result.sort(key=lambda x: (-x[1], x[0]))
# Return top n customers
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 non-ASCII
normalized = unicodedata.normalize('NFKD', title)
ascii_chars = ''.join(c for c in normalized if unicodedata.category(c) != 'Mn')
ascii_only = ''.join(c for c in ascii_chars if ord(c) < 128)
# Step 2: Lowercase
lowercased = ascii_only.lower()
# Step 3: Replace non-alphanumeric chars with single "-" and strip leading/trailing "-"
slug = re.sub(r'[^a-z0-9]+', '-', lowercased).strip('-')
# Step 4: Shorten if longer than max_len without cutting words
if len(slug) <= max_len:
return slug
# Split into words and build prefix
words = slug.split('-')
prefix = ""
for word in words:
candidate = prefix + (word if not prefix else f"-{word}")
if len(candidate) <= max_len:
prefix = candidate
else:
# If adding this word would exceed max_len, check if first word is longer
if len(words[0]) > max_len:
return words[0][:max_len]
break
return prefix
``` Coding: Token bucket Worked
```python
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) -> float:
self._refill()
return self._tokens
def allow(self, cost: float = 1) -> bool:
self._refill()
if self._tokens >= cost:
self._tokens -= cost
return True
return False
def _refill(self):
now = self.clock()
elapsed = now - self._last_refill_time
refill_amount = elapsed * self.refill_per_sec
self._tokens = min(self.capacity, self._tokens + refill_amount)
self._last_refill_time = now
``` Decisions: Refund window (hard) 100% right
{"outcome": {"full_refund": 0.0, "partial_refund": 0.0, "store_credit": 1.0, "nothing": 0.0}, "defective": {"true": 0.0, "false": 1.0}} Decisions: Incident matrix (hard) 0% right
{"severity": {"0": 0.05, "1": 0.3, "2": 0.6, "3": 0.05}, "page": {"true": 0.7, "false": 0.3}} 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": 0.8, "false": 0.2}} 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": 0.0, "false": 1.0}} Decisions: Review mixed (hard) 100% right
{"hardware": {"true": 0.95, "false": 0.05}, "support": {"true": 0.9, "false": 0.1}} Decisions: Support checkout down 100% right
{"department": {"billing": 0.1, "technical": 0.8, "account": 0.05, "shipping": 0.02, "sales": 0.03}, "urgency": {"0": 0.01, "1": 0.05, "2": 0.1, "3": 0.84}, "outage": {"true": 0.95, "false": 0.05}} Decisions: Refund wrong plan 100% right
{"department": {"billing": 0.95, "technical": 0.01, "account": 0.02, "shipping": 0.01, "sales": 0.01}, "refund": {"true": 0.9, "false": 0.1}, "tone": {"frustrated": 0.1, "calm": 0.9}} Decisions: Moderation doxxing 100% right
{"policy": {"none": 0.1, "harassment": 0.9, "hate": 0.0, "spam": 0.0, "self_harm": 0.0}, "personal_info": {"true": 0.95, "false": 0.05}} 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": 0.95, "resume": 0.01, "contract": 0.02, "bank_statement": 0.01, "other": 0.01}, "missing_due_date": {"true": 0.8, "false": 0.2}} Decisions: Phishing paypal 100% right
{"phishing": {"true": 0.99, "false": 0.01}, "risk": {"0": 0.01, "1": 0.02, "2": 0.05, "3": 0.92}} Decisions: Pii ssn email 100% right
{"data_kind": {"none": 0.0, "contact": 0.1, "financial": 0.05, "government_id": 0.85, "health": 0.0}, "sensitive": {"true": 0.95, "false": 0.05}} Decisions: Review mixed 100% right
{"sentiment": {"positive": 0.1, "neutral": 0.2, "negative": 0.7}, "defect": {"true": 0.9, "false": 0.1}, "recommend": {"true": 0.1, "false": 0.9}} Documents: Saas escalator (hard) 20% right
{
"year2_price_per_seat_month": 46.59,
"year3_price_per_seat_month": 49.44,
"year1_invoice": 64800.00,
"year2_invoice": 67478.40,
"addon_months_billed": 4,
"addon_invoice": 31641.60,
"year3_invoice": 73051.20,
"year3_discount_percent": 15,
"total_contract_value": 246971.20,
"contract_end_date": "2027-02-28"
} Documents: Expense thread 71% 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":35.28},{"date":"2025-02-25","category":"meals","amount_usd":196.67},{"date":"2025-02-26","category":"lodging","amount_usd":400.0},{"date":"2025-02-27","category":"ground_transport","amount_usd":38.43}],"rejected_item_count":1,"per_diem_days":1,"per_diem_usd":65.0,"total_reimbursable_usd":1929.98,"approver_email":"priya.raman@corvane.com"} Documents: Lease amendment 83% 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":2586,"move_in_payment":6450} Documents: Ticket SLA 50% right
{
"ticket_id": "48213",
"account_id": "ACC-7731",
"open_issue": "inventory_sync",
"resolved_issues": [
"billing_address",
"invoice_pdf"
],
"affected_orders": [
"SO-99827"
],
"priority": "P2",
"sla_due_local": "2025-09-15T17:00",
"sla_due_utc": "2025-09-15T21:00:00Z",
"reissued_invoice": "INV-2025-0812"
} Documents: Sales footnotes 56% right
{"q3_total_usd":4535100,"q2_total_usd":2205000,"q2_central_originally_reported_usd":3047000,"q2_to_q3_change_pct":7.4,"top_region_q3":"West","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: 31B parameters. First tested OCT 10.
Comments
Sign in with GitHub to comment. Spam and abuse are hidden automatically.